summaryrefslogtreecommitdiff
path: root/docs/chapters/setup.tex
blob: be0ee4a0f8c0428873bd943910abc4734cac6a3c (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
In genere, quando vogliamo tenere traccia del traffico di richieste che vi è dentro una web app (XHR \cite{XHR:1}, loading di immagini, fonts, codice JS) apriamo la "console sviluppatore" che ci dà a disposizione Firefox (o qualsiasi altro browser, come Chrome) e iniziamo a guardare. 
Con questa relazione però, vogliamo fare un attacco attraverso un dispositivo mobile, quindi controlleremo il traffico in uscita nella nostra rete per scoprire a quale server la nostra mobile app sta facendo capo.
\newline\newline
Imposteremo tutto il necessario per replicare l'attacco visto nel capitolo precedente:
\begin{itemize}
	\item Una REST API con un problema di autorizzazione nell'endpoint degli utenti, il quale non verifica che l'utente loggato è effettivamente il possessore di quella risorsa. La installeremo in un server su internet;
	\item Un'applicazione mobile che fa richieste a tale API;
	\item Wireshark\cite{WIRESHARK:1} per monitorare la rete.
\end{itemize}

\section{API}
Nella realtà, come questa API pubblica \cite{REDDIT:1}, si espone un endpoint \emph{/api/v1/me/} dove \emph{v1} è la versione dell'API in cui si ritornano i dati per l'utente autenticato. E questa è una buona prassi, un endpoint che si può trovare più o meno in tutte le REST API.

\textbf{Cosa proveremo a fare noi?} Proprio un'API che fa ciò, niente più e niente meno. Ci limiteremo però solo a controllare che il JWT passato è valido in modo da ritornare i dati dell'utente che noi pensiamo sia stato autorizzato.
\newline\newline
Il codice di questo servizio è presente al link \underline{\url{https://git.dcariotti.me/m6-ie/tree/server}}.
\newline\newline
La parte incriminata è la route qui sotto. Qui si limita a ritornare la riga utente che corrisponde all'ID utente passato dall'header.

\begin{lstlisting}
async fn get_user(claims: Claims) -> Result<Json<UserList>, AppError> {
    match User::find_by_id(claims.user_id).await {
        Ok(user) => Ok(Json(user)),
        Err(_) => Err(AppError::NotFound),
    }
}
\end{lstlisting}

in realtà qui non vi è nessun problema reale di sicurezza. È un API che funziona, ad ogni richiesta infatti controlla se il token è valido

\begin{lstlisting}
// bearer = variable with token string

let token_data = decode::<Claims>(bearer.token(), &KEYS.decoding, &Validation::default())
            .map_err(|_| AppError::InvalidToken)?;
\end{lstlisting}

infatti il problema sta nell'inizializzazione della codifica/decodifica di JWT, in particolare quando definiamo il secret.

\begin{lstlisting}
static KEYS: Lazy<Keys> = Lazy::new(|| {
    let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");
    Keys::new(secret.as_bytes())
});

impl Keys {
    fn new(secret: &[u8]) -> Self {
        Self {
            encoding: EncodingKey::from_secret(secret),
            decoding: DecodingKey::from_secret(secret),
        }
    }
}
\end{lstlisting}

E proprio in questo "errore" nel secret che andremo ad attaccare. Useremo un attacco di bruteforcing all'header Authorization per far sì di avere i dati dell'utente con ID che noi vogliamo.

\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 \cite{JWT_SECRETS_LIST:1} 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.eyJ1c2VyX2lkIjoxMywiZXhwIjoxNjY2Mjk0Nzk2fQ.ay_RPoeTuV4e
lBFqqCdTzF64GPcoEDOlJN2DUAOqwds:hello

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

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

\section{App mobile}
Il codice dell'app è presente al link \underline{\url{https://git.dcariotti.me/m6-ie/tree/app}}.
È una "banale" applicazione scritta usando Ionic \cite{IONIC} con 3 pagine:
\begin{enumerate}
    \item Home: ricorda cosa serve fare, ovvero il login;
    \item Sign in: permette di fare il login mediante username e password;
    \item Me: visualizza le informazioni dell'utente loggato.
\end{enumerate}

Sapendo ciò dovremo esaminare il file APK dell'applicazione per vedere come si comporta realmente.\\\\
Dentro il codice sorgente è presente il codice in JavaScript, ma a noi serve usarlo nel nostro dispositivo Android. Quindi, come faremo realmente sviluppando un'app Ionic, lo andremo a compilare e visualizzare l'APK dentro Android Studio \cite{ANDROIDSTUDIO}.
Questo passaggio lo ricreiamo per ricondurre a tutti i passaggi.


\begin{lstlisting}
$ git clone https://git.dcariotti.me/m6-ie
$ cd m6-ie/app
$ npm i
$ ionic capacitor add android
$ vi .env # Chi fa la build conosce effettivamente l'URL dell'API
$ npm run build --production
$ npx cap copy android
$ npx cap sync android
$ cd android
$ export ANDROID_SDK_ROOT="<path all'sdk>"
$ ./gradlew assembleDebug
\end{lstlisting}

L'ultimo comando creerà un APK valido dentro \emph{m6-ie/app/build/outputs/apk/debug/app-debug.apk}.
Da non confondere quindi con la creazione di pacchetti AAB\cite{APKVSAAB:1} per le release.

\subsection{Configurazione Android Studio}
Scaricato e installato il pacchetto dal link ufficiale bisognerà inoltre installare anche l'SDK e un Device, ovvero un emulatore di un dispositivo Android.

\begin{figure}[h]
\centering
\includegraphics[width=0.2\textwidth]{data/android-studio-screenshot}
\caption{Screenshot di Android Studio in Ubuntu focal}
\end{figure}


Nell'esempio qui di seguito io userò SDK 30 su un Pixel 4.\\
\\
Procediamo quindi al profiling dell'applicazione come da \textbf{Figure 3.1}.


Avviando l'emulatore attraverso \emph{Shift+F10} vedremo l'applicazione dentro il device Android, Google Pixel in questo caso.

\begin{figure}[h]
\centering
\includegraphics[width=0.2\textwidth]{data/pixel-device}
\caption{Screenshot dell'emulatore}
\end{figure}

Se provassimo ad intercettare il traffico usando il \emph{Network profiler} integrato non vedremmo nulla perché non vengono esaminate le richieste HTTP fatte in maniera ibrida, quindi useremo Wireshark per monitorare le richieste al server.