BROP - Blind Return Oriented Programming
Informazioni di Base
Lo scopo di questo attacco è quello di sfruttare un ROP tramite un buffer overflow senza alcuna informazione sul binario vulnerabile. Questo attacco si basa sul seguente scenario:
Una vulnerabilità dello stack e la conoscenza di come attivarla.
Un'applicazione server che si riavvia dopo un crash.
Attacco
1. Trovare l'offset vulnerabile inviando un carattere in più fino a quando non viene rilevato un malfunzionamento del server
2. Forzare il canary per effettuare un leak
3. Forzare gli indirizzi RBP e RIP memorizzati nello stack per effettuare un leak
È possibile trovare ulteriori informazioni su questi processi qui (BF Forked & Threaded Stack Canaries) e qui (BF Addresses in the Stack).
4. Trovare il gadget di stop
Questo gadget permette essenzialmente di confermare che è stato eseguito qualcosa di interessante tramite il gadget ROP perché l'esecuzione non è crashata. Di solito, questo gadget sarà qualcosa che ferma l'esecuzione ed è posizionato alla fine della catena ROP quando si cercano i gadget ROP per confermare che un determinato gadget ROP è stato eseguito.
5. Trovare il gadget BROP
Questa tecnica utilizza il gadget ret2csu. E questo perché se si accede a questo gadget nel mezzo di alcune istruzioni si ottengono gadget per controllare rsi
e rdi
:
Questi sarebbero i gadget:
pop rsi; pop r15; ret
pop rdi; ret
Nota come con quei gadget è possibile controllare 2 argomenti di una funzione da chiamare.
Inoltre, nota che il gadget ret2csu ha una firma molto unica perché estrae 6 registri dallo stack. Quindi inviando una catena come:
'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP
Se viene eseguito il STOP, ciò significa essenzialmente che è stato utilizzato un indirizzo che estrae 6 registri dallo stack. Oppure che l'indirizzo utilizzato era anche un indirizzo di STOP.
Per rimuovere questa ultima opzione viene eseguita una nuova catena come la seguente e non deve eseguire il gadget STOP per confermare che il precedente ha estratto 6 registri:
'A' * offset + canary + rbp + ADDR
Conoscendo l'indirizzo del gadget ret2csu, è possibile dedurre l'indirizzo dei gadget per controllare rsi
e rdi
.
6. Trovare il PLT
La tabella PLT può essere cercata da 0x400000 o dall'indirizzo RIP leakato dallo stack (se viene utilizzato PIE). Le voci della tabella sono separate da 16B (0x10B), e quando viene chiamata una funzione il server non crasha anche se gli argomenti non sono corretti. Inoltre, controllando l'indirizzo di una voce nel PLT + 6B non crasha 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
alla lunghezza della stringa confrontata. Nota che rdx
è il terzo argomento e deve essere maggiore di 0 per poter successivamente utilizzare write
per effettuare un leak del programma.
È possibile trovare la posizione di strcmp
nel PLT basandosi sul suo comportamento utilizzando il fatto che ora possiamo controllare i primi 2 argomenti delle funzioni:
strcmp(<indirizzo non letto>, <indirizzo non letto>) -> crash
strcmp(<indirizzo non letto>, <indirizzo letto>) -> crash
strcmp(<indirizzo letto>, <indirizzo non letto>) -> crash
strcmp(<indirizzo letto>, <indirizzo letto>) -> nessun crash
È possibile verificare ciò chiamando ciascuna voce della tabella PLT o utilizzando il percorso lento PLT che consiste essenzialmente nel chiamare una voce nella tabella PLT + 0xb (che chiama a dlresolve
) seguito nello stack dal numero di voce che si desidera esaminare (a partire da zero) per esaminare tutte le voci PLT dalla prima:
strcmp(<indirizzo non letto>, <indirizzo letto>) -> crash
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
-> Crashstrcmp(<indirizzo letto>, <indirizzo non letto>) -> crash
b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
strcmp(<indirizzo letto>, <indirizzo letto>) -> 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
conterrà già un valore maggiore di 0, quindi questo passaggio potrebbe non essere necessario.
### 8. Trovare Write o equivalente
Infine, è necessario un gadget che esfiltrerà 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 del 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 una delle nostre connessioni.
Firme di comportamento per trovare queste funzioni:
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Se vengono stampati dei dati, allora è stata trovata puts'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Se vengono stampati dei dati, 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 vengono stampati dei dati, allora è stata trovata write
Esploitation Automatica
Riferimenti
Documento originale: https://www.scs.stanford.edu/brop/bittau-brop.pdf
Last updated