Sunday, May 29, 2016

Buffer Overflow Challenge

This challenge consisted of a single 32-bit executable in which you had to exploit a buffer overflow and call the function being asked within the program. The only tools used to complete this challenge was gdb, the linux CLI debugger.

Hopping into gdb the first thing I wanted to test was its breaking point, testing with an initial input of 300 A's done with a simple python script in gdb.
 This results in a segmentation fault within the program caused by 0x41414141 (The program crashed due to the overflow of A's).

To get a better view of the program in action we need to set some break points, to find the best break points we can analyze the assembler code for the programs main function.
I decided to put breaks at the start of the main function, at all of the print calls, and at the end of the main function. So in gdb we need to put breaks at main, *main+23, *main+40, *main+85, and *main+96.
From this we can see two things:
  • Breakpoint 3, or *main+40, prints the string "Call me twice" which we can assume will be the point we will need to call later on.
  • Breakpoint 4, or *main+85, prints "Buffer overflows are easy" + our input string and it also displays the point in which the program becomes completely overrun by the buffer overflow.
Now that we know the points of corruption within the program caused by the buffer overflow we can analyze the stack pointer register (esp) at each breakpoint to assess the damage of the overflow throughout the program.

We will be analyzing the first 100 memory addresses of our stack point of breakpoints 3, 4, and 5.
  
Breakpoint 3 (python -c 'print "A" * 300')

Breakpoint 4 (python -c 'print "A" * 300')

Breakpoint 5 (python -c 'print "A" * 300')
 

To actually inject code through a buffer overflow attack we're going to need to find the lowest point of overflow. This was ultimately done like a game of higher and lower until I found the sweet spot of 112 A's. Why 112 you ask? Good question, that's due to the way gcc, the gnu C compiler, compiles code allowing it to store a bit more buffer memory than specified (in this case 12).

The green highlighting equates to the saved frame pointer on the stack, cyan represents the functions return address, pink represents the functions' arguments, and red represents the next breakpoints saved frame pointer, return address, and arguments.

Breakpoint 3 (python -c 'print "A" * 112')

Breakpoint 4 (python -c 'print "A" * 112')

Breakpoint 5 (python -c 'print "A" * 112')
   
We can see from breakpoint 4 that if we entered 4 more A's the program would have a new saved frame pointer of 0x41414141 instead of it's usual 0x7ddca00.

Now that we know it requires 112 bytes to get into the function's return saved pointer frame all we need to do is create a payload that reroutes the program to a previous method call (The one that asked to be called twice).

inspecting the disassembled main function shows a call for the function print_msg located at 0x80484e5 (the memory location of the method call we need to re-execute).

  
This memory location (0x80484e5) allows us to create a new stack pointer to redirect the program to the print_msg method call during our buffer overflow attack. Great! Now that we have our location, all we need to do is plop it onto the end of our A's right? Well, not exactly. To allow the program to read it we first need to split 0x80484e5 into groups of 2 hex digits. We then need to reverse the hex digits due to the initial memory location being of a different endianness than what we can set as our stack pointer.

With the memory location reversed we can now tack it onto our 112 A stack to create the input string of AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAA\xE5\x84\x04\x08.

Now for the hex in our string to be read as hex values we need to encase our string in some form of bash command that allows hex to be properly read. In this case we have 2 bash options: echo and printf and 2 enclosure methods: $() notation and backticks resulting in 4 different methods that result in the same answer.

`printf "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAA\xE5\x84\x04\x08"`

$(printf "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA\xE5\x84\x04\x08")

`echo - e "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAA\xE5\x84\x04\x08"`

$(echo -e "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAA\xE5\x84\x04\x08")

 
Finally giving us our well deserved victory message.






No comments:

Post a Comment