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.
|
|
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.
|
|
Is it Vulnerable?
If we run the application we can input some data.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.