Buffer Overflow (ret2func)
EXPLOITING STACK BUFFER OVERFLOWS OVER FUNCTIONS
BUFFER OVERFLOW
A buffer overflow occurs when the size of data exceeds the storage capacity of the memory buffer
As a result, the program will try to write the data to the buffer which overwrites nearer memory locations Instruction Pointer(IP), Base Pointer(BP)
C and C++ are two languages that are highly susceptible to buffer overflow attacks, as they don’t have built-in safeguards against overwriting or accessing data in their memory
Mac OSX, Windows, and Linux all use code written in C and C++.
WHAT ARE BUFFERS?
Buffers are memory storage regions that temporarily hold data while it is being transferred from one location to another
CAUSE & MITIGATTION
Buffer overflow is triggered by user input
In the case of buffer overflow vulnerabilities, the developer must check the input length before using any functions that might cause an overflow to happen
These attacks are caused by vulnerable functions in C
The following five common unsafe functions that can lead to a buffer overflow vulnerability:
printf, sprintf, strcat, strcpy, and gets.
Unfortunately, the base C language provides only one safe alternative: fgets (to be used instead of gets). Various platforms have their non-standard implementations. For example, the Microsoft version of C includes sprintf_s, strcpy_s, and strcat_s
You can also protect against buffer overflows by using an extension of a compiler that uses “canaries”
The canaries are special values that the compiler places on the stack between the location of the buffer and the location of control data
When a buffer overflow occurs, it is the canary that is corrupted first and this corruption can be immediately detected
MEMORY LAYOUT
The memory layout of a binary in stack can be reprsented as
The buffer space grows towards the Base Pointer (BP) and Instruction Pointer (IP) from lower memory to higher memory
Below Base Pointer (BP) there will be Instruction Pointer (IP)/Return Address
The stack components of the program are always stored above the Base Pointer (BP)
EXAMPLE
Lets consider this simple program
Compile this to binary
Here we have declared a ‘buffer’ space for char array variable of 32 bytes
So by theory this buffer space of this variable should hold only 32 bytes
Lets check it by passing inputs of random length
If we pass input with size less than 32 bytes,it runs perfectly
But if this size limit exceeds,it stores this input from the starting space of the ‘buffer’ and it ends in memory where the input data ends
Until that size, our input can overwrite the buffer space in the stack memory
This leads to serious flaw by which an attacker can change the track of the program
When we overwrite some important registers like Instruction Pointer (IP) and Base Pointer (BP),it points to an address which can be utilized for exploitation
It the address is not meaningful, it gives “ERRORS”(Segmentation Fault/Stack Smashing Detected/Core Dumped)
But with the help of the input values we can ovewrite these important register correctly to acheive our desired task/function
EXPLOITATION
Lets see how to use buffer overflow in stack to call another function from the memory
In this program we can see a function named “hidden” which is not being called in “main()”
So the actual program should receive input and print it as output
It should not display the contents inside the “hidden()”
To test stack based buffer overflows in Linux, you need to compile the source code with certain flags to enable “DISABLE STACK PROTECTION & STACK EXECUTION”
STEP 1 : CHECKING ASLR
Here we use a debugging line to print the address of “hidden()” function
Whenever this function needs to be called, our binary uses “call address_of_hidden()” in ASM to run “hidden()”
This program prints random address for “hidden()” whenever it is being called
This is because of ASLR present in the machine
ASLR (Address Space Layout Randomization)
Address space layout randomization (ASLR) is a memory-protection process for operating systems (OSes) that guards against buffer-overflow attacks by randomizing the location where system executables are loaded into memory
Lets disable ASLR to get the fixed address of “hidden()” to perform successful buffer overflow attack
Now the ASLR is disabled
Lets check for the address of “hidden()”
No matter how many times we run, it always gives us fixed address of “hidden()” when we try to run the program, because ASLR is disabled
STEP 2 : CRASHING THE PROGRAM
When all the protections are disabled, try to crash the program with inputs of random sizes
You can see that this program also gets crashed due to the size of the input passed which is larger than the buffer size
We can also view the crash with traps using “dmesg”
By seeing this we can come to a conclusion that this program is vulnerable to buffer overflow attack
STEP 3 : FINDING OFFSET
Lets pass the input data to crash the program and test it in GDB Debugger
So, when we pass the random junks of ‘A’ in input it causes an error
And during the crash the Base Pointer (BP) value is set as 0x4141414141414141
Here we can say that our junk input overwrote the Base Pointer (BP) register
We need to find the offset(location) of the Base Pointer (BP) so that we can craft our payload precisely
Lets create a random pattern of 100 bytes with “GDB-PEDA” and pass it through the program
Our pattern overflowed the buffer space and the Base Pointer (BP) causing an error
In this , we can see that our Base Pointer (BP) is filled by a random value from the pattern we passed as input
This value is a part of our pattern we passed
So this value can be used to find the offset of our Base Pointer (BP)
By calculating the offset it says that our Base Pointer (BP) memory starts after 64bytes of the “buffer” memory
STEP 4 : CONTROLLING INSTRUCTION POINTER
We know where our Base Pointer (BP) starts
Next to Base Pointer (BP) is the Instruction Pointer (IP)
Instruction Pointer (IP)
It is a register responsible for storing the address of next instruction which is needed to be executed in stack
The main goal of all buffer overflow attacks is to overwrite the Instruction Pointer (IP) which can help the attacker to redirect the program to their purpose
We know that for x64 architecture,
Base Pointer (BP) ---> 8 bytes
Instruction Pointer (IP) ---> 6 bytes
'A' in hex ---> 0x41
'B' in hex ---> 0x42
Lets pass an input crafted with the help of python to check,whether we can overwrite the IP or not
Here we have ‘A’s to fill upto BP
And 8 ‘B’s to fill BP
And 6 ‘B’s to fill IP
Lets pass this input and check it
Here we can see that BP and IP is overwritten successfully
STEP 5 : EXPLOITING WITH PAYLOAD
Now we have verified that we can overwrite IP successfully
To spawn the function “hidden()” we need to call it using IP
When IP is pointed with the address of “hidden()”,it executes the contents in the “hidden()” and allocates the registers according to the stack and continues the execution
Since we are using “LITTLE ENDIAN” our address should be represented in reverse order
Lets test the payload
Here we can see our “hidden()” function has been pwned successfully
Lets try this out of debugger