You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
> Di solito i comandi shell nelle varie risorse online sono preceduti da un `$`, che rappresenta il prompt del terminale.
37
+
> Questo viene usato per:
38
+
>
39
+
> - indicare che il comando va eseguito come **utente normale** (ovvero non come root).
40
+
> - rendere il copia e incolla più difficile e costringere a modificare il comando prima di eseguirlo, riducendo il rischio di eseguire comandi pericolosi per errore.
41
+
42
+
## Buffer Overflow
32
43
33
44
Ogni problema si può comprendere su diversi livelli di astrazione. Solitamente, ragioniamo su un programma C (o qualsiasi altro linguaggio) al livello del linguaggio stesso.
34
45
Da questo punto di vista il programma non è altro che un insieme di variabili e funzioni, e tutta l'esecuzione parte dal `main`. Sappiamo però che ciò che esegue il nostro computer è un binario generato dal compilatore e dal linker -> **cosa succede tra un' astrazione e l'altra?**
@@ -38,7 +49,9 @@ Da questo punto di vista il programma non è altro che un insieme di variabili e
38
49
Senza entrare nelle specifiche, concettualmente il nostro computer funziona in modo "lineare" consuma un "nastro" di codice, eseguendone le istruzioni.
39
50
Questo differisce abbastanza dalla visione strutturata di un programma C, dove abbiamo diverse funzioni innestate che si chiamano fra di loro (o se stesse)
40
51
41
-
### cos'è veramente una funzione?
52
+
### Come funziona una funzione?
53
+
54
+
Oltre al gioco di parole, è importante capire come funziona una funzione a basso livello.
42
55
43
56
Non esiste un concetto nativo di funzione per il computer esegue un programma : il programma è semplicemente un insieme (tendenzialmente) monolitico di istruzioni, da caricare in memoria (il nastro).
44
57
@@ -56,9 +69,9 @@ A un alto livello, cosa serve per implementare una funzione?
56
69
57
70
D'ora in poi considereremo nello specifico l'architettura x86_32, i cui eseguibili sono facili da eseguire sulla maggior parte dei computer moderni
58
71
59
-
### un semplice programma-cavia
72
+
### Un semplice programma-cavia
60
73
61
-
Prendiamoci un attimo di pausa dalla teoria per analizzare un semplice programma C che prende input dall'utente con la `gets`:
74
+
Per capire meglio come funzionano le chiamate a funzione, consideriamo il seguente programma:
62
75
63
76
```c
64
77
#include<stdio.h>
@@ -81,73 +94,163 @@ int main() {
81
94
}
82
95
```
83
96
97
+
Il programma è molto semplice, chiede il nostro nome e lo stampa a schermo. Da notare la funzione `gets`, che è **molto** pericolosa e non dovrebbe essere usata mai in un programma reale.
98
+
84
99
Se provate a compilare questo programma normalmente, il compilatore si arrabbierà (con buone ragioni). **Perchè**?
85
100
101
+
```bash
102
+
$ gcc dim.c
103
+
104
+
dim.c: In function‘funzione_normalissima’:
105
+
dim.c:10:5: error: implicit declaration of function‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
A prima vista, il programma sembra funzionare correttamente. Notiamo però che la variabile `buffer` è grande solo 64. Cosa succede se passiamo più di 64 caratteri?
89
116
90
117
Facciamolo!
91
118
92
-
A prima vista, il programma sembra crashare, **perchè**? Cos'altro può succedere?
119
+
```shell
120
+
$ ./dim
121
+
Qualche professore amerebbe questo programma
122
+
Come ti chiami? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-`gdb` : debugger, ci aiuterà a capire meglio cosa fa il nostro programma e a capire perchè crasha
97
132
-`pwntools` : libreria di python, ci permette di interagire in modo programmatico con il nostro binario
98
133
99
134
Entrambi installati di default su Kali!
100
135
101
-
### cosa sta succedendo?
136
+
### 2. Cosa sta succedendo?
137
+
138
+
Proviamo ad analizzare il problema con `gdb`:
139
+
140
+
```shell
141
+
$ gdb ./dim
142
+
(gdb) run
143
+
```
102
144
103
-
Aprendo il programma con gdb ed eseguendolo vediamo qualcosa del genere:
145
+
Inseriamo il nostro input e vediamo cosa succede:
104
146
105
147
```
106
148
Program received signal SIGSEGV, Segmentation fault.
107
149
0x41414141 in ?? ()
108
150
```
109
151
110
-
Il programma ha provato ad eseguire una "funzione" all'indirizzo 0x41414141. è un caso?
152
+
L'errore di segmentazione (SIGSEG) è il più classico, ma cosa significa `0x41414141 in ??`?
153
+
154
+
Normalmente `gdb` ci mostra sia l'indirizzo di memoria in cui si è verificato l'errore, sia la funzione in cui si è verificato.
155
+
156
+
-`0x41414141` è un indirizzo di memoria, ma cosa rappresenta?
157
+
-`??` indica che `gdb` non sa cosa ci sia in quell'indirizzo di memoria
158
+
159
+
Il programma ha quindi provato ad eseguire una "funzione" all'indirizzo `0x41414141`. E' un caso?
160
+
161
+
Se proviamo a convertire `0x41414141` in ASCII otteniamo "AAAA", che è esattamente una parte del nostro input.
111
162
112
-
"AAAA", codificato come indirizzo è proprio 0x41414141 -> abbiamo sovrascritto qualcosa di importante con il nostro input
163
+
Possiamo quindi dedurre che **il programma ha provato ad accedere ad una cella di memoria che è stata sovrascritta con il nostro input**.
113
164
114
-
### come sono implementate le chiamate a funzione in x86_32?
165
+
### 3. Come sono implementate le chiamate a funzione in x86_32?
115
166
116
-
Non entreremo troppo nei dettagli (layout dello stack e memoria), ciò che ci basta sapere per ora è questo:
167
+
Non entreremo troppo nei dettagli (layout dello stack e memoria), ciò che ci basta sapere per ora è questo: durante una chiamata a funzione, il programma salva i metadati della funzione chiamante nello stack, e poi salta (_jump_ incondizionato) alla funzione chiamata.
117
168
118
-
- è che i _metadati_ di una chiamata sono conservati in modo adiacente ai _dati_ -> per ora immaginiamoci che vengano direttamente dopo in memoria
119
-
- accedere ad un elemento in un array (array[i]) vuol dire accedere ad un elemento in un indirizzo (array + i * sizeof(type)) -> buffer[65] ad ex. è un'espressione sensata in questo contesto
169
+
Lo stack è una struttura dati a pila, ovvero una struttura dati in cui l'ultimo elemento inserito è il primo ad essere rimosso, quindi, al momento di chiamare la funzione, i metadati della funzione chiamante si trovano in cima allo stack.
120
170
121
-
Possiamo sovrascrivere l'indirizzo di ritorno -> possiamo eseguire una funzione qualsiasi!
171
+
Durante l'inizializzazione della funzione chiamata, vengono pushati sullo stack i dati della funzione chiamata (ovvero le variabili locali) e poi viene eseguito il codice della funzione.
122
172
123
-
### cosa eseguiamo?
173
+
Quindi:
124
174
125
-
Sappiamo che una funzione è identificata da un indirizzo in memoria -> come troviamo quello di `impossibile_da_chiamare`?
175
+
> I _metadati_ di una chiamata a funzione sono conservati in modo adiacente ai _dati locali_ della funzione chiamante;
176
+
per ora immaginiamoci che siano situati negli indirizzi di memoria appena successivi.
126
177
127
-
Riapriamo gdb:
178
+
Inoltre ricordiamoci che In C accedere ad un elemento in un array (`array[i]`) vuol dire accedere ad un elemento in un indirizzo = `array + i * sizeof(type)`.
179
+
180
+
Ad esempio, `buffer[65]` nonostante sia "fuori dal buffer" è un modo per accedere al 65° byte del buffer, che però non è del buffer stesso ma di un'altra variabile in memoria (o, in questo caso, dei metadati della funzione chiamante).
181
+
182
+
Utilizzando queste due informazioni, possiamo capire cosa è successo:
183
+
184
+
1. Abbiamo sovrascritto il buffer con più di 64 caratteri;
185
+
2. Abbiamo sovrascritto i metadati della funzione chiamata;
186
+
3. Nel momento in cui la funzione chiamata tenta di ritornare alla funzione chiamante (`return`), il program counter (PC) si sposta all'indirizzo che abbiamo scritto come 65° byte del buffer (in realtà non è detto sia proprio il 65° byte, in quanto il compilatore può inserire padding tra le variabili).
187
+
188
+
Possiamo quindi controllare il PC, ergo possiamo eseguire una funzione qualsiasi (con alcuni limiti, ma per ora non ci interessano)!
189
+
190
+
### 4. Cosa eseguiamo?
191
+
192
+
Proviamo, ad esempio, a chiamare la funzione `impossibile_da_chiamare`.
193
+
194
+
Sappiamo che ogni funzione è caricata in memoria ad un indirizzo specifico dello spazio di indirizzamento del processo
195
+
. Come facciamo a sapere quale è l'indirizzo di `impossibile_da_chiamare`?
196
+
197
+
Riapriamo `gdb`:
128
198
129
199
```
130
-
x *impossibile_da_chiamare
200
+
gdb ./a.out
131
201
```
132
202
133
-
NB : per motivi di sicurezza di solito gli indirizzi di memoria cambiano tra un esecuzione e l'altra (ASLR). Possiamo disabilitare questa feature su Linux con:
203
+
e chiediamo l'indirizzo della funzione:
134
204
135
205
```
136
-
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
206
+
(gdb) x *impossibile_da_chiamare
137
207
```
138
208
139
-
### come scriviamo un indirizzo arbitrario in input?
209
+
oppure
140
210
141
-
Non tutti i byte dell'indirizzo corrispondono a caratteri ASCII. A prima vista dal terminale non riesco a codificare l'indirizzo!
211
+
```
212
+
(gdb) info functions
213
+
```
142
214
143
-
In realtà un `char` può avere qualsiasi valore da 0 a 255, basta riuscire a mandarlo in input -> `pwntools`
215
+
> [!WARNING] Address Space Layout Randomization
216
+
> Per motivi di sicurezza di solito gli indirizzi di memoria cambiano tra un esecuzione e l'altra (vedi _ASLR_).
217
+
> Possiamo però disabilitarlo temporaneamente con:
218
+
>
219
+
> ```bash
220
+
> $ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
221
+
>```
144
222
223
+
### 5. Come scriviamo un indirizzo arbitrario in input?
224
+
225
+
Da terminale non possiamo scrivere un indirizzo arbitrario, in quanto non possiamo scrivere caratteri non ASCII. Per questo è necessario un aiuto da parte di `python3`.
in questo modo stiamo passando al programma `dim` 64 caratteri `A` seguiti da `0x04030201` (in little-endian, ovvero con gli indirizzi in ordine inverso rispetto a come li scriviamo).
232
+
233
+
In alternativa, possiamo usare `pwntools`:
234
+
235
+
```python
236
+
r = process("./dim")
146
237
r.sendline(b"A"*64+b"quello che voglio \x01\x02\x03")
238
+
r.interactive()
147
239
```
148
240
149
-
Non mi rimane che mandare l'indirizzo giusto. Alcune sfide:
241
+
In sequenza:
242
+
243
+
-`r = process("./dim")` crea un sotto-processo eseguendo il programma `dim`;
244
+
-`r.sendline(b"A"*64 + b"quello che voglio \x01\x02\x03")` invia la stringa al programma seguita da un newline (`\n`);
245
+
-`r.interactive()` ci permette di interagire con il programma come se fosse un terminale.
246
+
247
+
Non mi rimane che mandare l'indirizzo giusto!
248
+
249
+
## Esercizio
250
+
251
+
Alcune sfide:
150
252
151
-
- Dopo quanti caratteri lo metto?
152
-
- Come lo codifico?
153
-
-**Cosa altro posso combinare?**
253
+
- Come faccio a trovare il numero di caratteri da scrivere prima dell'indirizzo? (padding)
254
+
- Come faccio a trovare l'indirizzo di una funzione nel caso io non abbia accesso al compilato?
255
+
- Cosa riesco a fare con questa tecnica?
256
+
- Cosa posso fare per evitare che il mio programma sia vulnerabile a questo tipo di attacco?
description = "Install and configure Kali Linux in a VirtualBox virtual machine."
4
+
+++
5
+
6
+
To participate in the labs, you will need a functioning virtualization environment. As the majority of people use Windows, we will use VirtualBox as our virtualization software (if you are using Linux, you should already know how to create a virtual machine).
7
+
8
+
VirtualBox is a free and open-source hosted hypervisor, developed by Oracle Corporation. VirtualBox allows you to run multiple operating systems on a single machine.
9
+
10
+
## 1. VirtualBox Installation
11
+
12
+
1. Download the latest version of VirtualBox from the [official website](https://www.virtualbox.org/wiki/Downloads).
13
+
2. Run the installer and follow the instructions.
14
+
15
+
You should then be able to open VirtualBox and get a screen similar to the one below:
16
+
17
+

18
+
19
+
## 2. Kali Linux Installation
20
+
21
+
We will use Kali Linux as our main operating system for the labs. You can download the latest version of Kali Linux from the [official website](https://www.kali.org/downloads/).
22
+
23
+
1. Go to the [official website](https://www.kali.org/downloads/) and click on the "Virtual Machines" button.
24
+
2. Click on the "VirtualBox" button. The download should start automatically.
25
+
3. Once the download is complete, extract the files to a folder of your choice.
26
+
4. Double click on the `.vbox` file to open it in VirtualBox.
27
+
28
+
You should now have a Kali Linux virtual machine running in VirtualBox.
29
+
30
+
## Troubleshooting
31
+
32
+
If you encounter any issues during the installation process, please refer to the [official VirtualBox documentation](https://www.virtualbox.org/wiki/Documentation).
33
+
34
+
If you are still having trouble, feel free to ask for help in the Telegram chat or directly during the meetings.
0 commit comments