Protostar: Format 0
A while back I went through the uncontrolled format string vulnerability exercises in the Protostar image from Exploit Exercises and I have decided to go through them again, this time with writeups. I’ll do these without recompiling the source with debug messages, extracting information from standard fuzzing techniques and calculations to align the attacks. Let’s get started on Format 0.
The Source
For this challenge we are given the following source:
And the following specification:
- This level should be done in less than 10 bytes of input.
We already know this program is vulnerable to a format string exploit, so we can get right into it.
The Vulnerability
Reading through the program source we can see that it takes a command line argument and passes it to the vuln()
function. In this function the program:
- Declares a volatile int
target
(volatile probably to make sure the compiler doesn’t remove it during the program optimization) - Declares a 64 byte character array,
buffer
- Sets
target
to zero - Uses the
sprintf()
function to print our string into the character array - Checks if
target
is now 0xdeadbeef, and if so prints our success message
Since in the program target
is never set to 0xdeadbeef, we must use some form of stack-smashing to overwrite it. The call to sprintf()
is vulnerable to a buffer overflow because it doesn’t check the size of our input (try snprintf()
next time). It is also vulnerable to a format string attack because it doesn’t properly call sprintf()
with our input, so we can inject our own format strings.
The Buffer Overflow
We can easily overflow the allotted 64 byte buffer and write to the next item on the stack by taking advantage of the call to sprinf()
. Since we are given the source, we can easily see that the target
variable and the char buffer live next to each other on the stack. This makes overflowing as simple as:
This fills our 64-byte buffer with ‘A’ and (remembering our endianness) writes 0xdeadbeef to the next item on the stack.
As we expect, running this we are greeted with our success message:
However we are over our 10 byte limit and we didn’t use any format strings in our exploit! Luckily there is a format string function which allows us to specify a width field which we can use to fill the buffer and accomplish our goal.
Combining With the Format String
To write 64 bytes with a format string, we could do %64x
which would use hex to write 64 bytes (if we wanted we could use a different type like d or s, but x looks nice). This takes fewer characters than the straight overflow and allows us to complete the challenge.
Implementing this idea our previous exploit becomes:
which is 8 bytes in length. Sure enough, running it gives: