JWT Strange Alg


Alcune librerie nel 2015 permettevano una funzionalità particolare di JWT senza fare i dovuti controlli. I token JWT nel loro standard prevedono l'algoritmo "none", questo significa che il token deve essere considerato valido a prescindere (perchè già validato). Il token, quindi, viene accettato senza verificare la firma e viene elaborato il payload.

Questa modalità non avrebbe nessun problema, se non fosse che le librerie accettavano come impostazione di default il valore "none" nel campo "alg" dell'header dei token. Per via di queste configurazioni di default era quindi possibile forgiare dei token con un payload arbitrario, ed era semplicemente sufficiente omettere la firma.

Header: {"alg": "HS256","typ": "JWT"}
Payload: {"sub": "1234567890","name": "guest","iat": 1516239022}
Sign: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header: {"alg": "none","typ": "JWT"}
Payload: {"sub": "1234567890","name": "admin","iat": 1516239022}
Sign: 

jwt-none-1

RSA vs HMAC -> Key Confusion


Lo standard per i JWT prevede il supporto per algoritmi a chiave asimmetrica.
Con questi algoritmi, i token vengono firmati utilizzando una chiave privata, ma verificati utilizzando la chiave pubblica corrispondente. L'uso di algoritmi asimmetrici può risultare interessante in quanto, mantenendo la chiave privata per sé, è comunque possibile pubblicare quella pubblica per verificare che il token sia correttamente firmato.

La maggior parte delle librerie hanno una funzione simile a questa per verificare un token:

jwt.verify(token,verificationKey)

Nel caso si utilizzi un algoritmo asimmetrico (RSA) la chiave di verifica sarà la chiave pubblica, nel caso si in uso un algoritmo a chiave simmetrica (HMAC) allora sarà la stessa utilizzata per firmare il token.

Header: {"alg": "RS256","typ": "JWT"}
Payload: {"sub": "1234567890","name": "guest","iat": 1516239022}
Sign: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Come si può abusare di questo supporto per entrambi gli algoritmi? Se un server si aspetta un token firmato con RSA ma riceve un token firmato con HMAC, e come chiave per la firma viene usata quella pubblica, allora il server utilizzerà HMAC come algoritmo per verificare il token. Essendo che come parametro per "verificationKey" per la funzione "jwt.verify" viene utilizzata la chiave pubblica, risulterà che il token è correttamente firmato.

Header: {"alg": "HS256","typ": "JWT"}
Payload: {"sub": "1234567890","name": "admin","iat": 1516239022}
Sign: FFdrGxwH5YxeILO2QT4fwpMeJf36POk6yJVaadQssw6z


Con questa vulnerabilità è possibile quindi forgiare token arbitrari e creare una firma valida anche per il server, sfruttando la chiave pubblica e il fatto di avere il controllo sul campo "alg" dell'header.

jwt-rsavshmac-1

Fine Seconda Parte


Con questo articolo abbiamo affrontato le prime due vulnerabilità di questa serie sui token JWT. Queste vulnerabilità sono causate dal fatto che un utente, a causa di una mancanza di controlli sul campo "alg" dell'header, può controllare in modo arbitrario quale algoritmo deve essere utilizzato per verificare la validità del token!

Se vuoi mettere in pratica ciò che hai appreso in questo articolo, ti consiglio la sottoscrizione a Pentester Lab Pro per accedere a moltissimi esercizi pratici su decine di vulnerabilità.

Tools


JOSEPH - by Dennis Detering
JavaScript Object Signing and Encryption Pentesting Helper (Burp Store)