0x00 概述
传统的ROP技术,尤其是amd64上的ROP,需要寻找大量的gadgets以对寄存器进行赋值,执行特定操作,如果没有合适的gadgets就需要进行各种奇怪的组装。这一过程阻碍了ROP技术的使用。而SROP技术的提出大大简化了ROP攻击的流程。
SROP 应用场景与原理
上方为用户层,下方为内核层。对于Linux来说
- 当一个用户层进程发起signal时,控制权切到内核层
- 内核保存进程的上下文(对我们来说重要的就是寄存器状态)到用户的栈上,然后再把rt_sigreturn地址压栈,跳到用户层执行Signal Handler,即调用rt_sigreturn
- rt_sigreturn执行完,跳到内核层
- 内核恢复②中保存的进程上下文,控制权交给用户层进程
有趣的是,这个过程存在着两个问题:
- rt_sigreturn在用户层调用,地址保存在栈上,执行后出栈
- 上下文也保存在栈上,比rt_sigreturn先进栈,且内核恢复上下文时不校验
因此,我们完全可以自己在栈上放好上下文,然后自己调用re_sigreturn,跳过步骤1、2。 此时,我们将通过步骤3、4让内核把我们伪造的上下文恢复到用户进程中,也就是说我们可以重置所有寄存器的值,一次到位地做到控制通用寄存器,rip和完成栈劫持。这里的上下文我们称之为Sigreturn Frame。文章中同样给出了Sigreturn Frame的结构。0x01 利用方法
利用条件
我们在构造 ROP 攻击的时候,需要满足下面的条件
- 可以控制栈溢出的内容
- 需要知道相应的地址
- /bin/sh字符串的地址
- syscall的地址
- SginalFrame的地址
- 需要有够大的空间来塞下整个 sigal frame
0x02 利用技巧
- 可以通过
read
等函数的返回值设置rax
来调用syscall
从而完成sigreturn
的系统调用 - 通过劫持
ebp
来使栈上的数据地址可确定 pwntools
的SignaFram
工具
对于原生的i386来说,其SignalFrame的设置为
1 | context.arch = ‘i386’ |
对于amd64上运行的32位程序来说,其SignalFrame的设置为
1 | context.arch=’i386’ |
0x03 示例
分析
程序很简单,只是一个栈溢出,只开了NX
保护。但是看到程序没有后门函数甚至没有泄露函数,无法通过泄露libc
来获得shell
。并且找的gadget
屈指可数。。。。
1 | ropper -f unexploitable |
使用这个工具成功找到了一个syscall
的gadget
所以用SROP就比较合适了
exp
1 | from pwn import * |