Notice that the first dword of each user’s name chunk is a pointer to the user’s description chunk.

Additionally, when we update a user’s description, the program checks to verify that the description’s length does not cause it to overflow from the user’s description chunk into the user’s name chunk.

If it does, the program outputs “my l33t defenses cannot be fooled, cya!” and exits. Otherwise, it calls fgets() and copies a user-specified number of bytes into the chunk’s description chunk.

This check only works if a user’s description and name chunks are contiguous to each other in the heap.

But, what if we were able to allocate a user’s chunks in a way such that a user’s description and name chunks were far away from each other?

That would allow us to overwrite anything in between the user’s description chunk and name chunk, including other valid heap chunks, introducing a heap overflow vulnerability.

We can actually “massage” the heap into this state by using the following flow.

allocate user A

small description (size 0)

normal name (size 0x80)

allocate user B

small description (size 0)

normal name (size 0x80)

free user A

allocate user C

large description (size 0x20)

normal name (size 0x80)

The reason this flow will force user C’s description chunk and name chunk to be separated is because user C’s description chunk is allocated first.
Once it is reallocated from user A’s old description chunk, there will not be enough space left to reallocate user A’s name chunk to service chunk C’s name chunk request, since chunk C’s description is bigger than chunk A’s.

We can then leverage this heap overflow vulnerability to overwrite chunk B’s pointer-to-description-chunk pointer to give us both arbitrary read and arbitrary write privileges!

We get an arbitrary read because we can print out a user’s description and the process for that involves first dereferencing the pointer-to-description-chunk pointer and printing the data stored at that memory address.

Similarly, we can get an arbitrary write when we update a user’s description because the process for that involves first dereferencing the pointer-to-description-chunk pointer and writing user controlled data to the dereferenced address.

Using the two exploit primitives, we put together the following plan.

massage the heap

see aforementioned flow

libc/GOT leak

overwrite pointer in chunkB_name w/ __libc_start_main@GOT

print chunkB to get __libc_start_main@libc

calculate system@libc

GOT overwrite

overwrite pointer in chunkB_name w/free@GOT

edit description of chunkB

input system@libc

write “/bin/sh\0” to heap

allocate another chunk w/any description size

set description to “/bin/sh\0”

call system(“/bin/sh\0”)

free chunk index 3

Putting everything together, we are able to get a shell using the following exploit.