cHeap was a very basic heap challenge based on libc-2.31, including tcache.
It allows us to create one note, show it and remove it.
This implementation contains two major flaws. For one, the pointer to the note doesn’t get zeroed out after free, allowing us to show it again for an easy leak. And the second one is, that in create we’ll always be able to write 0x100 bytes into our note independent from its real size, allowing us to overwrite follow up chunk data.
Since the size for the creation isn’t checked or restricted, this opens up the way for a lot of different solutions, depending on the leaks you can get.
I went for just leak libc directly and overwre __free_hook, so all we need is a libc leak, then we can just overwrite the FD pointer of a freed chunk with it and reallocate it.
Only tricky part for this is, that in libc-2.31tcache will check, how many chunks are currently freed and only serve the chunk from tcache, if the fastbin count is > 0. Since we can only allocate one chunk at a time to free it, we’ll always have only one freed chunk of a specific size (but surely, there’s a way to overcome this).
Let’s start with some heap grooming to achieve a libc leak. For this, I created multiple chunks from different sizes to fill up the heap, overwrote the size of a freed chunk with a fake size (with the oob write on creating a note), so it cannot be served by tcache and put a fake next_size at the bottom of the heap to avoid any errors on freeing this chunk later on.
So, before creating the last 0x50 note to overwrite the size, the heap will look like this.
After creating the 0x50 note (for overwriting 0x300 note size)
Since the chunk at 0x555555559380 is still in the 0x300 tcache fastbin, we can reallocate it (tcache will NOT update the size of it on allocation) and then free it, to let it be handled as an unsorted bin.
For this, we just have to make sure, that it has a valid next_size
With these constraints fulfilled, we can now reallocate a 0x300 chunk and free it
So, now we have the current UAF note pointing to the freed unsorted bin chunk, and can just leak a main_arena pointer from it via show
Knowing libc base address, we can now go on with overwriting __free_hook, but as said in the beginning, we’re only allowed to create and free one note at a time, but we need at least two freed chunks in a tcache fastbin to overwrite the FD pointer of a freed chunk and reallocating it.
But since we have the oob write, we can just corrupt the sizes of two existing chunks (similar to what we did for leaking libc) and free them, so they get freed into the same tcache fastbin.
Again, we can now allocate a 0x40 chunk, free it (will be put into 0x50 tcache fastbin) and then allocate a 0x30 chunk and free it (which will also be put into 0x50 tcache fastbin).
Using the oob write again on creating a 0x20 chunk, we can now overwrite the FD of the next 0x50 chunk, reallocate it and overwrite __free_hook
So, our current note now points to /bin/sh string and __free_hook is overwritten with system.
Freeing the current note will now trigger system("/bin/sh") giving us our shell.