Format String Vulnerabilities have been out there for quite a long time now, the main idea of exploiting format string vulnerabilities revolves around looking for format functions defined without format specifiers in code and passing format specifiers to them. These format specifiers allow us to read and write a value to and from any memory location thus becoming a potential candidate for code execution.
Note
If you are new to format string vulnerabilities, I will recommend reading following article before proceeding further with this article. In this article, we will cover the all the steps for writing precise data to a specific location by exploiting format string vulnerabilities. Exploiting Format Strings: The Stack
Downloads
VM used in this article can be downloaded from here. Following code snippet is provided to us for this level, as we can see that it uses a format function named sprintf insecurely. The sprintf function is used to write formatted data to a string buffer. Therefore, it will not fetch/print any data as we have seen in the case of printf, it just copies your data to a specific location. As no format specifiers are defined in the function, we can directly fill the buffer with junk padding and overwrite the target variable. The Challenge also states that the solution can be less than 10 bytes of input. Before moving forward, let us debug the binary in gdb and confirm our theoretical deductions made so far. As can be seen, if we provide 64 bytes of input with the value required to overwrite the target variable with (0xdeadbeef) we can complete this challenge. I have also highlighted the address of target variable and where our input will be landed in the stack. However, as the challenge suggested, this can be solved with payload less than 10 bytes. Let’s try to understand how. #include <stdio.h> #include <string.h> void vuln(char *string) { volatile int target; char buffer[64]; target = 0; sprintf(buffer, string); if(target == 0xdeadbeef) { printf(“you have hit the target correctly :)n”); } } int main(int argc, char **argv) { vuln(argv[1]);
Well, we can simply use any format specifiers (example: %x or %s or %p) to fill the 64-byte buffer with junk padding and then append the value (0xdeadbeef) to complete this challenge.
Following code snippet is provided to us for this level, as we can see that it uses a format function named printf insecurely. However, in this case, we can directly fetch different memory addresses from the stack and write to any memory address. To complete this level, we need to enter any value in target variable. Let’s first try to locate where out input is being placed on the stack; we can do that by appending our input with format specifiers as shown. #include <stdio.h> #include <string.h> int target; void vuln(char *string) { printf(string); if(target) { printf(“you have modified the target :)n”); } } int main(int argc, char **argv) { vuln(argv[1]); }
After some trial and error and some input padding, we got to know the position of our input which is at 129th position and using dollar ($) sign we can directly fetch the data from that location.
We further locate the address of target variable and write some value to it using %n format specifier to complete this challenge.
In this case, we need to overwrite the target variable with a specific value. We wrote the following script to determine the position of our input and found out our input is getting placed at the 4th position in the stack. #include <stdio.h> #include <string.h> int target; void vuln() { char buffer[512]; fgets(buffer, sizeof(buffer), stdin); printf(buffer); if(target == 64) { printf(“you have modified the target :)n”); } else { printf(“target is %d :(n”, target); } } int main(int argc, char **argv) { vuln(); }
We confirmed the same by running commands in terminal as shown.
Now we need to write 64 to the target variable; we opened the program in gdb set the breakpoint after printf to check how much value we are writing currently.
As can be seen, we are writing 0x18 = 24 bytes (4A’s+4B’s+4C’s +1dash+10u ), we need 40 more bytes so if pass 40 + (10 bytes for %10u that we are already writing) we should be able to write 64 bytes to the target variable. As can be seen, we were able to solve the challenge by overwriting specific bytes.
This challenge is very similar to previous one, instead of writing just some bytes we will learn how to write larger values to some specific memory location. We used our previous script to find out the location of our input as shown. #include <stdio.h> #include <string.h> int target; void printbuffer(char *string) { printf(string); } void vuln() { char buffer[512]; fgets(buffer, sizeof(buffer), stdin); printbuffer(buffer); if(target == 0x01025544) { printf(“you have modified the target :)n”); } else { printf(“target is %08x :(n”, target); } } int main(int argc, char **argv) { vuln(); }
We further find out the address of target variable using objdump and started overwriting it with some values, but the value required to pass the challenge is way larger to write in one go.
To overcome this situation, we will start writing 4 bytes at a time, that is we will be writing lower 4 bytes (0x5544) first to lower address (0x080496f4) and then higher four bytes (0x0102) to higher address (0x080496f6) By passing the format string as “echo -ne “xf4x96x04x08xf6x96x04x08″CCCC-%21814x-%12$n-%10x-%13$n|./format3″ we are able to overwrite lower bytes successfully. Note 0x5544 = 21828 – (14 bytes that we are already writing, so value becomes 21814)
For higher bytes, we are already writing 0x5550 which is a higher number than 0x0102, so we will need a larger value for subtraction with 0x5550. Therefore, we will subtract it with 0x10102 we get (43954+10 bytes which we are already writing using %10x) = 43964. Our final exploit looks like following: allowing us to write desired value to target variable.
This challenge introduces us execution flow redirection that is we need to change the current program execution flow and execute another function instead. As this level is very similar to previous one, we wrote a small script to solve this challenge. The idea of exploit is to overwrite exit GOT with the address of hello function. #include <stdio.h> #include <string.h> int target; void hello() { printf(“code execution redirected! you winn”); _exit(1); } void vuln() { char buffer[512]; fgets(buffer, sizeof(buffer), stdin); printf(buffer); exit(1); } int main(int argc, char **argv) { vuln(); }
Using above script, we were able to solve this challenge.
This is one of the interesting techniques I came across once that we can use here to get a shell, let’s understand how? So basically, we are writing exit GOT with the address of (vuln+40 i.e. 0x080484fa) and printf GOT with the address of (system i.e. 0xb7ecffb0). When the program reaches exit it will jump to (vuln+40) putting a pointer to our input on the top of the stack (lea eax, ebp-0x208),(mov DWORD PTR[esp], eax) and instead of printf call, system call will get executed with our payload.
We constructed the following exploit for gaining shell by exploiting format4 binary.
We used the following command to execute our exploit: As can be seen, we were able to get code execution by exploiting format string vulnerability.
https://g0blin.co.uk/smashthetux-vulnhub-writeup/#0x01 https://exploit-exercises.com/protostar https://www.owasp.org/index.php/Format_string_attack