Exploiting Protostar – Heap Levels 0-2
In this article, we will be solving Heap Levels of Protostar. We will be mainly focusing at how and why of Heap Buffer overflows.
Heap memory can be viewed as another memory region. Heap memory is allocated via different memory allocators such as dlmalloc, jemalloc, ptmalloc, etc. In this article, we will be looking into vulnerabilities related to the older version of dlmalloc. In case of Linux systems, a malloc call is implemented as a wrapper around mmap and brk syscalls. At the very low level, this memory allocation is handled by the kernel. However, when, and how much memory needs to allocated depends on memory allocators.
VM used in this article can be downloaded from here.
To make these challenges more fun, I have tried to gain code execution in each level via different techniques which also implies that the scope of these challenges is not limited to overwriting variables/function pointers.
Following code is presented to us for solving Heap0 and the objective of this level is to overwrite the function pointer with the address of winner function.
By looking at the code, we can identify that 64 bytes of heap memory are reserved for struct name, and 4 bytes are reserved for struct fp. Further function pointer to a nowinner function is placed in heap memory. And our input is copied into name variable of struct data using the vulnerable strcpy function. So, we can safely assume that after 64 bytes of space we should be able to overwrite the function pointer to nowinner function.
We further disassembled the main function and analysed that malloc is called to allocate 64 bytes of space however it has allocated 8 more bytes (0x00000049) in which first 4 bytes hold the prev_size data (if the previous chunk is free then size of previous chunk else if the previous chunk is allocated then contains previous chunk’s user data). Another 4 bits indicate the size of allocated chunk which in itself has 3 LSB bits reserved for different flags (PREV_INUSE [0x1], IS_MAPPED [0x2], NON_MAIN_ARENA [0x4]) containing information about the previous chunk.
As can be seen in the following screenshot, how our input is being laid out in heap memory. As this is the top-most block, therefore, the prev_inuse bit is set in the chunk size field (72 bytes = 0x00000048 + 0x1). To get the start address of heap memory in gdb, you can use info proc mappings command and further we can examine number of bytes as x/64wx 0x804a0000.
We can also see that pointer to nowinner function is at 0x804a050, and after passing 64 bytes of input, we are able to overwrite till 0x804a044. So, we need to overwrite 2 more words and then overwrite the nowinner function pointer with the winner function address to pass this level.
We further analyzed that the pointer to our input is placed just after the function address on the stack. Thus we can simply change the function pointer of nowinner function to system function and get code execution.
As can be seen, in this level we have nested malloc calls, and further, we have a vulnerable strcpy function that copies the content of our input into the name of struct internet.
We started our dynamic analysis by putting breakpoints at each malloc and strcpy call and started analyzing the contents of the heap.
As can be seen after each strcpy the heap structure looks like this, from this we can deduce that at first malloc call priority and the memory address of char *name are placed on the stack, and same happens when malloc call is completed for I2. As this program is using the vulnerable strcpy function, this also means that we can write arbitrary data to any writable memory locations.
As can be seen, we have used a first strcpy function to modify the address 0x804a038 with puts GOT and second strcpy function to modify 0x42424242 with winner function’s PLT address to pass this level.
To gain code execution, in this case, we have placed our 20 bytes shellcode and modify the address 0x804a038 with the puts GOT using the first strcpy so that at second strcpy puts GOT gets overwritten with the address of our shellcode.
Heap 2 (Use-After-Free)
By Looking at the source code provided to us, we can see that the application acts on three commands from the user input.
Auth: Copy some data on the heap
Service: Uses strdup -> which internally uses malloc call to copy some data on the heap and then place the same in eax register.
Reset: Free Auth object.
Login: To check whether we are logged in or not.
The program suffers from multiple vulnerabilities first if we see that there are multiple variables with the name Auth. Secondly program uses Auth object which is used after being freed.
As can be seen, the state of the heap after passing Auth and service command with some data. Now as we know that the result of strdup is returned in eax and further the check is made at 0x20 bytes from the eax which resolved in (mov eax, DWORD PTR [eax+0x20]; test eax,eax ). So, it means if we can pass a long string greater than 0x20 bytes in service command we can overwrite the auth variable pass the level.
We can also do this by assigning service command with some data which ultimately assign one more chunk overwriting the auth variable on the heap.
As can be seen, we were able to overwrite the auth variable by assigning data twice using service command.
We can try the same in the terminal as shown.
As the code was written so terribly, the lesson from here is to name your variables properly and do not use variables after freeing them. We can see that even if the name of variables were assigned properly, we could still overwrite the auth variable using service command multiple times after it was still being used after being freed.
In this article, we have looked into some basic heap overflows and how we can use them to overwrite data in an arbitrary location and gain code execution. In my next article, I will be covering some heap exploitation techniques.