tl;dr
- Linux heap exploitation with arbitary free vulnerability
Challenge points: 381
No. of solves: 16
Solved by: slashb4sh
This was a fun challenge. It is a menu driven program where you can store a set of numbers and also retreive the average of the numbers.
The challenges runs in an environment with glibc 2.30, It has tcache double free mitigation, as explained in my last blog.
1 | $ ./chall |
get numbers
- store maximum of 10 set of numbers on a heap chunk, no restriction to the count of numbers in one set
- calloc(count + 1, 8uLL) is called to allocate memory for the numbers, The first 8 bytes is used to store the count
- calloc() nulls out the memory region. Also, calloc doesn’t allocate from tcache
print_average
- gets index from user and prints average of the numbers at that index
- index is not validated, leading to potential information leaks
delete_numbers
- free() the pointer holding a set of numbers at a particular index and pointer is nulled out.
- index is not validated, giving us aribiary free
The table, holding all the pointers to the set of numbers, is on the heap. The allocations made to store the numbers are right below the table.
Using the out of bound indexing
we can free any chunk on the heap. As of now we do not have a heap leak to perform arbitary free. Let’s first get the heap leak
How to get a heap leak?
To leak information using the out of bounds
bug we invoke print_average
but for this there needs be a proper structure to the heap chunk. The 1st 8 bytes should be the count of the numbers, followed by the numbers themselves, the count is used to add up the numbers in a for loop, if count is too large then the program will segfault accessing invalid memory. We need to craft a chunk such that the 1st 8 bytes contain the count, it should be a reasonably low value and next few blocks should contain the information we want to leak.
To achieve this layout, I first freed a chunk in the tcache range, when you free a chunk in tcache range the a key is set to tcache structure’s address.
1 | This is what the chunk looks like after free |
As you can see there is a pointer to the tcache structure on the heap, plus this is a valid pointer for free
Now I freed this pointer with the out of bounds
indexing. Let’s see what the tcache structure looks like now
1 | 0x555555559000: 0x0000000000000000 0x0000000000000291 |
Now I allocate and free a chunk of size 0x20, This sets the tcache count of 0x20 bin to 1.
1 | 0x555555559000: 0x0000000000000000 0x0000000000000291 |
Now the tcache structure resembles the structure used to store the set of numbers. The first 8 bytes is the count, which is 1, and the next 8 bytes conatin a heap address. Invoke the print_average
function on the tcache structure.
Now that we have the heap leak we can forge chunks on the heap and free arbitary chunks.
Libc leak
Getting a libc leak is quite simple
- free a 0x440 (or any size greater than tcache range) chunk so that it gets inserted to the unsorted bin, as you know, the fd and bk are libc addresses
- allocate a large chunk of size less than 0x440 so the 0x440 chunk is split and the rest is put back in the unsorted bin
- we split the chunk so that the size of the chunk is small and can we are going to use it as the faked count of numbers
- write heap address of the structure such that the size of the heap chunk is the count of numbers and invoke
print_average
on it. - The average of all the data is print out. Extract the libc address by simply reversing the calculations.
Exploitation
We need arbitary write to get code execution, so we need calloc to allocate a chunk of our choice. Since calloc doesn’t allocate chunks off the tcache bins, there is no point in corrupting the fd of tcache chunks. Chunks get allocated from the fastbin though, we can overwrite the next pointer of a free pointer to get arbitary write, our target is restricted to __malloc_hook
as we can easily forge a size(0x7f), but this is not a an issue. (We cannot carry out a traditional fastbin double free corruption as we do not control the fd directly as it is used for count)
We will have to work with 0x70 chunks to carry out the fastbin attacks. We had already overwritten the tcache count for 0x70 chunk with value much greater than 7 when tcache structure was freed. Further freed chunks will be put in fastbin.
To do the fastbin attack:
- allocate a chunk and create a fake chunk of size 0x71 ( choose any size that will be freed into the fastbin and can be allocated back with calloc() ).
- This fake chunk will be freed and allocated back as an overlapping chunk to overwrite the next pointer of an free fastbin chunk lying right below
- allocate the target chunk of 0x71 size
- free the target chunk
- free the fake chunk by using arbitary free
- allocate a chunk of 0x70 size, the fake chunk will be returned, now overwrite the fd of the target chunk with address of the misalligned chunk above
__malloc_hook
such that the size field is 0x7f. - allocate the target chunk
- The next allocation will give you write to
__malloc_hook
Overwrite __malloc_hook
with system and call malloc
with heap address pointing to "/bin/sh"
( get_numbers
should be given addr/8 as calloc
calls malloc
with arguments as arg1*arg2 ).
You got shell :)
1 | from pwn import * |