• Non ci sono risultati.

1.5 Tecniche avanzate di attacco

1.5.3 Aggirare ASLR e W⊕X

Sfruttando una variante della tecnica di return oriented programming appena descritta `e possibile sfruttare buffer overflow anche nel caso siano attive misure di protezione quali W⊕X ed ASLR. L’attacco descritto nel- l’articolo di Roglia et al. [22] consiste nella costruzione ed esecuzione di alcuni gadget che permettono di ottenere informazioni sull’indirizzo di base a partire dal quale sia stata allocata la libreria standard C. Grazie a queste informazioni `e dunque possibile ricavare l’indirizzo di ogni altra funzione della libreria ed eseguire con successo un attacco return-to-libc aggirando di fatto la randomizzazione della memoria virtuale.

Prima di spiegare nel dettaglio il funzionamento dell’attacco riepiloghia- mo alcuni concetti che ci saranno utili per comprenderne il funzionamento. Le funzioni appartenenti a librerie condivise (shared library) non vengo- no invocate come delle normali funzioni, con una singola istruzione call. L’invocazione di una funzione di libreria dinamica passa prima attraverso l’esecuzione di alcuni salti indiretti che permettono di raggiungere infine la funzione chiamata. Ogni programma contiene infatti due strutture dati che sono dedicate allo scopo di gestire queste chiamate:

Global Offset Table - GOT un array contenente gli indirizzi in memo- ria virtuale effettivi delle funzioni appartenenti alle librerie dinamiche usate dal programma.

Procedure Linkage Table - PLT una struttura dati contenente salti in- diretti facenti riferimento ad elementi della GOT.

La struttura denominata Procedure Linkage Table `e costruita in modo che l’i-esimo elemento della tabella contenga un’istruzione di salto indiretto al- l’indirizzo dell’i-esimo elemento della Global Offset Table. Al momento della

compilazione tutte le chiamate a funzione faranno quindi riferimento agli elementi della PLT per eseguire le chiamate a funzioni appartenenti alle librerie dinamiche. Disassemblando il codice di un eseguibile che sia linka- to dinamicamente potremo quindi ritrovare esempi di chiamata come call strcpy@plt

In questi casi il debugger riconosce che l’indirizzo della funzione chiamata `

e corrispondente al record nella PLT che porter`a, tramite la GOT, ad esegui- re la vera funzione di libreria strcpy. Nel momento in cui viene eseguito il collegamento tra le librerie dinamiche e l’eseguibile infatti il linker si occupa di inserire gli indirizzi corretti delle funzioni di libreria nei record della GOT. Tali indirizzi non sono noti al momento della compilazione dato che con la randomizzazione della disposizione in memoria virtuale dei vari elementi, ad ogni esecuzione del processo le librerie dinamiche verranno mappate nello spazio di indirizzamento del processo ad un indirizzo differente, noto solo a partire dal momento in cui viene eseguito il linking dinamico. Questo processo di compilazione della GOT pu`o avvenire in realt`a al momento del- l’esecuzione del programma (preemptive binding) o pu`o essere ritardato fino all’occorrenza della prima chiamata ad ognuna delle funzioni di libreria (lazy binding).

L’ attacco sfrutta quindi le informazioni presenti all’interno della GOT per calcolare l’indirizzo di base della libreria, cos`ı da poter ricavare l’indirizzo assoluto di una funzione arbitraria della libreria stessa. Entriamo pi`u nel dettaglio delle operazioni eseguite per ricavare l’indirizzo di partenza della libreria standard C. Scegliamo una funzione dalla libreria standard C, ad esempio open e ricaviamone l’indirizzo in memoria virtuale a cui `e stata mappata, tramite la funzione address of. Abbiamo ora la necessit`a di una funzione che chiameremo got base offset(foo) in grado di restituire l’offset rispetto alla base della Global Offset Table del record contenente l’indirizzo della funzione open. A questo punto possiamo ricavare l’indirizzo di base della libreria standard eseguendo il calcolo

libc_base_address = address_of(open) - got_base_offset(open) A questo punto possiamo ricavare l’indirizzo in memoria di una qualsiasi funzione foo() appartenente alla libreria standard eseguendo

Questo calcolo deve per`o essere eseguito nel contesto del processo vulne- rabile, sfruttando il Return Oriented Programming dato che non `e possibile eseguire codice iniettato in memoria.

L’attacco `e stato implementato in due varianti leggermente diverse, che andremo a descrivere di seguito.

GOT dereferencing

Questa modalit`a di attacco implementa la tecnica di base che calcola l’indirizzo della funzione foo() desiderato tramite Return Oriented Pro- gramming per poi deviare il flusso di esecuzione alla funzione foo() Per implementare questo attacco sono dunque necessari anche solo tre gadgets. L’articolo cita, come uno dei gadget necessari ad eseguire il calcolo, una sequenza come add 0x5dc4(%ebx), %eax; pop %edi; ret. Questo gadget ci permette infatti di leggere un valore arbitrario dallo spazio di indirizza- mento del processo. Sfrutteremo quindi la sequenza per leggere l’indirizzo del record della GOT della funzione open. Per fare ci`o dovremo prima pre- disporre i valori di %eax e %ebx tramite un’altra sequenza che imposti i seguenti valori nei registri:

%eax = got_base_offset(foo) - got_base_offset(open) %ebx = got_base_offset(open) - 0x5dc4

A questo punto, dopo l’esecuzione di questi gadget, %eax conterr`a l’indi- rizzo della funzione foo e ci baster`a un terzo ed ultimo gadget del tipo jmp *%eax per trasferire il controllo alla funzione da noi scelta.

GOT overwriting

La seconda variante ha l’obiettivo di sovrascrivere uno degli elementi della Global Offset Table (ad es. quello della funzione open) con l’indirizzo di una funzione a nostra scelta per poi invocare la funzione sfruttando il record appena modificato. Questo attacco `e possibile dato che il binding tra libreria e processo viene eseguito ”on demand” dal linker, dunque la pagi- na di memoria contenente la GOT `e scrivibile. Le principali operazioni da eseguire tramite gadgets sono la lettura di una locazione di memoria, una somma ed una successiva scrittura. Per fare questo `e sufficiente un’operazio- ne di somma con operando di destinazione un indirizzo di memoria. Infine

per trasferire il controllo alla funzione da noi scelta ci baster`a sfruttare una chiamata alla funzione di cui abbiamo sovrascritto il record nella Global Offset Table.

Documenti correlati