BROP - Blind Return Oriented Programming
Impara e pratica Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Impara e pratica Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Informazioni di base
L'obiettivo di questo attacco è essere in grado di abuse un ROP tramite un buffer overflow senza alcuna informazione sul binario vulnerabile. Questo attacco si basa sul seguente scenario:
Una vulnerabilità nello stack e conoscenza di come attivarla.
Un'applicazione server che si riavvia dopo un crash.
Attacco
1. Trova l'offset vulnerabile inviando un carattere in più fino a quando non viene rilevato un malfunzionamento del server
2. Brute-force canary per rivelarlo
3. Brute-force indirizzi RBP e RIP memorizzati nello stack per rivelarli
Puoi trovare ulteriori informazioni su questi processi qui (BF Forked & Threaded Stack Canaries) e qui (BF Indirizzi nello Stack).
4. Trova il gadget di stop
Questo gadget consente fondamentalmente di confermare che qualcosa di interessante è stato eseguito dal gadget ROP perché l'esecuzione non è andata in crash. Di solito, questo gadget sarà qualcosa che ferma l'esecuzione ed è posizionato alla fine della catena ROP quando si cercano gadget ROP per confermare che un gadget ROP specifico è stato eseguito.
5. Trova il gadget BROP
Questa tecnica utilizza il gadget ret2csu. E questo perché se accedi a questo gadget nel mezzo di alcune istruzioni ottieni gadget per controllare rsi
e rdi
:
Questi sarebbero i gadget:
pop rsi; pop r15; ret
pop rdi; ret
Nota come con questi gadget è possibile controllare 2 argomenti di una funzione da chiamare.
Inoltre, nota che il gadget ret2csu ha una firma molto unica perché andrà a poppare 6 registri dallo stack. Quindi inviando una catena come:
'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP
Se il STOP viene eseguito, questo significa fondamentalmente che è stato utilizzato un indirizzo che sta poppando 6 registri dallo stack. Oppure che l'indirizzo utilizzato era anche un indirizzo STOP.
Per rimuovere quest'ultima opzione, viene eseguita una nuova catena come la seguente e non deve eseguire il gadget STOP per confermare che il precedente ha effettivamente poppato 6 registri:
'A' * offset + canary + rbp + ADDR
Conoscendo l'indirizzo del gadget ret2csu, è possibile inferire l'indirizzo dei gadget per controllare rsi
e rdi
.
6. Trova PLT
La tabella PLT può essere cercata da 0x400000 o dall'indirizzo RIP rivelato dallo stack (se PIE è in uso). Le voci della tabella sono separate da 16B (0x10B), e quando una funzione viene chiamata il server non va in crash anche se gli argomenti non sono corretti. Inoltre, controllare l'indirizzo di una voce nella PLT + 6B non va in crash poiché è il primo codice eseguito.
Pertanto, è possibile trovare la tabella PLT controllando i seguenti comportamenti:
'A' * offset + canary + rbp + ADDR + STOP
-> nessun crash'A' * offset + canary + rbp + (ADDR + 0x6) + STOP
-> nessun crash'A' * offset + canary + rbp + (ADDR + 0x10) + STOP
-> nessun crash
7. Trovare strcmp
La funzione strcmp
imposta il registro rdx
sulla lunghezza della stringa che viene confrontata. Nota che rdx
è il terzo argomento e abbiamo bisogno che sia maggiore di 0 per poter utilizzare successivamente write
per rivelare il programma.
È possibile trovare la posizione di strcmp
nella PLT in base al suo comportamento utilizzando il fatto che ora possiamo controllare i 2 primi argomenti delle funzioni:
strcmp(<indirizzo non leggibile>, <indirizzo non leggibile>) -> crash
strcmp(<indirizzo non leggibile>, <indirizzo leggibile>) -> crash
strcmp(<indirizzo leggibile>, <indirizzo non leggibile>) -> crash
strcmp(<indirizzo leggibile>, <indirizzo leggibile>) -> nessun crash
È possibile controllare questo chiamando ciascuna voce della tabella PLT o utilizzando il percorso lento PLT che consiste fondamentalmente nel chiamare una voce nella tabella PLT + 0xb (che chiama dlresolve
) seguita nello stack dal numero di voce che si desidera sondare (partendo da zero) per esaminare tutte le voci PLT dalla prima:
strcmp(<indirizzo non leggibile>, <indirizzo leggibile>) -> crash
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
-> Andrà in crashstrcmp(<indirizzo leggibile>, <indirizzo non leggibile>) -> crash
b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
strcmp(<indirizzo leggibile>, <indirizzo leggibile>) -> nessun crash
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
Ricorda che:
BROP + 0x7 punta a
pop RSI; pop R15; ret;
BROP + 0x9 punta a
pop RDI; ret;
PLT + 0xb punta a una chiamata a dl_resolve.
Avendo trovato strcmp
, è possibile impostare rdx
su un valore maggiore di 0.
Nota che di solito rdx
avrà già un valore maggiore di 0, quindi questo passaggio potrebbe non essere necessario.
8. Trovare Write o equivalente
Infine, è necessario un gadget che esfiltri i dati per esfiltrare il binario. E in questo momento è possibile controllare 2 argomenti e impostare rdx
maggiore di 0.
Ci sono 3 funzioni comuni che potrebbero essere abusate per questo:
puts(data)
dprintf(fd, data)
write(fd, data, len(data)
Tuttavia, il documento originale menziona solo la funzione write
, quindi parliamone:
Il problema attuale è che non sappiamo dove si trova la funzione write all'interno della PLT e non conosciamo un numero fd per inviare i dati al nostro socket.
Tuttavia, sappiamo dove si trova la tabella PLT ed è possibile trovare write in base al suo comportamento. E possiamo creare diverse connessioni con il server e utilizzare un FD alto sperando che corrisponda a alcune delle nostre connessioni.
Firme di comportamento per trovare quelle funzioni:
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Se ci sono dati stampati, allora è stata trovata puts'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Se ci sono dati stampati, allora è stata trovata dprintf'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Se ci sono dati stampati, allora è stata trovata write
Sfruttamento automatico
Riferimenti
Documento originale: https://www.scs.stanford.edu/brop/bittau-brop.pdf
Impara e pratica Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Impara e pratica Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Last updated