Protostar Format String Exploits (Solutions 0-4)
Format string exploit exercises.
Published on 29 August 2012Overview
Protostar is a series of exercises from Exploit Exercises. In addition to three final levels, it has four basic sections: network programming, format strings, heap overflows, and stack overflows.
This post contains solutions and walkthroughs for the four format string levels (“Format”).
Solutions
Format: Level 0
Description (full): Change the value of a stack variable to 0xdeadbeef
using less than 10 bytes of input.
This exploit shows similarities (sometimes) between format string exploits and buffer overflows.
Format: Level 1
Description (full): Change the value of a global variable, target
, to any nonzero value.
target
is a global variable. To overwrite it, we first find its address using obdjump -t
(we could also use nm
to save some keystrokes).
Next, we need to find where the format function expects the first (missing) argument to the format string.
An important detail: adding n characters will actually decrease the starting address of the string’s characters by n bytes. Adding one more character decrements the address from 0xbffff98f
to 0xbffff98e
. This leaves the location of the first parameter unchanged.
Remember that the offset is calculated relative to the format function (i.e. printf
) rather than vuln()
.
0xbffff784
: The first argument to the format string.0xbffff997 - n
: The first character of the format string, wheren
is the total number of characters in the string. The odd alignment is caused by the null byte.
As seen earlier, the distance between the beginning of the format string (the address of target
) and the first argument to the format string (a starting address for “stack popping” additional arguments) is about 500 bytes.
With a few calculations, we can find the correct offset.
Format: Level 2
Description (full): Change the value of a global variable, target
, to 0x40
.
Using the same techniques as before, we find the beginning of the format string in memory. The fourth expected format string argument coincides with the beginning of the format string in memory. We store our target address at the beginning of the format string in order to write to it.
Finally, we calculate the number of bytes needed to write 64 (0x40). The address of target
occupies four bytes first, and we need another 60 bytes.
Format: Level 3
Description (full): Change the value of a global variable, target
, to 0x01025544
.
We calculate the offset between the format function parameters and the characters of the string in the same way as the previous exercises.
We can confirm that our offset of 48 (12 * 4 bytes) is correct.
Next, we need to change the value of target to 0x01025544
. Below is each byte in decimal:
Finally, we write values to each of the four bytes of target
:
Note that it wasn’t necessary to include the last address, 0x080496f7
, since the most significant byte (0x01
) is written as a consequence of the overflow.
Format: Level 4
Description (full): Redirect execution flow to hello()
by using a format string exploit.
In this exercise, the offset is only 16 bytes (four parameter arguments). The calculation is omitted as the two previous exercises illustrate the process.
In order to perform this exploit, we’ll overwrite an entry in global offset table. Specifically, we’ll overwrite the entry of the exit()
function (0x08049724
) with the address of the hello()
function (0x08048b4
).
To confirm that our overwrite works correctly, we run a quick test:
Execution jumped to the address 0x00000004
, meaning that we’re on the right track. Finally, we use a short write (%hn
, allowing us to write to two bytes) to overwrite the last two bytes of the GOT entry. This works because both addresses start with 0x0804
– only the last two bytes need to be changed.