PWN3 : RGBCTF soda pop bop Heap challenge

RGBCTF soda pop bop Heap challenge

Hello guys this is another write-up, but now it's a heap challenge it's really a great challenge I did learn a lot, let's download the binary and libc (they are in my github repo in the bottom), let's go.

I wasn't able to solve the challenge without the help of hk, big thanks.
Let's begin

File


the binary called spb
we're dealing with :
* 64 binary
* not stripped binary (easy to reverse engineer the binary with ghidra or IDA or any decompiler you're using)

Checksec :



The binary has all protection mechanisms enabled, this is common in heap challenges. We can't think of overwriting the GOT because the binary is Full RELRO and we can't think of shellcode because NX is enabled...

Terms used :

* House of force
* One gadget


Reverse engineering:

Let's reverse engineer the binary, open ghidra create a new project name it "spb", click file > import file > spb, then double click it and click analyze wait for a few seconds


Go to Symbol Tree and search for main like this :


Double click it and see the decompiler in the right, this is how the main looks like :




Ouuf this looks boring, I'm gonna give you resume of what the main is doing :

- First, it print to the screen some data then it asks you for a size, then it will shift that size to the left 5 times (this is not important just don't overthink it),malloc the size you have given shifted, and store the address in a global variable party.


-Second if the size given is less than 2, it will asks you for your name (it actually asks for a number i), if you provided 0 as a name the top chunk will overwritten with 0xffffffffffffffff. House of force !, and don't get confused by the else block because it will never trigger just some useless code to make the binary looks hard xD

- Third it will enter an infinite loop :



I will explain what each funtion is doing :

1) If the supplied number is 1 the binary will ask you for the size of the chunk you want to allocate and there is no size restriction, then it will store the malloced chunk address in a global variable in bss called selected_song, and lastly it will let you fill your chunk you just allocate. I will be using this function to apply House of Force.


----------------------------------------------------------------------------

2)  if you supply 2 the binary will ask you for a number 'c', this number needs to be smaller than the size you just give in step 1, then it will ask you for a number from 1 to 4 (the check is if d < 4 you can give negative value to be interpreted as a huge number), then it will do this : party + c*0x20 + 0x18 = d
For my exploit I will provide c as 0 and d as -1, so I can get House of Force again with top chunk overwritten to 0xffffffffffffffff, I will tell you how.
------------------------------------------------------------------------

3) This is important if you supply 3, it will leak party
I will be using this function very often every time I wanna leak something.
I did like binary, heap and libc addresses just using this .

Finally the boring things over xD, let's jump to the plan and the exploit !


Plan :

1) - Provide the size as 0 in the beginning of the program as I told you it will overwrite the Top chunk with 0xffffffffffffffff this is the biggest number you can have in 64 binaries as unsgined, this can give you chunk whenever you want in the binary !

2) - if you used function 3, it will first leak an address from the binary, because selected_song variable got initialized with a value from the binary so o we can leak this address and break ASLR for at least the binary,  now we have an address from the binary we can have an address from the bss.

3) - Alloc just a normal chunk, this time the selected_song variable will get updated by a value from the heap you can just use function 2 to leak heap, and we break ASLR for the heap too !

4) - We need just to allocate a HUUUGE chunk using House of Force that go into bss, then allocate another big chunk, and if you use the leak function you got an address from libc ! amazing right ? this is a technique to consider if you have a heap challenge again.

5) - Now we have all addresses we need, the idea here is to overwrite __malloc_hook with a one_gadget right ?? Yes I agree 100% but the top chunk is now having a value that varies from 0x100000 to 0xc00000 (at least in my case) in the bss, so we need to overwrite the top chunk again and make it great again xD just make it 0xffffffffffffffff so I can reach __malloc_hook with a second allocation and write one_gadget there.

6) - Here I have one gadget in __malloc_hook and I tested them all no one works :/ but I still have a technique to satisfy the requirement for one_gadget and I will show you how

7) - Enjoy you have shell now.

The exploit :

This is just some functions I defined to help me do my tasks :

Now let's apply the plan step by step :

Step 1/7 :

Depending on the version of libc the implementation of malloc() differ this challenge we were provided with libc 2-27 this version accepts malloc(0) and return a pointer, that pointer + 0x18 resides the top chunk let's see this in practice, I will be executing this :



Open gdb attach to it this process and run `heapinfo`:



As you can see the top chunk is now 0xffffffffffffffff

Step 2/7 :

As I said if you used leak() function is the beginning you will get an address from the binary and it's easy now to calculate the binary base address :



Let's execute it and see the result :


Perfect now I have the binary base address. the bss as well.

Step 3/7 :

To leak heap is easy just allocate normal chunk and leak the address.
I will be executing until this :


I mean by 'top' the top chunk or wilderness, If I have leaked heap it's easy to deduce the address of the top chunk just look into gdb and see the difference between top chunk and heap base address. (it's necessary for House of Force to know it's address).

Let's run :

Step 4/7 :

Now comes the real play allocate a chunk into bss and leak the address and we get a libc address, this is the formula I will be using to get a heap there :


if you malloc(offset) the pointer the top chunk will go to return address of malloc() this is hard to understand but just accept it for now you will know how by time xD

This is the code that allocate a chunk and move heap to bss, then allocate a new big chunk think of 0x1924500 ,and because it's a huge chunk it will provide a libc address, and it's easy now to get libc base address :

formula : move heap to bss -> allocate a big chunk -> leak you have libc. tadaaa xD

I will be executing this for now :


Let's execute and see what happend in the binary especially bss :


I have libc now, open GDB and attach it our process, press "Enter" :




The top chunk is in bss now let's see it :




You can see teh top chunk 0x1456229 in the bss right ? now just allocate a big chunk and you have libc leak :)

Step 5/7 :

All I need now is write one gadget to __malloc_hook right ?

But the top chunk is small now compared to 0xffffffffffffffff, so I can't reach __malloc_hook.

So how can I make it 0xffffffffffffffff ?? you remember this ?

If I provided c as 0 and d as -1, it will write 0xffffffffffffffff to party + 0x18 or (to 0x5632f69ca060 if you consider the above screen-shot) , but my top chunk is not there ? I can make it there just by allocating a chunk of size 0x10, and then use the trick !





This means House of Force again to __malloc_hook !

Step 6/7:


Now that I have everything leaked and top chunk to the higher value an unsigned long can take, I can think of overwriting the __malloc_hook with one_gadget of course, but I can't satisfy requirement let's see why :



These are the one_gadgets for me I will go with the second 0x4f322.
I need [rsp + 0x40] to be NULL, let's see if we have it or not !
This is the code I will be using :


with the first malloc I get a chunk that is near __malloc_hook so I sended some padding of "M" then the one gadget let's run and see what happened in gdb :


As you can see __malloc_hook is in 0x7fd5ea087c30 and the one gadget is at 0x7fd5e9ceb322 ,let's attach gdb and see it's content !



As you can see it has the one gadget :)
Let's breakpoint the one_gadget in trigger it :



Go to the binary and trigger it by allocating a new chunk of any size I will choose 0 :

Go to gdb and you need to see it stopped and reason is breakpoint :


Yea perfect we controled RIP, but what was the constraint it was [rsp + 0x40] NULL , yes ? let's check if we met the requirement :


And sadly no :/

But I have an Idea and we can make [rsp + 0x40] null, if we jump to realloc a little bit in the middle exactly realloc + 0x9 this will push some NULL pointers to the stack like r14 and r15 that are already null, and by the end it will make [rsp + 0x40] null so we just need to jump to the one gadget after this and we have shell.

But you wonder how can I jump to realloc and satisfy the one_gadget requirement and then jump to one gadget again ??

This will answer your question :

__realloc_hook is always behind __malloc_hook (and we already overwritten it by padding "M"*16) :


So the idea is as follows :

Make __malloc_hook pointer to realloc + 0x9
Make __realloc_hook pointer to one_gadget

so malloc will call realloc+0x9 which will setup the stack and make [rsp+0x40] = NULL and because we overwritten __realloc_hook with one_gadget it will jump to it with the requirement satisfied !

This is the beginning of realloc :


Now the final part will look like this :


Step 7/7 :

Enjoy :




Full exploit , binary and libc in my github repo : 
https://github.com/MaherAzzouzi/PWNing/tree/master/RGBCTF


My discord : Maher@7775 if you have any question.

Comments