Aller au contenu

Contourner DEP sous Windows avec ROP

DEP rend le shellcode sur la pile inexécutable ; une chaîne ROP appelle VirtualProtect pour rendre la région exécutable, puis y saute.

Publié le 4 min de lecture

Ceci est le deuxième tutoriel de l'espace Exploitation Windows. L'écrasement SEH nous a donné le contrôle de l'exécution. Mais DEP — le NX de Windows — signifie que le shellcode sur la pile ne s'exécutera pas. Comme sous Linux, la réponse est le ROP ; l'idiome Windows consiste à faire du ROP juste assez longtemps pour rendre la région du shellcode exécutable.

Mêmes règles : votre propre programme, VM Windows jetable, WinDbg/x32dbg + mona.py. Voir les règles du labo.

DEP : le shellcode est là, mais non exécutable

Avec DEP activé, la pile et le tas sont non exécutables. Vous contrôlez le pointeur d'instruction (via un débordement de pile ou l'écrasement SEH), et votre shellcode est en mémoire — mais y sauter provoque une faute. Plutôt que d'injecter du code exécutable, réutilisez du code existant pour changer la protection de la page du shellcode.

Une chaîne ROP de contournement de DEP Windows bascule la page du shellcode en exécutable
ROP chaingadgets qui préparent l'appel
prépare les args
VirtualProtect(...)marque la région du shellcode RX
revient dans
shellcodedésormais exécutable, s'exécute normalement
codelibrarydata

L'appel à VirtualProtect

VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect) change la protection d'une région existante. La chaîne ROP doit préparer ces quatre arguments (en 32 bits, empilés dans l'ordre) avec flNewProtect = 0x40 (PAGE_EXECUTE_READWRITE), pointer lpAddress/dwSize sur la région du shellcode, donner à lpflOldProtect une adresse de brouillon inscriptible, et régler l'adresse de retour sur le shellcode.

Faire cela à la main implique de trouver des gadgets pour charger les registres et les écrire dans les emplacements d'arguments. En pratique, mona génère toute la chaîne à partir des modules du processus :

!mona rop -m *.dll -cpb '\x00\x0a\x0d'      # build a VirtualProtect ROP chain, avoiding bad bytes

Il émet une chaîne prête (et rapporte quels imports il a utilisés). L'exploit devient alors :

# exploit.py — 32-bit DEP bypass via VirtualProtect (lab target)
import struct
def p(x): return struct.pack("<I", x)

offset = 260
rop = b"".join(p(g) for g in mona_rop_chain)   # generated by !mona rop
nops = b"\x90" * 16

payload  = b"A" * offset      # overflow to the saved return address
payload += rop                # ROP: VirtualProtect(shellcode, len, 0x40, scratch)
payload += nops + shellcode   # made executable by the chain, then run
open("poc.txt","wb").write(payload)

La chaîne appelle VirtualProtect sur la région contenant le NOP sled + shellcode, la marque PAGE_EXECUTE_READWRITE, et y revient. DEP est satisfait parce que la page est désormais légitimement exécutable.

Alignement de pile et octets interdits

Deux détails pratiques sous Windows : les octets des gadgets et arguments choisis doivent éviter les octets interdits de l'entrée (\x00, \x0a, \x0d pour les copies de chaînes), et la chaîne doit garder la pile cohérente — mona gère les deux, mais les comprendre est ce qui permet de déboguer une chaîne qui fait une faute en cours de route.

Réactivez les mitigations

MitigationEffet sur le contournement de DEP
DEPLa raison même du besoin de ROP — le shellcode injecté ne s'exécutera pas
ASLRRandomise les adresses de gadgets/modules ; il vous faut un module sans ASLR ou une fuite
CFGContraint les appels indirects à des cibles valides — voir le guide des mitigations
CET matérielUne pile fantôme casse la chaîne pilotée par ret

Une cible moderne entièrement durcie exige de vaincre l'ASLR (une fuite) et de composer avec CFG/CET — le contournement de DEP seul est nécessaire mais n'est plus suffisant.

Ce que cela apprend à un défenseur

  • DEP est nécessaire mais pas suffisant — exactement comme sous Linux. Il a mis fin à l'injection de shellcode et les attaquants ont répondu par une chaîne ROP VirtualProtect. Activez-le (il l'est par défaut), mais ne le considérez pas comme complet à lui seul.
  • L'ASLR est ce qui donne du mordant à DEP. Le facteur le plus courant d'un contournement de DEP sous Windows est un module du processus compilé sans ASLR (/DYNAMICBASE). Auditez chaque DLL chargée, y compris tierce, pour l'ASLR ; un seul module sans ASLR livre à l'attaquant des gadgets fixes.
  • Progressez vers CFG et CET. Compilez avec /guard:cf et exécutez sur du matériel compatible CET avec /CETCOMPAT ; ceux-ci ciblent le ROP lui-même, couvert ensuite.

Points clés à retenir

  • DEP (le NX de Windows) arrête le shellcode sur la pile, une chaîne ROP appelle donc VirtualProtect pour rendre la région du shellcode exécutable, puis y saute.
  • Réglez flNewProtect = 0x40 (PAGE_EXECUTE_READWRITE) et pointez l'appel sur votre région de shellcode ; mona rop construit la chaîne.
  • Évitez les octets interdits et gardez la pile cohérente — les obstacles pratiques habituels.
  • L'ASLR (nécessite une fuite ou un module sans ASLR), CFG et CET sont les mitigations qui arrêtent cela sur une cible moderne.

Ensuite : les mitigations d'intégrité de flux de Windows — SafeSEH, SEHOP, CFG et CET, et comment elles ferment les techniques de cet espace.

Guides associés