The challenge gave us the source code of the vulnerable binary, which is hosted on mercury.picoctf.net
on port 16439
. So let’s take a look at the source code, starting from the main function.
From the main function, we can see that we are first presented with a menu. If we want to “Buy some stonks”, the buy_stonks
function will be called and if we want to “View my portfolio”, the view_portfolio
function will be called. So let’s take a look at the function one by one.
From the buy_stonks
function we can identify a vulnerable function, printf()
. So for context, the syntax for printf()
is as follows.
printf [format] [argument]
With that syntax in mind, if two arguments are given, the first argument will define the second argument’s data type. For instance, strings will be defined as %s
, decimals will be defined as %d
.
However, if one argument is given, we will either be printing out the argument or defining the format. As we can put whatever we want, we can put %s
or %x
. When we provide the data format, printf()
wil try to find the second argument, and when it can’t find anything, it will start printing data from the stack. This can be easily visualised in the diagram below.
With that in mind, let’s start our attack. As a proof of concept, to see whether the vulnerability works, we will try inserting %x
after selecting to “Buy some stonks” which leads to our vulnerability. The %x
format will print out data from the stack and display them in hex values.
As we can see here, our exploit works as a hex value has been leaked from the memory. So from here, we can conclude the program has a format string vulnerability.
So, let’s put more %x
‘s to see what we can leak from memory. Transfering that hex data into CyberChef, and converting it from hex to string, we can see some what of a format of a flag. However, it’s quite messed up, maybe because of the endianess? The endianess determines how data is transferred into memory. For our case, the program probably is in little-endian, where what is display is reversed.
However, by converting the data from little endian to big endian, the flag still doesn’t look right.
However, remembering previous buffer overflow challenges, maybe one byte can affect the next byte’s values. I started to remove hex values starting from the beginnning.
Doing so, reveals the “formatted” (see what I did there?) flag.
picoCTF{I_l05t_4ll_my_m0n3y_c7cb6cae}