• Non ci sono risultati.

1.5 Tecniche avanzate di attacco

1.5.2 Return oriented programming

Con il nome di Return Oriented Programming si fa riferimento ad una modalit`a di attacco che pu`o essere considerata come un’evoluzione della categoria degli attacchi return-into-libc e borrowed-code-chunks [23] . La tecnica sviluppata da Shacham si basa sull’idea di combinare alcune sequen- ze di istruzioni terminate da una ret fino ad ottenere una funzione di base, come ad esempio lo spostamento di un valore dalla memoria ad un registro o un’operazione aritmetica come xor o and. Ognuna di queste sequenze viene denominata gadget. La tesi dell’articolo [23] `e che concatenando una appropriata sequenza di gadget sia possibile eseguire una qualsiasi sequen- za di operazioni. Le sequenze di istruzioni necessarie alla costruzione dei gadget possono essere individuate all’interno dei programmi disponibili tra- mite l’analisi del codice binario del programma. L’articolo illustra come sia possibile costruire dei gadget sfruttando la libreria standard C come base di codice da analizzare, ma si ipotizza che a partire da una qualsiasi base di codice sufficientemente estesa sia possibile recuperare le sequenze necessarie a costruire tali gadget. Le fasi necessarie a condurre l’attacco, che andremo di seguito a descrivere nel dettaglio sono:

• individuare le sequenze di istruzioni utili allo scopo

• assemblare tali sequenze in gadget che, se concatenati, permettano l’esecuzione della funzionalit`a desiderata

Come individuare le sequenze

Prima di entrare nel dettaglio di come sono state recuperate le sequenze di istruzioni necessarie alla costruzione dei ’gadget ’ definiremo in modo pi`u preciso cosa intendiamo per ’sequenza utile’ di istruzioni. Una sequenza di istruzioni pu`o essere considerata utile se gli elementi della sequenza sono istruzioni valide che non deviano il flusso di esecuzione (ad esempio salti condizionati e non). Inoltre l’ultima istruzione della sequenza deve essere una ret. Le sequenze che possono essere usate per la costruzione dei gadget possono essere ottenute a partire dall’analisi statica degli eseguibili. Nel caso dell’ISA Intel IA-32 vi `e inoltre la possibilit`a di sfruttare il fatto che le istruzioni abbiano lunghezza variabile per estrarre sequenze interpretando

solo parti di istruzioni. Possiamo difatti interpretare una sequenza di bytes qualsiasi come un opcode, dunque partendo da un certo offset rispetto al- l’inizio dell’istruzione originaria c’`e la possibilit`a di ottenere comunque un opcode valido. Con questa procedura ci `e possibile individuare sequenze che non avremmo potuto individuare in altro modo dato che non sono sta- te inserite intenzionalmente nel programma. Quanto frequente si verifichi questo fenomeno `e un fattore che dipende dalle caratteristiche dell’ISA su cui stiamo lavorando, ovvero da quanto `e probabile che una sequenza di by- te scelta nel modo sopra descritto possa rappresentare un’istruzione valida. L’Instruction Set IA-32 `e molto denso, ovvero un flusso di byte casuale pu`o essere interpretato come una serie di istruzioni valide con una probabilit`a sufficientemente alta da rendere fattibile questo tipo di approccio.

L’algoritmo usato per popolare l’albero delle sequenze valide, chiamato Galileo, si basa sull’assunzione tale per cui `e pi`u semplice cercare all’indie- tro a partire da una sequenza valida, piuttosto che il contrario. Le sequenze trovate inizieranno quindi tutte a partire dalle occorrenze di istruzioni ret presenti all’interno della base di codice scelta per l’analisi. Il procedimento scelto per la ricerca `e ricorsivo e parte dal byte immediatamente precedente alla sequenza valida attuale. Ci si chiede se questo byte rappresenta un’istru- zione valida, nel caso lo sia si crea un nuovo nodo dell’albero e si continua leggendo il byte precedente. A questo punto si controlla se la concatenazione di questi due ultimi byte costituisca un’istruzione valida. Se lo `e si aggiunge un altro nodo allo stesso livello del precedente e si itera fino a raggiungere il numero massimo di byte consentiti per un’istruzione IA-32 valida. A questo punto si discende di un livello nell’esplorazione dell’albero e si considera co- me sequenza valida anche l’istruzione riconosciuta con le iterazioni appena completate e si ricomincia l’analisi a partire da questo punto. Una volta co- struiti gli alberi delle possibili sequenze valide contenute nella base di codice considerata il passo successivo `e quello della costruzione dei gadgets.

Assemblare ed eseguire gadgets

L’articolo mostra inoltre come a partire dalle sequenze valide individuate con l’algoritmo Galileo sia possibile costruire gadgets che implementino le funzioni essenziali per la computazione che vengono raggruppate in quattro categorie:

• load/store

• aritmetica e logica • flusso di controllo

• syscalls e funzioni di libreria

E’ dunque possibile costruire un’insieme di gadgets tale da poter affermare che tale insieme sia Turing-completo e permetta l’esecuzione di funzioni ar- bitrariamente complesse. Dal punto di vista pratico le limitazioni principali sono quelle dello spazio disponibile dove inserire la sequenza di indirizzi e parametri che permettono l’esecuzione dei gadget. Andiamo ora a descrivere nel dettaglio la struttura di una stringa di overflow in grado di implementare un attacco di tipo ROP prendendo ad esempio l’attacco descritto in figura 1.9 La figura ci mostra lo stato dello stack al momento in cui uno stack

Figura 1.9: Uno schema della tecnica di Return Oriented Programming overflow ha avuto successo. La prima cella di memoria alla base della figura contiene il valore deadbeef che `e andato a sovrascrivere il frame pointer salvato della funzione originaria. L’attaccante ha inoltre deviato con suc- cesso il flusso di esecuzione del programma sovrascrivendo anche il return address. L’esecuzione ora passer`a quindi al primo gadget che consiste in una sequenza popret che permette di assegnare ai registri %eax ed %edx i va- lori 08049167 e 080491a1 assegnati durante l’overflow alle celle dello stack immediatamente precedenti.

A questo punto viene eseguito il gadget successivo che far`a uso di questi valori e cos`ı via.

Come abbiamo visto `e quindi necessario costruire la stringa di overflow tenendo conto dell’endianness dell’architettura e poi collocare in ordine la sequenza di indirizzi a cui si trovano i gadget che vogliamo vengano eseguiti, sfruttando sequenze di popret per assegnare i valori da elaborare nei registri opportuni.

Documenti correlati