BROP - Blind Return Oriented Programming

Supporta HackTricks

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 sia 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. O 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(<non read addr>, <non read addr>) -> crash

  • strcmp(<non read addr>, <read addr>) -> crash

  • strcmp(<read addr>, <non read addr>) -> crash

  • strcmp(<read addr>, <read addr>) -> nessun crash

È possibile controllare questo chiamando ciascuna voce della tabella PLT o utilizzando il PLT slow path che consiste fondamentalmente nel chiamare un'entrata nella tabella PLT + 0xb (che chiama dlresolve) seguito nello stack dal numero di entrata che si desidera sondare (partendo da zero) per scansionare tutte le voci PLT dalla prima:

  • strcmp(<non read addr>, <read addr>) -> crash

  • b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP -> Andrà in crash

  • strcmp(<read addr>, <non read addr>) -> crash

  • b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

  • strcmp(<read addr>, <read addr>) -> 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 ospiterà 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

Supporta HackTricks

Last updated