Attaquer les JWT

HTTP est un protocole web stateless ce qui implique que nativement il est impossible de mettre en place une session pour un utilisateur. Pour pallier ce problème de nombreux mécanismes ont été mis en place tels que les cookies de session ou encore les JWT. Au cours de cet article nous verrons ce qu’est un JWT et comment l’attaquer.

I/ La théorie

Un JWT est une une chaîne de caractères signée, encodée en base64 et stockée dans un cookie qui ressemble globalement à ça :

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Imd1ZXN0In0.OnuZnYMdetcg7AWGV6WURn8CFSfas6AQej4V9M13nsk

Comme on peut le voir notre chaîne de caractères est découpée en 3 parties délimitées par un point :

  • Un header qui indique le type d’objet que l’on manipule ainsi que l’algorithme de signature utilisé (par défaut c’est l’algorithme HS256) :

jwt1.png

  • Un payload dans lequel sont stockées les données :

jwt2.png

  • Une signature numérique qui permet de s’assurer de la validité des données contenues dans le JWT.

jwt3.png

Il existe différents algorithmes de signature :

Algorithm Digital Signature or MAC Algorithm Requirements
HS256 HMAC using SHA-256 Required
HS384 HMAC using SHA-384 Optional
HS512 HMAC using SHA-512 Optional
RS256 RSASSA-PKCS1-v1_5 using SHA-256 Recommended
RS384 RSASSA-PKCS1-v1_5 using SHA-384 Optional
RS512 RSASSA-PKCS1-v1_5 using SHA-512 Optional
ES256 ECDSA using P-256 and SHA-256 Recommended
ES384 ECDSA using P-384 and SHA-384 Optional
ES512 ECDSA using P-521 and SHA-512 Optional
PS256 RSASSA-PSS using SHA-256 and MGF1 with SHA-256 Optional
PS384 RSASSA-PSS using SHA-384 and MGF1 with SHA-384 Optional
PS512 RSASSA-PSS using SHA-512 and MGF1 with SHA-512 Optional
none No digital signature or MAC performed Required

Crédit : PayloadAllTheThings @Swissky

La création d’un JWT est assez simple et se déroule en six étapes :

  1. Encoder le header en base64
  2. Encoder le payload en base64
  3. Concaténer les deux résultats en les séparant par un point
  4. Obtenir la signature via un algorithme de signature
  5. Encoder la signature en base64
  6. Concaténer la signature à la précédente chaîne créée

Voilà vous avez votre JWT  !

II/ L’attaque

La première chose à faire lorsque l’on découvre l’utilisation des JWT c’est d’en inspecter le contenu puisqu’ils peuvent révéler des informations sensibles. Sur le token JWT analysé précédemment nous pouvons voir que le rôle de l’utilisateur est stocké dans le JWT. Qu’est ce qu’il se passerait si on modifiait la valeur « guest » en « admin » ? Selon l’application on pourrait peut être augmenter nos privilèges  !

Globalement on répertorie trois types d’attaques liées au JWT que nous allons traiter ci-dessous. Pour l’ensemble de ces attaques j’utiliserai un seul et même outil : jwt_tool écrit par ticarpi.

git clone https://github.com/ticarpi/jwt_tool.git
  • Modification de l’algorithme de hashage :

Comme nous l’avons vu plus tôt, les JWT sont signés. Pour des raisons obscures il est possible de ne pas signer ces JWT. Là où ça devient dangereux c’est lorsque les applications ne vérifient pas cette signature. Dans ces cas là nous serons en mesure d’en modifier le contenu pour y mettre absolument tout ce que nous voulons.

Dans le cas présent on pourrait modifier la valeur du champ username du JWT que nous avons disséqué plus tôt. Voici comment faire avec jwt_tool :

jwt4.png

Au final jwt_tool nous rend un JWT valide non signé. Notez que même si la signature n’est pas présente dans notre JWT final, ce dernier se termine quand même pas un point.

  • Bruteforce de clé secrète:

Certains algorithmes de hashage utilisent une clé secrète afin d’empêcher la modification des données contenues dans un token JWT. Cette clé n’est en fait rien d’autre qu’un mot de passe plus ou moins long/compliqué. Suivant la clé utilisé il sera possible de la retrouver en crackant le JWT via un dictionnaire tel que rockyou.txt.

Pour cela on pourra utiliser cette commande :

python3 jwt_tool.py <JSON Web Token> <dictionnaire>

jwt5.png

Sur le screen ci dessus nous pouvons voir que la clé a bien été cassée. Nous allons donc pouvoir modifier le contenu de notre JWT et le signer sans que celui-ci soit invalide. Encore une fois on pourra forger notre nouveau JWT valide via jwt_tool.

  • Changer l’algorithme RS256 en HS256 :

L’algorithme RS256 utilise une clé privée pour signer les messages et une clé publique pour la vérifier (eh oui, RS256 repose sur l’algorithme RSA). A l’inverse l’algorithme HS256 utilise une clé secrète afin de signer ses messages.

Si une application vous renvoie un JWT signé via RS256 alors vous devriez aussi obtenir une clé publique qui vous permettra de vérifier la signature. Or si on change l’algorithme RS256 en HS256 la signature est vérifiée à l’aide d’une clé secrète qui est en fait la clé publique (que tout le monde possède donc). Ainsi on va pouvoir signer correctement nos JWT. Pour cela, on utilisera jwt_tool de cette manière :

jwt6.png

Au vu des attaques évoquées il devient du coup assez intéressant de passer quelques minutes sur un token JWT si vous en trouvez lors de vos audits  !