PWN11 : Heap Off-by-One vulnerability

 Hello guys this is a quick write-up about a heap challenge, it's a pwn challenge from NACTF.

The vulnerability is Off-By-One as you saw in the title.

You can find the exercise and the solution in my GitHub here : 

My_GitHub

File:


We're dealing with a 64 bits binary.

The binary is not stripped from its debugging symbols.


Checksec :



All protection mechanisms are enabled and who cares we can get RCE anyways x)

Play with the binary :

This is the menu we got :



   


Playing with the binary wasn't helpful for me, I don't know what's happening here.
I moved to reverse engineer the binary immediately..

Reverse engineering :


So I opened up ghidra, I created a new project I named it "NACTF", and I opened the challenge file there.
After analyzing the binary I found the main function, which is just a switch for the options 1, 2 and 3.



So let's analyze option one which is "add_category".



You just provide the weight which in an int and the size and the binary will malloc(size), no checks on the size, lastly it will store the weight, the size and the chunk in the array 'categories'.

Let's move on to the next option.


This function is a little bit interesting.
First you provide the index of the category you want, the check is present here against OOB vulns, 
Then you provide the new size, you can provide any character just to escape scanf used in get_int(), you can stay with the same size by providing 0 too.
If you change the size, the indexed chunk will be freed, and you will allocate another one with the new size. and the second chunk will replace the old chunk in the categories array which means no UAF.
Then comes the vulnerability!
The while statement at the end that takes the user input has <= which is an off by one, it should be <.
So we have control over the 1 byte overflowed! and it's very powerful it can lead to RCE!




The option 3 just print the content of the category, and it will stop printing depending on the size, if we allocate 1 chunk of 0x200 size and use the option 3, it will print 0x200 bytes starting from the chunk address. That's it, nothing so special.

Plan :
Now what we know is that we can allocate up to 16 chunks, we have full control over the size of the chunks, we don't have direct free we need to free and allocate another chunk which is not a problem.
We don't have a UAF but we have Off-By-One vulnerability, what we can do with it!
My plan was this :

- Leak GlibC.
- Re-arrange the chunks in the heap somehow using the OBO, so that I can achieve tcache poisoning (I will show you how)
- change __free_hook to system() and free a chunk containing "/bin/sh\x00"

Exploitation :

Leak GlibC :

The leak of Libc was easy, you just need to allocate a chunk that is bigger than 0x408 and free it using option 2 (now we have a chunk in unsorted bin with it's bk and fwd addresses from GlibC), then allocate a smaller chunk and use exactly 0x18 (it will be taken from the chunk we just free), I chose 0x18 as a size just to make use of the vulnerability.

This is the part of my exploit that do what I said :







You can see some negative values right? this is just the leaked main_arena+96 byte per byte.
I'm going to define an implementation that takes those bytes and rearrange them to a visible address.
This is my implementation : 








Okey we will take that ;)

Take advantage of the vulnerability:
Here I'm going to allocate more chunks just to achieve Tcache poisoning I'm going to show you how, for now let me allocate three more chunks.
Those are chunk 4, 5 and 6.




I'm gonna  run and show you how they look in the heap.



The third chunk with size 0x18 we created earlier is at categories+64
Let's expand it.



You can see our 0x18 chunk and the other three cute chunks we created one after the other.
Those libc pointers are there because we didn't update our chunks by any data.

Here comes the trick !
You know that we have 1 byte off overwrite, and you know that our 0x18+1 bytes of data will be written to the third chunk or 0x558f7eae5290, which means 1 byte is overflowing the chunk 4 size.

What we can do with the last byte overwritten? answer : we can change the size of the chunk 4 (currently 0x21) (in this case 0x21 because we allocated 0x10 the 0x10 added is the chunk header and the 0x1 is PREV_INUSE which means that the previous chunk is not free 0x10+0x10+0x1.)

What if we change 0x21 to 0x41?
If we did that and free the chunk 4, and allocate another one of size 0x30 you will have full control over chunk 5.

But what if we free chunk 5 before all of this? the chunk 5 is gonna be linked to tcache and you have tcache poisoning attack!

This is the part of my exploit that changes the chunk size of chunk 4, and free it :




This how chunk 4, and 5 looks in memory :

Let's free chunk 5 and overwrite it from chunk 4:


Now let's make use of the chunk 4, and make fwd pointer of chunk 5 (freed in tcache) to __free_hook-0x8 :




Allocate two more 0x10 and you have __free_hook changed to system : 


  Now all you have to do is to free chunk 9.


Enjoy : 

If you have any questions regarding anything you can use the comment section below, or contact me in my Discord : Maher#7775

Comments