PWN12 : Corrupting the next pointer of a linked list.

 Hello guys this is a write-up about a challenge from KipodAfterFree CTF, the challenge name is ShadowStuck it looks like a stack challenge when you first take a look at it because it models the real shadow stack, which is a mechanism used to protect stack from stack overflow.

I exploited the challenge in the heap, and we got a neat third blood.



Let's go, if you don't have the binary and GlibC downloaded, you can find them on my GitHub here: 

My_github
File: 


We're dealing with 64bits binary, and it's not stripped.


Checksec:


All protection mechanisms are enabled except the canary, you can tell from now that the stack can be overflowed easily, but the program is using it's own shadow stack which can be of the same use as the canary.


Play with the binary :


    


We got an address and a menu at the beginning let's see what address is this :





I'm highlighting the memory region, which is 0x2000 off the GlibC base.

No ASLR for GlibC anymore free leak :)

Let's continue with the program:






You can add, fire, read and change the name of an employee. Let's take a deeper look at the internals.


Reverse engineering :

The main() contain a switch case : 



You can see the usual options. the Q option give you a stack overflow, but with the presence of shadow stack it's like overflowing the return pointer without leaking the canary. Let's test it!


Let's see the other options : 

To add a new employee kems_add() is called :


__cyg_profile_func_enter() and __cyg_profile_func_exit() are just the prologue and epilogue of this stack protection mechanism, __cyg_profile_func_enter() just store the return pointer somehow in the BSS and whenever this functions wants to return __cyg_profile_func_exit() compares the return pointer in the stack with the one in the bss if they are != it should exit protecting the binary from any non-expected RIP, those two functions are present in every function.


The name of the new employee can be of a max length of 0x10, new lines will be deleted from the name (replaced with NULL), then comes manager_add()



This function is allocating a node of size 0x18 using calloc(), and it creates a linked list with the head pointer being g_list_root, every time a new employee is added a new node is created and it's linked to the list.

This how I imagine the node.

struct node {

char name[0x10];

node * next;

};

The size of this struct is 0x18 in 64 bits. No vulnerability in here, all good.


Kems_read() just prints the name if the employee by index (not important I didn't use it in my exploit we already got GlibC leak which is good)

Kems_change() changes the name of an already created employee by providing the old name of the employee you want to change his name in the linked list.

Kems_fire() here where the vulnerability resides !

First you provide the name of the employee you want to fire, and manager_remove() will be called with the first parameter being the name of the employee.



It starts from the head of the linked list and strcmp() the name you provided with the name of the node, if they differ it goes to the next node, and so on until it finds the node you want in the linked list.

The tricky part here is the last while loop, if your linked list is larger than two nodes, you will end up running in an endless while loop, meaning if you want to free a node the linked list should be of length 2 maximum.

Then comes the free(node), this node will end up in the tcache.

Immediately after the node is freed which is of a size 0x10, a new chunk is allocated of size 0x18, it will take just the node we freed from tcache but now we can write 0x18 to it which means we control the next pointer of the first node in the linked list.



Exploitation:

These are all functions I'm gonna use just to automate the task :



Get the free GlibC leak and calculate the base address : 



Create 1 node and free it and change the next pointer to point to __free_hook-0x8 : 



Now node 0 has "M"*0x10 as the name and __free_hook-0x8 as it's next node, now you can think that node1 which is node0->next is __free_hook-0x8.

If we use "change" option on node 1 we're actually writing to __free_hook-0x8 right? let's do it :



Now __free_hook is system() let's just free node1 which will be interpreted as :
free(node1) -> free(/bin/sh; 'system address') -> system("/bin/sh")



Enjoy !



I'm recruiting new team members in my team NBPwn. Only for people who are interested in reverse engineering and binary exploitation (Intermediate to Advanced). if you're interested HMU here in my discord : Maher#7775

Comments