How to exploit printf vulnerability for different input methods?


I am trying to exploit printf vulnerability with the following C code:

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

gcc -fno-stack-protector -z execstack -o test test.c
void attack(){
    printf("Dropping to shell...\n");

int main(int argc, char **argv){
    char buf[100];

    printf("Enter user name:");

    gets(buf);  // what if using: scanf("%s",buf); ?

    printf("buffer (%d): %s\n",strlen(buf), buf);

    return 0;

Then I try to change the printf@plt to jump to the attack function, by re-write the value in 0x804a00c

08048370 <printf@plt>:
 8048370:   ff 25 0c a0 04 08       jmp    *0x804a00c
 8048376:   68 00 00 00 00          push   $0x0
 804837b:   e9 e0 ff ff ff          jmp    8048360 <_init+0x28>

I used the following command to test in GDB, I try to change the value in 0x804a00c to 0x00000041, to verify that I can change it. Then i can change it to the attack() address.

gdb-peda$ r
Starting program:
Enter user name:$(printf "\x0c\xa0\x04\x08").%60x%5\$n

However it doesn't work for me, the address doesn't change, I verified the value in stack (break at printf() address) and got:

0000| 0xbffff070 --> 0x80485c6 ("buffer (%d): %s\n")
0004| 0xbffff074 --> 0x26 ('&')
0008| 0xbffff078 --> 0xbffff08c ("$(printf \"\\x0c\\xa0\\x04\\x08\").%60x%5\\$n")
0012| 0xbffff07c --> 0x0 
0016| 0xbffff080 --> 0xbffff134 --> 0x4a91b2bc 
0020| 0xbffff084 --> 0xbffff0a8 (".%60x%5\\$n")
0024| 0xbffff088 --> 0xbffff0a0 ("04\\x08\").%60x%5\\$n")
0028| 0xbffff08c ("$(printf \"\\x0c\\xa0\\x04\\x08\").%60x%5\\$n")

I think I did not pass the correct value, as it changed to another format. I guess this is because of gets(), because I can pass it correctly when I use argv to pass the: $(printf "\x0c\xa0\x04\x08").%60x%5\$n.

So does anyone know how to solve the problem? and further, what if the input is using scanf(%s,buf), as I am getting the following on stack, which is also incorrect.

0000| 0xbffff070 --> 0x80485f9 ("buffer (%d): %s\n")
0004| 0xbffff074 --> 0x8 
0008| 0xbffff078 --> 0xbffff08c ("$(printf")
0012| 0xbffff07c --> 0x0 
0016| 0xbffff080 --> 0xbffff134 --> 0x4f936a87 
0020| 0xbffff084 --> 0xbffff0a8 --> 0xb7e21c34 --> 0x2aad 
0024| 0xbffff088 --> 0xbffff0a0 --> 0xffffffff 
0028| 0xbffff08c ("$(printf")
asked on Stack Overflow Sep 19, 2016 by Eudaemon

1 Answer


You have a couple misconceptions here.

First, $(cmd) is a shell command substitution. It's useful when passing arguments: for example, ./foo $(python -c 'print "A"*4') is equivalent to ./foo AAAA. If you program takes input from stdin you have to use a pipe: for example, python -c 'print "A"*4' | ./foo is equivalent to running ./foo and typing AAAA on the keyboard.

Second, there is actually no format string vulnerability in your code. There is, however, a stack buffer overflow (on gets or scanf). A format string vulnerability happens when you call functions such as printf of scanf with a user-controlled format string. If you want one, you could change the last printf to:

printf("buffer (%d): ", strlen(buf));
printf(buf); // <-- this is vulnerable

As for the difference between gets(buf) and scanf("%s", buf): both will stop at newlines/EOFs, but scanf will also stop at whitespace (which can be seen in your output where only $(printf is read).

answered on Stack Overflow Sep 19, 2016 by Andrea Biondo

User contributions licensed under CC BY-SA 3.0