In this article we will tackle Heap Levels of Exploiting Protostar. We will mainly focus on how and why Heap Buffer overflows occur.
Introduction to Exploiting Protostar:
Heap memory can be thought of as another area of memory. Heap memory is allocated using various memory allocators such as dlmalloc, jemalloc, ptmalloc etc. In this article we will look at the vulnerabilities related to the older version of dlmalloc. On Linux systems, the malloc call is implemented as a wrapper around the mmap and brk system calls. At a very low level, this memory allocation is handled by the kernel. However, when and how much memory needs to be allocated depends on the memory allocators.
To make these challenges more fun, I tried to get the code execution in each level using different techniques, which also means that the scope of these challenges is not limited to overwriting variables/function pointers.
Stack of 0
We are presented with the following code to solve Heap0 and the goal of this level is to overwrite the function pointer with the address of the winner function.
Looking at the code, we can identify that 64 bytes of heap memory are reserved for the structure name and 4 bytes are reserved for the fp structure. Another function pointer to the nowinner function is located in heap memory. And our input is copied into the named struct data variable 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 the nowinner function.
Next, we dissected the main function and analyzed that malloc is called to allocate 64 bytes of space, but allocated another 8 bytes (0x00000049), in which the first 4 bytes contain prev_size data (if the previous part is free, then the size of the previous part elsewhere if allocated the previous block, then it contains the user data of the previous block). The next 4 bits indicate the size of the allocated chunk, which itself has 3 LSB bits reserved for various flags (PREV_INUSE [0x1], IS_MAPPED [0x2], NON_MAIN_ARENA [0x4]) containing information about the previous chunk.
As you can see in the following screenshot, how our input is distributed in the heap memory. Since this is the highest block, the prev_inuse bit is set in the block size field (72 bytes = 0x00000048 + 0x1). To get the starting address of the heap memory in gdb, you can use the command info proc mappings and further we can examine the number of bytes as x/64wx 0x804a0000.
We can also see that the pointer to the nowinner function is at 0x804a050 and after going through 64 bytes of input we are able to overwrite all the way to 0x804a044. So we need to overwrite 2 more words and then overwrite the nowinner function pointer with the address of the winner function to get through this level.
Running the code
Next, we analyzed that the pointer to our input is located right after the address of the function on the stack. So we can simply change the function pointer of the nowinner function to a system function and get the code to run.
As you can see, we have nested malloc calls at this level, and we also have a vulnerable strcpy function that copies the contents of our input into a struct named internet.
We started our dynamic analysis by inserting breakpoints at each call to malloc and strcpy and began 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 first the priority of the malloc call and the memory address char *name are put on the stack and the same happens when the malloc call is completed for I2. Since this program uses a vulnerable strcpy function, it also means that we can write arbitrary data to any writable memory location.
As you can see, we used the first strcpy function to modify the address 0x804a038 with GOT puts and the second strcpy function to modify 0x42424242 with the PLT address of the winner function to pass this level.
Running the code:
To get the code to run, in this case we placed our 20 byte shellcode and modified the address 0x804a038 by putting GOT with the first strcpy so that the second strcpy puts the GOT to be overwritten by the address of our shellcode.
Heap 2 (free use after use)
Looking at the source code provided to us, we can see that the application works based on three commands from the user input.
Auth: Copy some data to the heap
Service: Uses strdup -> which internally uses a malloc call to copy some data to the heap and then places it in the eax register.
Reset: Free Auth object.
Login: To check whether we are logged in or not.
If we see that there are multiple variables named Auth, the program first suffers from several vulnerabilities. Second, the program uses an Auth object that is used after release.
As can be seen, the state of the heap after passing the Auth command and the service with some data. Now as we know strdup result is returned in eax and further check is done on 0x20 bytes from eax which resolved in (mov eax, DWORD PTR [eax+0x20]; test eax,eax ). This means that if we can pass a long string greater than 0x20 bytes in the service command, we can override the auth variable pass level.
We can also do this by assigning a service command with some data that will eventually assign one more block overriding the auth variable on the heap.
As you can see, we were able to override the auth variable by assigning the data twice using the service command.
We can try the same in terminal as shown.
Since the code was written so horribly, the lesson here is to name your variables correctly and not use the variables after freeing them. We can see that even though the variable names were assigned correctly, we can still override the auth variable with the service command several times after it was still in use after freeing.
In this article, we’ve looked at some basic heap overflows and how we can use them to overwrite data at any point and get code to run. In my next article, I will cover some heap utilization techniques.