Format String Exploitation

A format string defines the way in which data will be represented when printed. For instance, the following C code will output the current year:

If data supplied to the printf function is derived from user input, this may lead to a format string vulnerability.

Taking advantage of the vulnerability allows attackers to read arbitary memory locations, which assist with further memory corruption attacks by undermining the effectiveness of ASLR.

In addition, an attacker may be able to write to arbitary memory locations and achieve code execution.

The below code listing shows an application which is vulnerable to format string exploitation.

Reading Memory Locations

The “%p” format string parameter interprets data entered as a pointer. If the number of format string parameters exceeds the amount of data entered additional values from the stack will be presented.

The above example shows stack memory being displayed after using the “%p” format string. We can clearly see the “AAAA” characters entered being echoed back to the user in hexadecimal format (0x41414141).

Writing to Memory

While the above behavior is interesting, to subvert the application logic and reach the victory function we will need to modify the “security_flag” variable to be a value greater than zero. This can be achieved using the “%n” format string parameter. This parameter takes the number of characters written, and stores the output into a pointer, which is supplied as an argument.

First, we need to find the memory address of the security_flag variable in memory. We can do this using GDB:

So, we know we need to write a value greater than 1 to the address 0x804a048.

The above exploit first packs the address we wish to write to, then uses the %p parameter to read some data. The %n parameter then counts the output produced from %p and stores it in the memory address referenced.

We can run the script, and output the contents to a file:

python exploit.py > out

Then run the target executable in GDB. Set a breakpoint on the exit function of the get_password method:

Next, run the application and evaluate the contents of the security_flag variable:

We can see the security check has been passed since the number 27 has been written to the security_flag address.