This is the fourth uncontrolled format string vulnerability exercise from the Protostar image from Exploit Exercises. This one again requires writing to a variable using a format string, but this time we are writing 4 bytes instead of one. I’ll be showing two methods to solve it, each writting the the integer’s address using
For this challenge we are given the following source:
Reading through the code it is practically the exact same as format 2 except we are now writting 4 bytes to
target instead of just one. We will again be using piped echo output to send our exploit to the program.
The program still contains the improper implementation of
printf() vulnerability, and while at first it may seem like we can solve this the exact way format 2 was solved, writing 4 bytes instead of 1 is a little trickier due to the properties and limits of
%n. This gives us two main options: we can either target each byte of the address specifically by putting mutliple addresses on the stack each increasing the least significant byte, or we could do the operation in two writes making use of the fact that short writes don’t destroy data on the stack and writing 2 bytes at a time.
The Exploit - Four-Write Method
The program again uses
fgets() to get our input, so our input shouldn’t be that far away on the stack. Again we can probe for its location using repeated
Our input shows up at the 12 position on the stack. Next up, you guessed it, is finding the address of
target by looking in the bss segment:
Target exists at 0x080496f4. To write 4 bytes of input we will have to specify each byte by name that we want to write to, so we will be writing to 0x080496f4, 0x080496f5, 0x080496f6, and 0x080496f7. Putting these locations on the stack will also force us to specify a new stack location to use with direct parameter access (DPA) each time. So if our first byte specified is at $12, the next would be at $13, then $14 and finally $15.
So far we are looking at our exploit as:
This is good news as we now have data being written to each byte of
target. So far we have printed 16 bytes of data, so each byte of
0x10. Now we need to calculate how many characters to write with our width field to make target equal
0x01025544. To do this we just need to keep track of each write we make because it will affect the count needed for later writes.
Starting with the least significant byte, which needs to be
0x44 = 68:
Then the next byte,
0x55 = 85:
The third byte presents a problem. is smaller than , so there is no way we can add up to for the next step. To solve this, we can take advantage of the same property of
%n that makes writing multiple bytes with it tricky. If the data we want to write is larger than
ff, the extra bits will overflow to the next byte.
01 to overflow, and
02 to be left. This means we want the next value to be
0x102 = 258:
That third step actually kills two birds with one stone, because by overflowing we also write
0x01 to our last byte. This reduces our four write solution to a three write solution.
Adding these writes to our exploit we get:
The Exploit - Two-Write Method
Getting a three write solution is pretty good, but we can bring it down to two bytes which is neater and easier on the stack as data that’s next to our address isn’t at risk of being destroyed. To do this we again use
%n, but this time we specify to do a short write with %hn. As per its name we are now writing 16 bits instead of 32, so our data will now fit evenly with two writes because we don’t have to worry about overflow.
The construction is similar to the 4 write method. Our input is the same location on the stack (
%12). We first want to write
0102 to the 2 most significant bytes of our address because it is the smallest value we are writing. Since we already have 8 bytes written from the two address: x
Note that the second address specified isn’t necessary yet, but I included it so we don’t have to adjust the math later.
0x0102 worked, now we can write the rest: x
We modified our target correctly, notice that we wrote to the two least significant bytes last. Doing this without a short write would have overwritten the data we already wrote, but since we only wrote 16 bits the information is preserved and we got our success message.