Introduction to Format String Bugs
Format strings allow developers to output data in a set format.
double AmountInDollars;
AmountInDollars = 25.01;
printf("$%.2f\n", AmountInDollars);
An example of using format strings to output a table of ASCII characters in decimal and hexadecimal representation can be found ascii.c
:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int c;
printf("Decimal Hex Character\n");
printf("======= === =========\n");
for(c = 0x20; c < 256; c++){
switch (c)
{
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x1b:
printf("%03d %02x \n", c, c);
break;
default:
printf("%03d %02x %c\n", c, c, c);
break;
}
}
return 1;
}
> gcc ascii.c -o ascii
./ascii
What is a Format String Bug
A format string bug occurs when user-supplied data is included in the format specification string of one of the printf
family of functions:
printf
fprintf
sprintf
snprintf
vfprintf
vprintf
vsprintf
vsnprintf
The attacker supplies a number of format specifiers that have no corresponding arguments on the stack and values from the stack are used in their place.
This can lead to sensitive information being exposed and arbitrary code execution.
A simple example of a vulnerable program can be found in fs.c
:
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv){
char buf[1024];
strcpy(buf, argv[1]);
printf(buf);
printf("\n");
}
> gcc -fno-stack-protector -no-pie -o fs -ggdb -m32 fs.c
> ./fs $(python3 -c "print('A' * 4 + '%x' * 4)")
AAAAffffd0bb080491b041414141
We can see that the end of the output is A ASCII code (41
). This means that the values printed are coming from the stack.
Note: Make sure that ASLR is disabled!
Format String Exploits
The parameters we pass to the the printf
family of functions are passed on the stack. When we supply too few parameters, the function will take the next values from the stack and use them instead. This allows us to print sensitive information from within the stack using the %s
or %x
specifier.
Format String bugs can also be used to run arbitrary code using the %n
specifier. This same specifier also allows us to modify values in memory in order to change the behavior of the vulnerable program.