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.
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')
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).
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.