0x00 前言
很久之前就听大佬说过SROP了,前几天也通过SROP技术做出来一道题目,不过是看的别人WP之后才会的。
今天具体学习了一下SROP技术,先贴几个参考链接:
需要注意的是,pwntools中的SigreturnFrame中并不需要填写rt_sigreturn的地址,我们只需要确保执行rt_sigreturn的时候栈顶是SigreturnFrame就行。
形如:
1 | mov rax,15; |
这种可以直接send(str(frame))
但形如
1 | read(0, &buf, 0x100uLL); |
这种需要send(str(frame)[8:])
前八个是调用rt_sigreturn函数的 ?
本文通过三道题目来练习这项技术,题目按照顺序来的层层递进
0x01 Ciscn_2019_s_3
这道题目也就是我刚刚说过的通过别人的WP第一次了解SROP并用这技术做出来的题目。
这道题目的特点在于
- 题目已经给了
mov rax,59(对应execve系统调用)
、mov rax,15(对应sigreturn系统调用)
- SROP能够利用到的gadgets是
syscall; ret
因为之前我已经专门更过这道题的博客了,所以这里直接放链接
0x02 smallest pwn
这是CTF-WIKI上面给的一道例题。
这道题目的特点是
- 程序非常小,由汇编语言写成
- 相比上一道题目,没有给出
mov rax,59
或者mov rax,15
- 需要通过程序的read系统调用的读入字节数来设置rax的值。
- SROP能够利用的gadgets是
syscall;ret
这道题目有一个博主已经讲解的非常详细了,而且最后这位博主画的流程图很直观,我再写也只能是模仿,很难再超越了。这里直接给出链接了
0x03 rootersctf_2019_srop
这是BUUCTF上的一道题目
这道题的特点是
- 程序非常小,由汇编语言写成
- 依然没有
mov rax,59
或者mov rax,15
- 存在
pop rax
- 能够利用的gadgets是
pop rax;syscall;leave;ret
和syscall;leave;ret
(可以注意到相比前两道题目,syscall和ret之间多了一个leave
0x001 分析
1 | Arch: amd64-64-little |
程序只开了NX保护
1 | .text:0000000000401000 ; =============== S U B R O U T I N E ======================================= |
程序只有两个函数,一个是调用了write和read的函数,另一个是调用exit的函数。
很明显可以看到read处存在栈溢出,可以用SROP来做。
0x002 思路
由于程序没有/bin/sh
,我们需要找到一个位置写入/bin/sh
,这个位置需要可读可写
。
可以先通过程序自带的一次read进行栈溢出来执行一次sigreturn系统调用,使其能够向指定位置写入/bin/sh
,然后再次进行sigreturn系统调用执行execve('/bin/sh',0,0)
如何连续进行两次sigreturn系统调用?这里就需要构造SROP链了。例题2也是构造的SROP链
0x003 Let’s do it!
我们需要先找到一个位置用来写入/bin/sh
,这个位置需要可读可写
可以看到程序的DATA区
权限符合我们的要求。这里选择addr = 0x402100
吧
1 | pop_rax_syscall_leave_ret = 0x401032 |
然后通过SROP执行一次read系统调用
1 | #read(0,addr,300) |
然后向addr写入/bin/sh
并再次进行sigreturn系统调用,执行execve('/bin/sh',0,0)
1 | frame = SigreturnFrame() |
注意第一次构造frame的时候,我们设置了frame.rsp = addr,frame.rbp = addr,这是因为由于我们的gadgets是syscall;leave;ret
,中间有个leave
,leave
就相当于mov rsp,rbp;pop rbp
,为了构造SROP链,我们需要让rsp指向我们下一个payload的地址(这个可以参考第二个例题),但如果我们的rbp为0的话,leave
以后rsp也为0,这显然不符合我们的要求,所以这个需要让rbp=addr
。
但由于leave
还有一个pop rbp
的功能,这里构造就较为巧妙,我们addr的位置前8字节正好被写为/bin/sh\x00
,这样当我们ret的时候,正好能将p64(pop_rax_syscall_leave_ret)
传给rip来执行,然后进行下一次sigreturn系统调用。
0x004 完整EXP
1 | from pwn import * |
context(arch = 'amd64',os = 'linux')#Important!!!!!!
这个一定要有,不然frame构造出来可能就不太对了。