format
Performing arbitrary writes using format strings.
Last updated
Performing arbitrary writes using format strings.
Last updated
This is an introduction to the arbitrary write of the format string bug. This allows us to write arbitrary values to an address of our choice. We use this to overwrite an authentication variable, which allows us to get the flag.
For this binary, we will supply the source code to understand the binary better. This is the binary:
In this case, here's what we notice:
There is a secure fgets
call that doesn't allow for buffer overflow. This means that we can't just overwrite the auth
variable (we also know that global variables get loaded into a different memory segment, so buffer overflow doesn't make much sense).
There is a format string vulnerability where they print our password. This means that we can leak data off the stack.
Our goal is to modify auth
, which is a global variable. This is where the arbitrary write comes in. We have a slight advantage that auth
is global, meaning its address is known at compile time. This means that we can write to it directly.
Let's first see what kind of data we can leak. We'll use a series of %x
inputs to leak as much data as possible.
We notice that a series of the same data pattern reappears after a few iterations. A quick observation shows us that:
This means that this pattern represents our input! Our input starts at the 7th position off the stack. This is vital info; we can use this as a baseline for where we are in memory.
There is a special format specifier in C that has a particular function: %n
. %n
is unlike the other format specifiers in that it stores data, namely the number of bytes written thus far. Typically, this is used like below:
This prints:
How can we exploit this? If we can pass in an address of our choice, we can write a number to that address.
We need three things to perform an arbitrary write:
The location on the stack of our buffer
The location of the address we want to write
The value we want to write
We figured out the first one already! We determined that our input started at the 7th position. Now we need the address of where we want to write. There are two ways to do this:
Use the list of global variables in gdb
: info variables
(is
in radare2
)
Use the symbol table, accessible with readelf -s <binary>
Either way, we get that auth
is located at 0x0804c02c
. Finally, we know that we want to write 10 bytes, meaning that before we place the %n
, we need to write 10 bytes.
Let's put this all together and discuss why this payload works:
We first write the address of auth
to the stack. Then, we write six more bytes of data to fill the requirement of writing 10 bytes of data. Finally, we write the %n
specifier, which writes the number of bytes written so far to the address at the 7th position on the stack. This is the address of auth
, so we write 10
to auth
!
Let's put this all together and get the flag!
Running this gives:
We see that auth
is changed, and the flag is printed!