Auteur : enze
Éditeur : Liz
Introduction
Récemment, la bibliothèque de cryptographie elliptic largement utilisée dans l'écosystème JavaScript a été trouvée avec une grave vulnérabilité de sécurité (GHSA-vjh7-7g9h-fjfh). L'attaquant peut extraire la clé privée en construisant des entrées spécifiques, obtenant ainsi un contrôle total sur les actifs numériques ou les identifiants de la victime avec une seule signature.
La cause profonde de cette vulnérabilité réside dans le défaut de traitement des entrées non standard de la bibliothèque elliptic, entraînant une possible répétition du nombre aléatoire k dans les signatures ECDSA. Étant donné que la sécurité de l'algorithme ECDSA dépend fortement de l'unicité de k, une fois k répété, la clé privée peut être directement déduite, causant un risque de sécurité irréversible.
Cet article analysera le principe de la vulnérabilité, ses causes et ses impacts, et fournira des recommandations de correction pour aider les développeurs à réduire les risques de sécurité.
Contexte de la vulnérabilité
Présentation de la bibliothèque elliptic
Elliptic est une bibliothèque de cryptographie à courbe elliptique largement utilisée dans l'écosystème JavaScript, prenant en charge plusieurs courbes (comme secp256k1, ed25519). Cette bibliothèque est utilisée par de nombreux portefeuilles de cryptomonnaie, systèmes d'authentification, applications Web3, etc.
Portée de l'impact
Versions affectées : elliptic <= 6.6.0
Courbes affectées : secp256k1, ed25519, etc.
Scénarios d'impact : toutes les applications acceptant des entrées externes pour signer des ECDSA (en particulier les systèmes qui acceptent des entrées externes pour signer)
Si l'application dépend de elliptic pour les signatures ECDSA et permet aux utilisateurs de fournir des messages non filtrés comme contenu de signature, il est très probable qu'elle soit affectée par cette vulnérabilité.
Principe de la vulnérabilité
Nombre aléatoire k dans les signatures ECDSA
Imaginez que vous avez un sceau unique (clé privée), chaque fois que vous signez un document, vous devez utiliser une sorte d'encre unique (nombre aléatoire k). L'empreinte produite génère deux chiffres r et s, qui constituent le résultat de la signature.
Ce processus comporte plusieurs points clés :
Le sceau (clé privée) reste toujours le même - représentant votre identité.
Chaque signature doit utiliser un sceau complètement nouveau (le nombre aléatoire k ne doit jamais être répété).
Même si vous signez le même document, le résultat de la signature (r, s) doit également être différent.
Tout le monde peut vérifier si l'empreinte a été scellée par votre sceau (la clé publique vérifie la signature).
Mais personne ne peut déduire votre sceau (clé privée) à partir de l'empreinte, tant que k est sécurisé.
Pour garantir la sécurité de k, ECDSA utilise la norme RFC 6979, qui utilise un générateur de nombres aléatoires déterministe (HMAC_DRBG ou HKDF) pour calculer k. Lors de la génération de k, la clé privée d et le message m sont utilisés comme graine, garantissant que k reste fixe pour les mêmes entrées. Le processus de calcul est le suivant :

Points clés :
1. k doit être aléatoire et unique, sinon cela entraînera une fuite de la clé privée.
2. Si k est le même lors de deux signatures, l'attaquant peut simplement récupérer directement la clé privée d à partir des deux signatures (r, s1) et (r, s2).
Démonstration mathématique :
Supposons qu'un utilisateur ait signé deux messages différents m1 et m2, mais que le nombre aléatoire k soit accidentellement le même, alors le résultat de la signature est le suivant :

L'attaquant peut calculer la différence entre s1 et s2 :

Puisque m1, m2, s1 et s2 sont tous connus, l'attaquant peut résoudre k :

Ensuite, substituez k dans l'équation de signature originale pour résoudre la clé privée d :

Analyse de la vulnérabilité
La bibliothèque elliptic devrait utiliser HMAC_DRBG (générateur de nombres aléatoires déterministe) pour garantir que k est unique pour chaque signature. Cependant, en raison d'une erreur de traitement des entrées, différentes messages peuvent dans certains cas générer le même k, entraînant une fuite de la clé privée.
Code de génération de k
Dans la méthode sign() de la bibliothèque elliptic, k est généré par HMAC_DRBG, le code associé est le suivant :
elliptic/lib/elliptic/ec/index.js

Les paramètres d'entrée de HMAC_DRBG comprennent principalement :
entropie → ici, la clé privée bkey est utilisée, garantissant que HMAC_DRBG dispose d'une randomité suffisante.
nonce (graine aléatoire) → calculé à partir de msg, influençant la valeur finale de k générée.
Puisque HMAC_DRBG est déterministe, la même entropie et le même nonce généreront le même k. Si le nonce est le même lors de différentes signatures, alors k sera également le même, ce qui entraînera une fuite de la clé privée.
Causes de la vulnérabilité : conversion BN entraînant la réutilisation de nonce
Dans l'implémentation de elliptic, msg est converti en BN (Big Number), puis le nonce est calculé par msg.toArray() :

Points problématiques :
msg est une instance BN, mais le nonce est un tableau.
Différentes instances BN peuvent générer le même tableau après conversion.
Cela signifie qu'il est possible de générer le même nonce pour différents messages, ce qui entraîne finalement la réutilisation de k.
Si k est réutilisé sur deux messages différents msg1 et msg2, la clé privée d peut être récupérée par la formule mathématique mentionnée ci-dessus.
L'attaquant peut construire un msg spécial pour que son nonce soit identique, entraînant ainsi la réutilisation de k. Il suffit d'obtenir une paire (r, s1) et (r, s2) pour récupérer la clé privée d par des calculs mathématiques. Pire encore, l'attaquant peut construire un tel msg malveillant pour n'importe quelle paire de messages/signatures connue, ce qui signifie qu'il peut complètement récupérer la clé privée et falsifier n'importe quelle signature en incitant la victime à signer une fois un message malveillant.
Exploitation de la vulnérabilité (PoC)
La bibliothèque elliptic permet d'utiliser des chaînes hexadécimales comme l'un des types d'entrée, lors de la signature, le msg est converti en instance BN, puis converti en type tableau. Si deux messages différents génèrent le même tableau après conversion, le nonce sera également identique, conduisant finalement à la réutilisation de k.
Exemple de code :

Sortie d'exécution :

msg1 = "message" et msg2 = "-message" après conversion BN ont généré le même nonce (la conception de elliptic accepte les chaînes hexadécimales comme un des types d'entrée possibles). Comme le nonce est identique, cela entraîne également la génération du même k par HMAC_DRBG. On peut voir d'après les résultats de la signature que la valeur de r est totalement identique, ce qui est la manifestation directe de la réutilisation de k.
Processus d'attaque complet :
1. Simuler la signature d'un message normal par la victime, obtenir le message msg0 et la signature sig0.
2. L'attaquant construit un message malveillant msg1 et incite la victime à signer, obtenant sig1.
3. Exploiter la vulnérabilité de réutilisation de k pour calculer k à partir de (r, s1) et (r, s2).
4. Utiliser k pour calculer davantage la clé privée d, succès de l'attaque.
Exemple de code :

Sortie d'exécution :

Recommandations de correction
1. Mettre à jour elliptic à 6.6.1+, le problème a été corrigé par l'équipe officielle.
2. Éviter de signer directement des messages non vérifiés, s'assurer que msg a été normalisé.
3. Remplacer la clé privée potentiellement affectée.
Résumé
Cette vulnérabilité de elliptic expose l'extrême sensibilité des signatures ECDSA au nombre aléatoire k. Une fois que k est réutilisé, la clé privée peut être récupérée directement, permettant à l'attaquant de contrôler complètement les actifs et l'identité de l'utilisateur. Les développeurs doivent réparer la vulnérabilité en temps opportun et réglementer strictement le traitement des entrées pour garantir la sécurité des signatures ECDSA. Enfin, merci au portefeuille Rabby pour les informations sur la vulnérabilité.
Références
[1] https://github.com/indutny/elliptic/security/advisories/GHSA-vjh7-7g9h-fjfh
[2] https://paulmillr.com/posts/deterministic-signatures
