BROP - Blind Return Oriented Programming

Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Informazioni di Base

L'obiettivo 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. Trova l'offset vulnerabile inviando un carattere in più fino a quando non viene rilevato un malfunzionamento del server

2. Forza bruta del canary per effettuare un leak

3. Forza bruta degli 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. Trova il gadget di stop

Questo gadget permette fondamentalmente 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. Trova 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é estrarrà 6 registri dallo stack. Quindi inviando una catena come:

'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP

Se viene eseguito lo STOP, questo significa fondamentalmente che è stato utilizzato un indirizzo che estrae 6 registri dallo stack. Oppure che l'indirizzo utilizzato era anche un indirizzo di 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 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. Trova 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 fondamentalmente 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 -> Crash

  • strcmp(<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 esfiltra 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 qualche nostra connessione.

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 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 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 dati, allora è stata trovata write

Sfruttamento Automatico

Riferimenti

Impara l'hacking di AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks:

Last updated