Return-Oriented Programming (ROP)

Indice dei contenuti

    Il Return-Oriented Programming (ROP) è una tecnica di exploit che permette di eseguire comportamenti arbitrari riutilizzando frammenti di codice già presenti nel binario o nelle librerie caricate, aggirando le protezioni che rendono lo stack non eseguibile. Introdotto formalmente da Hovav Shacham nel 2007 come generalizzazione del return-to-libc, ROP è diventato la tecnica dominante nell’exploit development moderno.


    Il problema che ROP risolve

    Le protezioni NX (No-eXecute) / DEP (Data Execution Prevention) marcano le pagine di dati (stack, heap) come non eseguibili. Un attaccante non può più iniettare shellcode sullo stack e saltarci sopra: il processore solleva un’eccezione.

    ROP aggira questo limite osservando che il codice già presente nel processo (eseguibile, librerie di sistema) è sempre eseguibile. L’attaccante non inietta codice: orchestra codice esistente.


    Gadget: l’unità elementare

    Un gadget è una breve sequenza di istruzioni che termina con una istruzione RET (o equivalente: JMP [reg], CALL [reg]):

    ; Gadget tipico: carica un valore in eax
    pop eax
    ret
    
    ; Gadget aritmetico
    add eax, ebx
    ret
    
    ; Gadget di sistema call (x86 Linux)
    int 0x80
    ret

    Questi gadget non sono stati scritti con intenzione dall’autore del binario: emergono naturalmente dall’analisi del codice macchina a qualsiasi offset, anche nel mezzo di istruzioni multi-byte (gadget unintended).


    Funzionamento della catena ROP

    L’attaccante sfrutta un buffer overflow o heap overflow per sovrascrivere il return address con l’indirizzo del primo gadget. Al momento del RET, la CPU:

    1. Preleva il valore in cima allo stack → indirizzo del gadget 1.
    2. Esegue le istruzioni del gadget 1 fino al RET.
    3. Il RET preleva il valore successivo dallo stack → indirizzo del gadget 2.
    4. Il processo si ripete per ogni gadget nella catena.

    Lo stack dell’attaccante è una sequenza di indirizzi di gadget e dati intermedi:

    [indirizzo gadget 1] [dato] [indirizzo gadget 2] [dato] ... [indirizzo execve/system]

    Il risultato è equivalente a eseguire un programma arbitrario composto da micro-operazioni estratte dal binario originale.


    Costruzione automatica: ROPgadget e ropper

    Strumenti come ROPgadget, ropper e pwntools automatizzano la ricerca dei gadget in un binario:

    ROPgadget --binary /usr/bin/target --rop

    L’output elenca tutti i gadget disponibili con i loro offset. In binari di grandi dimensioni (libc, kernel) esistono migliaia di gadget, sufficienti a costruire qualsiasi computazione (ROP è Turing-completo in presenza di sufficienti gadget).


    ROP e ASLR

    La principale difesa contro ROP è l’ASLR, che randomizza gli indirizzi del binario e delle librerie ad ogni esecuzione. Se l’attaccante non conosce gli indirizzi base, non può costruire la catena.

    Le tecniche per aggirare ASLR in presenza di ROP includono:

    TecnicaDescrizione
    Info leakSfruttare una vulnerabilità separata (es. format string) per leggere un indirizzo dalla memoria e calcolare l’offset base.
    Partial overwriteSovrascrivere solo i byte meno significativi del return address (i nibble bassi non sono randomizzati).
    Heap sprayRiempire l’heap con la catena ROP in molte posizioni; aumenta la probabilità che un salto arbitrario la colpisca.
    JIT sprayingNei browser JIT, spruzzare bytecode che produce gadget ROP nel codice compilato al volo.

    Varianti avanzate

    • JOP (Jump-Oriented Programming): usa gadget terminanti con JMP invece di RET, aggirando i controlli sul ritorno delle funzioni (shadow stack, CET).
    • SROP (Sigreturn-Oriented Programming): sfrutta la syscall sigreturn per caricare un intero contesto di registri controllato dall’attaccante con un solo gadget.
    • BROP (Blind ROP): costruisce la catena senza accesso al binario, sfruttando solo il comportamento del server (crash vs. no-crash) per scoprire gadget.

    Contromisure

    ProtezioneDescrizione
    ASLRRandomizza gli indirizzi base; necessaria ma non sufficiente senza PIE.
    PIE (Position-Independent Executable)Randomizza anche il base address dell’eseguibile stesso.
    Stack canaryRileva sovrascritture del return address prima del RET; non blocca info leak o heap-based ROP.
    Intel CET / Shadow StackMantiene uno stack separato dei return address in hardware; ogni RET verifica la corrispondenza.
    CFI (Control Flow Integrity)Restringe i target validi di JMP/CALL/RET a quelli legittimi determinati staticamente.

    ROP rimane la pietra angolare dell’exploit development su piattaforme x86/x64 e ARM. La comprensione dei suoi meccanismi è indispensabile tanto per chi costruisce exploit quanto per chi progetta difese.

    Ultimo aggiornamento: