HITCON 2017 : Ghost in The Heap Writeup

peternguyen
tradahacking
Published in
4 min readNov 9, 2017

This is quite interesting challenge in this CTF, I had spent nearly 20 hours to solve this challenges.

The vulnerability is in alloc_heap function. The scanf function read string into 168 bytes heap but scanf will automaticaly added NULL byte in the end as result lead to off byte one vulnerability.

IDA decompiler of alloc_heap

The program also have function allow alloc ghost in the heap with size 0x50 bytes (0x60 bytes includes malloc header).

We can’t not controll size of buffer so the technique off byte null https://googleprojectzero.blogspot.com/2014/08/the-poisoned-nul-byte-2014-edition.html can’t used.

In the beginning of challenge, after some hours read malloc.c in glibc to find some code path to achieve overlapping ghost chunk and heap chunk and then trigger unsorted bin unlink to overwrite _IO_list_all.

Leaking libc is quite simple, we have to create 3 heap chunk, free the middle and add ghost. Because of no terminated byte added, we can use it to leak main_arena->unsorted_bin stored in free heap chunk.

Leaking heap base is more complicated, create 3 heap chunk:A ,B, C, after that, adding ghost into heap to make sure we free 2 chunk A,C there don’t consolidate with top chunk. A,C is on unsorted bin linked list, free all chunk A,B,C, ghost to cleanup heap layout, and then alloc ghost chunk first, the heap base address is on ghost chunk, so that we can read it easily.

Create chunk overlapping by abuse NULL pointer and Consolidate Forward. We create 3 heap chunk called 1, 2, 3, ghost chunk at the bottom of heap (near top chunk) like image on the left.

After that free all heap chunk and add heap chunk 1 with 168 characters which ovewrite off byte to free_chunk (0x160 to 0x100).

Alloc heap chunk 2 we got layout like image on the left. Free heap ghost chunk and free heap chunk 1. The free function think got chunk prev_size (0x160) is correct size, and consolidate forward with top chunk so that heap chunk 2 is overlapped with top chunk.

Because of consolidate forward unlink heap 1 to unsorted bin and merge with top chunk so that we have to correct heap_1->bck and heap_1->fwd to make sure they point to address of heap_1.

In current glibc version, glibc also add a small check to unlink function to mitigate single byte overflow by checking next_chunk->prev_size, we also fix that by appending correct prev_size to heap chunk.

The top chunk is now overlapping with heap_1 we can align heap layout to gain free a chunk with size we control to setup main_arena->smallbin[4] which is FILE->chain structure, and then use unsorted_bin to overwrite _IO_list_all to control RIP.

Unfortunately for me, the libc challenge give me is different with my current libc (Ubuntu 16.04) after a bit searching I figured out that these libc binary is on Ubuntu 17.04 and include the newst migitation agains FILE vtable abusing.

I can’t overwrite FILE->vtable into heap any more.

IO_validate_vtable allows address in address of FILE vtable range so my payload is failed.

After some hours finding way to bypass the migitation, I found some vtable has used many function pointer call like _IO_wstr_finish .

_IO_wstr_finish take first argument is FILE structure which is controlled by me then trigger function call [rbx+0xe8] ,n this offset I also controlled.

Finally, Exploit chain is overwrite _FILE->vtable with address of _IO_wstr_finish and the spray other byte is one_gadget to trigger shell.

My full payload is https://gist.github.com/peternguyen93/d541cf00289252eb518e40348d0f2235

--

--