hacktricks/pentesting-web/hacking-jwt-json-web-tokens.md

19 KiB
Raw Blame History

Vulnérabilités JWT (Json Web Tokens)

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert de l'équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks :

Si vous êtes intéressé par une carrière en piratage et souhaitez pirater l'impiratable - nous recrutons ! (maîtrise du polonais écrit et parlé requise).

{% embed url="https://www.stmcyber.com/careers" %}

Une partie de ce post est basée sur le superbe post : https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Auteur de l'outil génial pour le pentest des JWTs https://github.com/ticarpi/jwt_tool

Gains Rapides

Exécutez jwt_tool en mode Tous les 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ù l'application web ne vérifie pas correctement le JWT :

Ensuite, vous pouvez rechercher la requête dans votre proxy ou extraire le JWT utilisé pour cette requête en utilisant l'outil jwt_ :

python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"

Manipuler les données sans rien modifier

Vous pouvez simplement manipuler les données en laissant la signature telle quelle et vérifier si le serveur vérifie la signature. Essayez de changer votre nom d'utilisateur en "admin", par exemple.

Le jeton est-il vérifié?

Pour vérifier si la signature d'un JWT est vérifiée :

  • Un message d'erreur suggère une vérification en cours ; les détails sensibles dans les erreurs verbeuses doivent être examinés.
  • Un changement dans la page renvoyée indique une vérification.
  • Aucun changement ne suggère aucune vérification ; c'est le moment d'expérimenter en altérant les revendications de la charge utile.

Origine

Il est important de déterminer si le jeton a été généré côté serveur ou côté client en examinant l'historique des requêtes du proxy.

  • Les jetons d'abord vus côté client suggèrent que la clé pourrait être exposée au code côté client, nécessitant une enquête plus approfondie.
  • Les jetons d'origine côté serveur indiquent un processus sécurisé.

Durée

Vérifiez si le jeton dure plus de 24h... peut-être n'expire-t-il jamais. S'il y a un champ "exp", vérifiez si le serveur le gère correctement.

Brute-force du secret HMAC

Voir cette page.

Modifier l'algorithme en None (CVE-2015-9235)

Définissez l'algorithme utilisé comme "None" et supprimez la partie signature.

Utilisez l'extension Burp appelée "JSON Web Token" pour tester cette vulnérabilité et modifier différentes valeurs à l'intérieur du JWT (envoyez la requête à Repeater et dans l'onglet "JSON Web Token", vous pouvez modifier les valeurs du jeton. Vous pouvez également choisir de mettre la valeur du champ "Alg" à "None").

Changer l'algorithme de RS256(asymétrique) à HS256(symétrique) (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 côté serveur 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

Nouvelle clé publique dans l'en-tête

Un attaquant intègre une nouvelle clé dans l'en-tête du jeton 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 au Repeater, dans l'onglet JSON Web Token sélectionnez "CVE-2018-0114" et envoyez la requête).

Contrefaçon de JWKS

Les instructions détaillent une méthode pour évaluer la sécurité des jetons JWT, en particulier ceux utilisant une revendication d'en-tête "jku". Cette revendication devrait être liée à un fichier JWKS (Jeu de clés Web JSON) contenant la clé publique nécessaire à la vérification du jeton.

  • Évaluation des jetons avec l'en-tête "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 jeton pour diriger vers un service web contrôlé, permettant l'observation du trafic.
  • Surveillance de l'interaction HTTP:
  • Observer les requêtes HTTP vers l'URL spécifiée indique les tentatives du serveur pour récupérer les clés à partir du lien fourni.
  • Lors de l'utilisation de jwt_tool pour ce processus, il est crucial de mettre à jour le fichier jwtconf.ini avec l'emplacement de votre JWKS personnel pour 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

Aperçu des problèmes de Kid

Une revendication d'en-tête facultative 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 jeton. Cette revendication aide à sélectionner la clé appropriée pour vérifier la signature d'un jeton.

Révélation de la clé via "kid"

Lorsque la revendication kid est présente dans l'en-tête, il est conseillé de rechercher le répertoire web pour le fichier correspondant ou ses variations. Par exemple, si "kid":"clé/12345" est spécifié, les fichiers /clé/12345 et /clé/12345.pem devraient être recherchés dans la racine du site web.

Traversée de chemin avec "kid"

La revendication kid peut également être exploitée pour naviguer dans le système de fichiers, permettant potentiellement la sélection d'un fichier arbitraire. Il est possible de tester la connectivité ou d'exécuter des attaques de falsification de requête côté serveur (SSRF) en modifiant la valeur kid pour cibler des fichiers ou des services spécifiques. La manipulation du JWT pour changer la valeur kid tout en conservant la signature d'origine peut être réalisée en utilisant le drapeau -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 falsifier un JWT valide. Par exemple, le fichier /proc/sys/kernel/randomize_va_space dans 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 de JWT.

Injection SQL via "kid"

Si le contenu de la revendication kid est utilisé pour récupérer un mot de passe depuis une base de données, une injection SQL pourrait être facilitée en modifiant la charge utile de kid. Un exemple de charge utile qui utilise une injection SQL pour modifier 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 JWT.

Injection OS via "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 entraîner des vulnérabilités d'Exécution de Code à Distance (RCE). En injectant des commandes dans le paramètre kid, il est possible d'exposer des clés privées. Un exemple de charge utile pour atteindre une RCE et l'exposition de clés est :

/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&

x5u et jku

jku

jku signifie URL de jeu de clés JWK.
Si le jeton utilise une revendication d'en-tête "jku", vérifiez l'URL fournie. Cela devrait pointer vers une URL contenant le fichier JWKS qui détient la clé publique pour vérifier le jeton. Modifiez le jeton pour pointer la valeur jku vers un service Web dont vous pouvez surveiller le trafic.

Tout d'abord, vous devez créer un nouveau certificat avec de nouvelles clés privées et publiques.

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

Ensuite, vous pouvez utiliser par exemple jwt.io pour créer le nouveau JWT avec les clés publiques et privées créées et en pointant le paramètre jku vers le certificat créé. Pour créer un certificat jku valide, vous pouvez télécharger l'original et modifier 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))

x5u

X.509 URL. Un URI pointant vers un ensemble de certificats publics X.509 (un standard de format de certificat) encodés sous forme 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 la RFC 52807. Une sécurité de transport est requise pour transférer les certificats.

Essayez de modifier cet en-tête pour qu'il pointe vers une URL sous votre contrôle et vérifiez si une requête est reçue. Dans ce cas, vous pourriez altérer le JWT.

Pour falsifier un nouveau jeton en utilisant 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

Ensuite, vous pouvez utiliser par exemple jwt.io pour créer le nouveau JWT avec les clés publiques et privées créées et en pointant le paramètre x5u vers le certificat .crt créé.

Vous pouvez également abuser de ces deux vulnérabilités pour les SSRF.

x5c

Ce paramètre peut contenir le certificat en base64 :

Si l'attaquant génère un certificat auto-signé et crée un jeton falsifié en utilisant la clé privée correspondante et remplace la valeur du paramètre "x5c" par le certificat nouvellement généré et modifie les autres paramètres, à savoir n, e et x5t, alors essentiellement le jeton falsifié serait accepté par le serveur.

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 contient une clé publique intégrée comme dans le scénario suivant :

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'incorporer la nouvelle clé publique à l'intérieur du jeton 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 les "n" et "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));

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 JWT, la clé privée peut être restaurée.

Voici un exemple: ECDSA: Révéler la clé privée, si le même nonce est utilisé (avec SECP256k1)

JTI (JWT ID)

La revendication JTI (JWT ID) fournit un identifiant unique pour un jeton JWT. Il peut être utilisé pour empêcher le jeton d'être rejoué.
Cependant, imaginez une situation où la longueur maximale de l'ID est de 4 (0001-9999). Les requêtes 0001 et 10001 vont utiliser le même ID. Donc, si le backend incrémente l'ID à chaque requête, vous pourriez abuser de cela pour rejouer une requête (nécessitant d'envoyer 10000 requêtes entre chaque rejou réussi).

Réclamations enregistrées JWT

{% embed url="https://www.iana.org/assignments/jwt/jwt.xhtml#claims" %}

Autres attaques

Attaques de relais entre services

Il a été observé que certaines applications web s'appuient sur un service JWT de confiance pour la génération et la gestion de leurs jetons. Des cas ont été enregistrés où un jeton, généré pour un client par le service JWT, a été 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 s'inscrire à un compte sur un autre client de ce service en utilisant le même nom d'utilisateur/email devrait être investiguée. Une tentative devrait ensuite être faite pour rejouer le jeton obtenu dans une requête vers la cible pour voir s'il est accepté.

  • Un problème critique peut être indiqué par l'acceptation de votre jeton, permettant potentiellement le spoofing du compte de n'importe quel utilisateur. Cependant, il convient de noter qu'une autorisation pour des tests plus larges pourrait être requise si vous vous inscrivez sur une application tierce, car cela pourrait entrer dans une zone grise légale.

Vérification de l'expiration des jetons

L'expiration du jeton est vérifiée en utilisant la revendication Payload "exp". Étant donné que les JWT sont souvent utilisés sans information de session, une manipulation prudente est requise. Dans de nombreux cas, capturer et rejouer le JWT d'un autre utilisateur pourrait permettre l'usurpation de cet utilisateur. La RFC JWT recommande de mitiger les attaques de rejouement de JWT en utilisant la revendication "exp" pour définir un temps d'expiration pour le jeton. De plus, la mise en œuvre de vérifications pertinentes par l'application pour garantir le traitement de cette valeur et le rejet des jetons expirés est crucial. Si le jeton inclut une revendication "exp" et que les limites de temps de test le permettent, il est conseillé de stocker le jeton et de le rejouer après que le temps d'expiration soit écoulé. Le contenu du jeton, y compris l'analyse de l'horodatage et la vérification de l'expiration (horodatage en UTC), peut être lu en utilisant le drapeau -R de l'outil jwt_tool.

  • Un risque de sécurité peut être présent si l'application valide toujours le jeton, car cela pourrait impliquer que le jeton ne pourrait jamais expirer.

Outils

{% embed url="https://github.com/ticarpi/jwt_tool" %}

Si vous êtes intéressé par une carrière en piratage et pirater l'impossible - nous recrutons ! (maîtrise du polonais écrit et parlé requis).

{% embed url="https://www.stmcyber.com/careers" %}

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres façons de soutenir HackTricks: