Exploit Format String Vulnerability In Printf() Funtion Of C
Hi this is Gurdeep, In this blog I want to discuss a vulnerability in c/c++ printf() which i encountered recently while playing a CTF. Let’s start this without wasting time.
So in this challenge we had 2 files rot26 and rot26.c . rot26 file is linux 32-bit executable file and we also have the C source code of that.
first thing i did was run that executable file to see the program in action.
as you can see it ask me for input and i give “abcd” and hit enter, it gives abcd as output.
Next thing i did was check its source code i.e rot26.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *ualphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *lalphabet = "abcdefghijklmnopqrstuvwxyz";
char *rot26(char *dst, char *src, size_t n)
{
int i, x;
for (i = 0; i < n; i++) {
if (isupper(src[i])) {
x = ualphabet[((src[i] - 'A') + 26) % strlen(ualphabet)];
} else if (islower(src[i])) {
x = lalphabet[((src[i] - 'a') + 26) % strlen(lalphabet)];
} else {
x = src[i];
}
dst[i] = x;
}
}
void winners_room(void)
{
puts("Please, take a shell!");
system("/bin/sh");
exit(EXIT_SUCCESS);
}
int main(void)
{
char buf[4096];
char sanitized[4096];
setbuf(stdout, NULL);
setbuf(stdin, NULL);
setbuf(stderr, NULL);
fgets(buf, sizeof(buf), stdin);
rot26(sanitized, buf, sizeof(sanitized));
printf(sanitized);
exit(EXIT_FAILURE);
}
As you can see in the code , there are three functions, rot26,winners_room and main. In main function it declared 2 char arrays named buf and sanitized and then take input using fgets() storing it in buf, then it calls rot26 function , after that it called printf() to print sanitized and then exits using exit() . rot26() shift each char in buf by 26 and store them in sanitized.
Note: in English ,shifting an alphabet by 26 is equal to shifting by 0, i.e why we have same output as input coz sanitized is same as buf.
As you noticed the winners_room() is never get called. And this is the function which gives us shell as it is executing statement System(“/bin/sh”) .
So i find that task in this challenge is to call this function by exploit any vulnerability this code has.
Next thing is to find that vulnerability to call winners_room. After doing some R&D i found that this code is vulnerable to FORMAT STRING vulnerability. The reason is the way it uses printf() it directly take sanitized as parameter in line printf(sanitized);
If you are familiar with c programming you know the printf() treat some characters or string differently. for example %c to print characters, %d to print integers and %x to print hex values. So next thing what i do is give %d as input to the program (as one key thing in hacking is about giving non intended inputs to programs and observe its behavior). The output is
also the output of %x is
You can see instead of printing %d or %x back, program prints integer and hex values. which is obvious. But question is what these values represent. First i thought these values are garbage values but after doing some homework i found that these are not garbage values. These are values stored in memory stack of program. Also the string we give as input is stored in memory stack too. Next thing i want to know is exact offset of the string which i give as input to program. and i found that by giving this simple input.
i gave it AAAA and some %x. output is quite explainable.
important thing in output is 41414141(the seventh hex value) and as its a hex value and 41 in hex also represent A which start of our input so i figure out that 7 is the offset of our input string in the stack. So good so far.
Before further working with format string i want to analyze the program in gdb(gnu debugger)
I ran it in gdb and disassemble main function to see the call to printf() funtion.
here you can see call to the print() function at main+181 at address 0x0804882c, also there is call to exit at address 0x80484a0 so i check disassembly of that too
as you can see it jumps at 0x804a020 , so i check what is in that address using x 0x804a020 command. It says exit@got.plt: , GOT here has specific meaning Global Offset Table. I am not gonna explain what GOT is(you can google it) Summary of Got is, it store offset or address of all global variable and function used by program and as exit is global function it has entry in GOT under name exit@got.plt. The value of exit@got.plt: i.e 0x080484a6 is actually the return address where program returns after calling exit.
Immediately i figured the attack i need to complete this challenge. All i need is to change the return address of exit_plt to the address of winners_room() so that when program exits it execute the winners_room() and give us the shell which is what this function doing
void winners_room(void)
{
puts("Please, take a shell!");
system("/bin/sh");
exit(EXIT_SUCCESS);
}
To test this first i manually change the return address in gdb using set command before that i also add break point after call to printf function i.e main+186 and run the commands as follows
As you can see after couple of command i changed the value of return address to address of winners_room and we are just one step behind calling exit function.
What you think gonna happen if i continue the execution??
lets check it out
running this code we get the shell!! Boom!!
You guessed correct, winners_room get executed and we get the shell. I can confirm using basic linux command like whoami,pwd and ls.
But if i tried to exit from the shell using exit i again get shell. Seems like i stuck in a loop. Can you tell me why??
Well , it is because winners_room() also calls exit() at end of it void winners_room(void) { puts(“Please, take a shell!”); system(“/bin/sh”); exit(EXIT_SUCCESS); }
and as we changed the return address for exit to winners_room it become an recursion calling winners_room again, isn’t it fascinating. I guess it is.
SO GOOD SO FAR.
Next task is doing same thing but this time using format string vulnerability. I want to achieve same goal but by exploiting Format String . How to do that well if you don’t know it , do not worry you come to right place.
As we already discuss , using %x we find the offset of input string which was 7. Also there is %n which can be used to write values to stack. This is exactly what we want as we need to write the return address as discussed above.
So I create a python exploit file to do some stuff
Here I have imported struct . Then i store the address of winners_room & exit_plt to use later. I also define a function pad to pad the string by some “X” to make it of length 540(enough to get the task done, however we have buffer of 4096 which quite large and unnecessary )
after that i define a exploit variable and first append the address of exit_plt(here i use pack function to pack the address in endian form used in Linux architech) to it as this the address where we want to write and then some chars and then “%7$n” to write . what i am basically doing in this code is writing at exit_plt using %7$n” as discussed above.
then i run this code and save the output in inp file using ./hack.py > inp command
I ran the program in gdb and check if i successfully changed the exit_plt values.
As you can see the value of exit_plt is changed from 0x080484a6 to 0x00000010 but it very less than the address of winners_room , now we only have to find a way to change it to exact value we want. To find that , we need to do some math.
I will Try to first write the lower four bytes then upper bytes of GOT value. as the address of winners_room is 0x8048737 I want to write 8737 to the lower 0000 of GOT. As now i have 0010 in it , when i gave 0 values to print i.e not used “%x” and 10 in hex means 16 …. and i need 8737 i.e 34615 in decimal. so i need to print 34615−16 = 34599 chars using “%34599x” so i add exploit+=”%7$34599x” line to the code
running the program in gdb with the output of above code we get
As you can see lower bytes successfully changed to 8737. impressive!!
now to change the upper bytes to desired value i pack the address of uppser 2 bytes using exit_plt+2 so that it affect those bytes only. and then again do the math to find correct value. This time i will not show calculation. But i find that the number is 32973 .
so my final python code become
i also have subtract 4 from previous number we found.
running the code using output of this code in gdb gives us
As you can see we have called the winners_room but we got infinite recursion so have to quit manually. Now the final thing is to exploit the remote server. What i did so far is on local machine. Now have to test it in remote machine.
TO do that i added few lines in the code. I use pwntools to connect to remote server. My final exploit was
running this code we get the shell!! Boom!!
Then i read the flag file in the server using cat command and it says Congrats!! you did it.
I really like this challenge as it teach me a lot and hope you like it too. Feel free to ask question in comment section or any suggestion for new blog. Thanks for reading.
Happy Hacking.