-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathintroduzione.tex
208 lines (190 loc) · 12.1 KB
/
introduzione.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
\thispagestyle{empty}
%\clearpage{\pagestyle{empty}\cleardoublepage}
\chapter*{Introduzione}
\addcontentsline{toc}{chapter}{Introduzione}
\markboth{Introduzione}{}
A seguito dell'introduzione di misure di sicurezza come l'isolamento
delle pagine di memoria, la casualizzazione dello spazio degli
indirizzi (ASLR), e i controlli di consistenza dello stack, le tecniche
per poter sfruttare le vulnerabilit\`{a} presenti in un programma al fine di
ottenerne il controllo sono cambiate radicalmente.
La tecnica forse pi\`{u} abusata era lo \emph{stack smashing}
\cite{Phrack-96}, con la quale si compromisero centinaia di
sistemi. La tecnica consiste nello sfruttare l'assenza o l'errato controllo dei
limiti di un buffer memorizzato sullo stack per poter corrompere lo
stack stesso, cio\`{e} veniva causato uno \emph{stack buffer overflow}. In
questo modo poteva essere ``iniettato'' nello stack codice arbitrario,
solitamente definito \emph{shellcode} per via del suo obiettivo pi\`{u}
comune: ottenere una shell. Scegliendo accuratamente i dati che
andranno a sovrascrivere lo stack, l'attaccante prende quindi controllo
della macchina su cui gira il programma attaccato.
Infatti, nello stack sono presenti informazioni chiave per il
funzionamento del codice macchina costituente un programma. Ad esempio, al momento di una chiamata a
funzione, l'indirizzo dell'istruzione successiva alla chiamata stessa viene memorizzato
sullo stack; in questo modo, una volta terminato il lavoro della
funzione, il programma sa da dove riprendere
l'esecuzione. L'attaccante, una volta preso il controllo dello stack,
pu\`{o} sovrascrivere il valore di questo indirizzo di ritorno, controllando da
dove il programma continuer\`{a} la sua esecuzione. All'attaccante quindi
basta redirezionare il flusso del programma sul codice da lui stesso
iniettato.
Sono state introdotte diverse misure di protezione per mitigare questo
tipo di attacco. Con l'introduzione dell'isolamento delle pagine di
memoria \`{e} stato possibile adottare una politica dei permessi in
memoria con la granularit\`{a} di una singola pagina. Il tentativo di eseguire
delle istruzioni presenti in una pagina marcata come non eseguibile, cos\`{\i} come il
tentativo di scrivere su una pagina marcata come non scrivibile causa l'arresto del
programma. Il codice legittimo viene invece mappato in memoria in pagine
eseguibili ma non scrivibili, mentre stack e dati, che il programma
deve manipolare, vengono mappati su pagine scrivibili ma non
eseguibili. In questo modo non si pu\`{o} pi\`{u} semplicemente iniettare del
codice in posizioni di memoria arbitraria, in quanto le aree di memoria scrivibili (tra cui l'area di
memoria riservata allo stack) non possono contenere istruzioni che
possano essere eseguite, senza causare l'interruzione del programma da parte
del sistema operativo.
Un'altra misura di protezione per rendere pi\`{u} difficile questo tipo di
attacco \`{e} la casualizzazione dell'area di memoria nel quale
risiede lo stack. In questo modo, non sapendo dove il codice iniettato
effettivamente risiede, non si sa dove dover redirezionare il
controllo del flusso per eseguire il codice iniettato.
Tuttavia l'isolamento delle pagine di memoria e la casualizzazione
dello stack non evita la compromissione n\'{e} del flusso del programma n\'{e}
dello schema dello stack. Un modo per ottenere il controllo di un
programma nonostante lo stack non sia marcato come eseguibile, consiste
nel riutilizzare il codice pre-esistente del programma stesso, come nella tecnica
denominata \emph{return-to-libc}\cite{c0ntex,
solar-return-to-libc}. In un utilizzo tipico di questa tecnica, il
flusso di esecuzione non viene dirottato su codice iniettato
dall'attaccante ma su una funzione di una qualche libreria utilizzata
dal programma. Su un'architettura a 32 bit gli argomenti vengono
passati alle funzioni posizionandoli secondo un ordine preciso
(definito dalla \emph{calling convention}) sullo stack. Dato che anche
lo schema dello stack pu\`{o} essere compromesso, l'attaccante ha,
predisponendo un arrangiamento minuzioso dei valori da posizionare sullo stack, di fatto la
possibilit\`{a} di controllare gli argomenti da passare alla
funzione. Potendo, ad esempio, richiamare la funzione
\lstinline{system} della libc con argomenti arbitrari \`{e} possibile
eseguire un qualsiasi comando sulla macchina in cui si sta eseguendo
il programma, oppure, chiamando la funzione \lstinline{mprotect}
(sempre nella libc) con gli opportuni argomenti, si possono modificare
i permessi della pagina di memoria dove risiede lo stack, rendendola
eseguibile. A quel punto redirezionare il flusso del programma sul
codice iniettato sullo stack non ne provocher\`{a} l'arresto. La tecnica
funziona perch\'{e} il codice ad essere eseguito risiede in un area di
memoria legittimamente eseguibile.
Una tecnica per mitigare questo tipo di attacco \`{e} la casualizzazione
dello spazio degli indirizzi (\emph{Address Space Layout
Randomization}). Infatti nel momento in cui ASLR \`{e} attivo le
librerie vengono mappate ad indirizzi di memoria casuali, che cambiano
da esecuzione ad esecuzione del programma. In questo modo non si
conoscono gli indirizzi delle funzioni e un attacco di tipo
return-to-libc na\"{\i}ve non risulta possibile. Tuttavia su 32 bit, e sotto
alcune condizioni, questi indirizzi possono essere facilmente ricavati
con un attacco a forza bruta\cite{Shacham:2004}. Anche se su
un'architettura 64 bit un attacco a forza bruta non risulta fattibile, \`{e}
tuttavia possibile che si riesca a sfruttare qualche altro errore di
programmazione per ottenere l'indirizzo della libreri che \`{e} fonte del codice riusabile,
riuscendo cos\`{\i} a montare comunque l'attacco.
Una generalizzazione della return-to-libc \`{e} la tecnica che prende
il nome di \emph{Return Oriented
Programming}\cite{Shacham-2007,Roemer-2012}. In questo caso il
flusso viene redirezionato non pi\`{u} all'inizio di una funzione, ma
all'inizio di una piccola serie di istruzioni, gi\`{a} presenti in un
area di memoria del programma marcata come eseguibile, seguita da
un'istruzione \lstinline{ret}. L'istruzione \lstinline{ret} preleva dalla
memoria dello stack un indirizzo e ridireziona il flusso del programma
a quel dato indirizzo e viene solitamente utilizzata al termine
dell'esecuzione di una funzione per tornare alla porzione di codice
che l'ha richiamata. Potendo controllare lo stack l'attaccante pu\`{o}
arrangiarlo in modo che una volta che l'esecuzione arrivi
all'istruzione \lstinline{ret} della prima serie, sullo stack sia
presente l'indirizzo di una seconda serie dalle caratteristiche simili
alla prima. Iterando questa tecnica pi\`{u} volte \`{e} possibile
concatenare un numero arbitrario di queste piccole serie di istruzioni
(che prendono il nome di \emph{gadget}). L'insieme degli indirizzi dei
gadget e dei parametri che se iniettati sullo stack deviano il flusso
del programma in modo da eseguire una catena di gadget vengono
denominati, sebbene con un piccolo abuso del termine,
\emph{payload}. La ROP \`{e} stata presentata per la prima volta in
\cite{Shacham-2007} e, quando sono presenti opportune tipologie di
gadget, utilizzando il ROP si possono effettuare computazioni
turing-complete. Tramite la ROP \`{e} possibile applicare tecniche per
eludere varie misure di protezione \cite{roglia:2009} tra cui ASLR.
ASLR \`{e} un termine abbastanza generico e fin ora abbiamo presupposto
che solo gli indirizzi delle librerie e dell'area dello stack siano
casualizzati, in realt\`{a} quando un programma viene compilato affinch\'{e}
il suo codice sia indipendente dalla posizione in cui viene mappato in
memoria (\emph{Position Indipendent Executable}), anche il codice del
programma stesso pu\`{o} essere caricato in un indirizzo casuale di
memoria e cambiare tra un'esecuzione e l'altra. In questo caso non \`{e}
pi\`{u} facilmente possibile montare un attacco del genere, in quanto non
vi \`{e} pi\`{u} nessuna parte di codice che essendo mappata ad una posizione
nota, possa essere sfruttata come destinazione del flusso del
programma. In altre parole anche se abbiamo il modo di influenzare il
flusso del programma, non sappiamo dove redirezionarlo. Altre tecniche
sono state sviluppate per mitigare attacchi di tipo ROP, anche se non
risultano efficaci o l'overhead \`{e} troppo grande \cite{Davi-2014}.
Lo scopo di questo lavoro di tesi \`{e} lo sviluppo di uno strumento che
possa aiutare nel compito di generare un payload che, se iniettati
sullo stack di un eseguibile, possa sostituire il normale flusso del
programma con l'esecuzione di una serie di gadget che hanno come scopo
quello di lanciare un programma arbitrario sulla macchina (ad esempio
una shell). In particolare lo strumento riesce a generare catene di
gadget per eludere misure di sicurezza come stack non eseguibile,
casualizzazione dello spazio degli indirizzi e casualizzazione dello
stack applicando, in maniera automatica, tecniche come \emph{got
patching}, \emph{got dereferencing} e \emph{return to plt}
\cite{roglia:2009}. Lo strumento utilizza per l'analisi del binario il
recente framework di sviluppo BARF \cite{Heitman-14} e supporta
l'architettura x86 e x86-64. Per la costruzione di questo payload, in
particolare per l'estrapolazione e l'analisi dei gadget, \`{e} decisivo
l'apporto dato da tecniche e strumenti propri dell'Intelligenza
Artificiale.
La \emph{Satisfiability Modulo Theories}\cite{Barrett-14} \`{e}
un'estensione della logica del primo ordine in cui l'interpretazione
di alcune funzioni e predicati viene esplicitata da alcune teorie
specifiche. Questo consente una modellazione naturale e efficiente di
strutture dati come quelle dei bit vector e degli array, rendendolo un
ottimo strumento per la modellazione dell'architettura di un
elaboratore (specialmente dei registri e della memoria). Negli ultimi
tempi \`{e} stata dedicata molta attenzione all'utilizzo di risolutori di
formule SMT con applicazioni nell'ambito della sicurezza
informatica\cite{Vanegue:2012}. Riuscendo infatti a modellare le
istruzioni e il contesto di esecuzione di un programma in formule SMT,
il risolutore pu\`{o} essere utilizzato, ad esempio, per verificare che un
programma aderisca ad una data semantica o per estrapolarne la
semantica stessa.
Anche l'esecuzione simbolica viene utilizzata sempre di pi\`{u}
nell'ambito della sicurezza. Usando l'esecuzione simbolica, un interprete
esegue le istruzioni da analizzare come in una simulazione ma, invece
di utilizzare valori concreti per i valori d'ingresso, utilizza valori
simbolici. Durante l'esecuzione, nelle varie operazioni vengono
utilizzati i valori simbolici. In questo modo sia le espressioni che
le variabili del programma, sia le condizioni che descrivono quando il
flusso segue un ramo di esecuzione rispetto ad un altro, possono
essere espresse in relazione ai valori simbolici. Queste relazioni
possono essere espresse sotto forma di formule SMT in modo da poter
interrogare un risolutore, al fine di chiedere, ad esempio, per quali valori
in ingresso il programma segua o meno un certo ramo d'esecuzione.
Nell'ambito del ROP un risolutore SMT pu\`{o} essere utilizzato sia per
estrapolare in maniera automatica la semantica di un gadget, sia per
ottenere le condizioni di partenza che consentano ad un gadget di
eseguire una determinata funzione, ad esempio i valori di registro per
i quali un dato gadget scrive in una specifica locazione di memoria un
dato valore.
Questo elaborato \`{e} organizzato come segue: la prima parte del primo
capitolo \`{e} una breve panoramica tecnica sui meccanismi principali
coinvolti nell'esecuzione di un programma che ci consente di avere le
basi, per analizzare, nella seconda parte dello stesso capitolo, alcune
delle principali tecniche che vengono utilizzate oggi per eludere le
pi\`{u} comuni misure di protezione attraverso la ROP. Il secondo capitolo
invece \`{e} una descrizione dettagliata dell'architettura designata per
Dropper, il sistema di payload generation presentato in questa tesi.
Il terzo capitolo parla invece dell'attuale
implementazione di Dropper. Infine, nelle conclusioni, sono presenti considerazioni
generali emerse durante il lavoro di tesi nonch\'{e} sugli sviluppi futuri
di Dropper.
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "tesi"
%%% End: