Le 4 août 2026, des attaquants ont compromis le compte GitHub du mainteneur responsable de keyv, une bibliothèque de stockage clé-valeur enregistrant environ 127 millions de téléchargements npm hebdomadaires, et a profité de cet accès pour injecter un ver de vol d'identifiants dans toute la famille de paquets. Le même mainteneur possède cacheable (29 millions de téléchargements/mois), flat-cache (565 millions de téléchargements/mois), file-entry-cache (557 millions de téléchargements/mois) et plusieurs autres utilitaires de mise en cache très utilisés, qui ont tous été touchés par la même attaque.
La compromission a été menée en poussant des fichiers malveillants directement vers principal branche, puis en publiant immédiatement une nouvelle version, ce qui signifie que les versions empoisonnées ont été déployées sur npm avec une traçabilité valide signée par GitHub Actions.
Les paquets compromis incluent :
keyv6.0.0 (604M/mois)flat-cache6.1.24 (580M/mois)file-entry-cache11.1.6 (571M/mois)cacheable-request13.0.20 (137M/mois)cacheable2.5.1 (30M/mois)@cacheable/memory2.2.1 (28M/mois)cache-manager7.2.10 (16M/mois)@cacheable/node-cache3.1.2 (6M/mois)@cacheable/utils2.5.1 (34M/mois)@cacheable/net2.1.1 (3,7 k/mois)ecto5.0.1 (4,5 k/mois)
Nous constatons également une propagation communautaire très active de ce ver de la chaîne d'approvisionnement vers d'autres mainteneurs et paquets, touchant notamment des organisations majeures :
@deliveroo/reevent1.0.1@or-sdk/invitations1.4.9@picsart/ai-sdk3.32.2@qlik/embed-runtime1.6.4picasso.js2.11.6
Mise à jour — 5 août 2026, 13:15 CEST : Au moins 444 paquets (répartis sur 1 381 versions) ont été compromis par le ver, représentant un total cumulé de plus de 2 milliards de téléchargements mensuels au moment de la rédaction.
Ce qui s'est passé
Chaque paquet de la famille a reçu deux nouveaux fichiers, setup.mjs et Math_Symbol.js, ainsi qu'une "preinstall": "node setup.mjs" entrée ajoutée à chaque package.json. Quiconque a exécuté npm install contre une version vulnérable aurait eu setup.mjs s'exécuter automatiquement avant la fin de leur installation.
setup.mjs est un dropper lourdement obscurci. Son unique rôle est de télécharger silencieusement le runtime JavaScript Bun depuis github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/ et de l'utiliser pour exécuter la véritable charge utile, Math_Symbol.js:
execFileSync(<bun binary>, ['<script_dir>/Math_Symbol.js'], {
stdio: 'inherit',
cwd: <script_dir>
})Le Math_Symbol.js est un fichier JavaScript de 728 Ko fortement obfusqué contenant des logiciels malveillants de type stealers, conçus pour récolter des secrets dans l'environnement de la victime, chiffrer les données collectées et les exfiltrer vers un dépôt GitHub public dont la description indique : "Shai-Hulud : c'est reparti". La charge utile intègre également une fonctionnalité de propagation de type ver afin d'infecter les paquets d'autres mainteneurs ayant installé l'un des paquets compromis.
Ce qu'il dérobe
Le Math_Symbol.js Le fichier implémente un ensemble d'extracteurs d'identifiants, chacun ciblant un magasin de secrets différent sur la machine de la victime.
Tokens npm
Lit ~\/.npmrc et analyse le système de fichiers à la recherche d'autres .npmrc fichiers. Extrait les authToken valeurs ainsi que toutes les //registry.*:_authToken=... entrées. Valide chaque jeton en direct auprès de registry.npmjs[.]org/-/whoami avant de procéder à l'exfiltration.
Tokens GitHub
Trois formats de jetons sont visés : les PAT classiques (ghp_...) et les jetons OAuth (gho_...), les jetons serveur à serveur GitHub App (ghs_...) et les jetons JWT OIDC. Les sources incluent ~\/.config\/gh\/hosts.yml, les variables d'environnement et une analyse du système de fichiers.
Sur les exécuteurs GitHub Actions, la charge utile exécute également une commande shell qui lit directement la mémoire du processus de l'exécuteur pour vider l'ensemble du stockage de secrets. Elle lit ACTIONS_ID_TOKEN_REQUEST_TOKEN et ACTIONS_ID_TOKEN_REQUEST_URL pour dérober les jetons OIDC utilisés pour la publication sur npm.
Identifiants AWS
~/.aws/credentialset~\/.aws\/config, analysant tous les profils nommésAWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, etAWS_SESSION_TOKENvariables d'environnement- Le service de métadonnées d'instance EC2 sur
169.254.169.254, essayant d'abord IMDSv2 avec un repli sur IMDSv1 - Le point de terminaison des métadonnées du conteneur ECS sur
169.254.170.2 - AWS Secrets Manager, en appelant
secretsmanager:ListSecretsdans plusieurs régions pour énumérer et exfiltrer tous les secrets qui y sont stockés
Les secrets Kubernetes
Lit le jeton de compte de service, le certificat CA et l'espace de noms depuis \/var\/run\/secrets\/kubernetes.io\/serviceaccount\/. Utilise le jeton de compte de service pour interroger directement l'API Kubernetes et récupérer tous les secrets de l'espace de noms. Cible également KUBECONFIG et ~/.kube/config.
Jetons HashiCorp Vault
Vérifie six sources par ordre de priorité : VAULT_TOKEN la variable d'environnement, ~/.vault-token, le chemin d'exécution des GitHub Actions /home/runner/.vault-token, plusieurs chemins de conteneurs connus, une connexion d'authentification Kubernetes utilisant le JWT du compte de service dérobé, ainsi que le point de terminaison d'authentification IAM AWS de Vault à l'aide de tous les identifiants AWS dérobés. Après avoir obtenu un jeton, il énumère tous les stockages KV via /v1/sys/mounts et lit chaque secret à partir des chemins KV v1 et v2.
Jetons Stripe et Slack
Analyse à la recherche de clés d'API Stripe (de test et de production, sk_ et pk_ préfixes) et de jetons Slack (xox[baprs]-...) dans tous les fichiers traités par le scanner de système de fichiers.
Analyse générique du système de fichiers
Un scanner compatible avec les plateformes (macOS vs Linux) exécute environ 200 motifs glob sur le système de fichiers, ciblant entre autres :
.env,.env.*, et.envrcfichiers- Fichiers de clés privées (
*.pem,*.key,*.p12,*.pfx,*.jks) - Clés et configuration SSH (
id_rsa,id_ed25519,.ssh/config) - Fichiers d'état Terraform et
.tfvars - Fichiers d'identifiants de registre Docker (
docker/config.json) - Bases de données KeePass (
*.kdbx) - Configurations VPN (
*.ovpn) - Fichiers de configuration IDE incluant
.vscode/tasks.jsonet.claude/settings.json
Les fichiers de plus de 5 Mo sont ignorés. Jusqu'à 64 lectures simultanées sont utilisées. Un moteur d'expressions régulières générique est également appliqué à tous les fichiers analysés, signalant les clés privées PEM, les clés publiques SSH, les clés de stockage Azure, les chaînes de connexion aux bases de données avec des identifiants intégrés et les clé=valeur motifs correspondant aux noms de champs de secrets courants.
Exfiltration
Une fois les identifiants récoltés, la charge utile chiffre l'ensemble du paquet avant de l'envoyer. Seul l'attaquant, qui détient la clé privée RSA correspondante, peut déchiffrer les données ainsi téléversées. Cela signifie que les données volées reposent en toute visibilité sur une infrastructure publique tout en restant illisibles pour quiconque.
La principale destination d'exfiltration est un dépôt GitHub public dont la description contient la chaîne « Shai-Hulud: Here We Go Again ». Au moment de la rédaction, GitHub recense environ 1 300 dépôts publics correspondant à cette chaîne, chacun servant de point de dépôt pour le lot d'identifiants chiffrés d'une victime.

Si le téléversement vers GitHub échoue, la charge utile se replie sur https://npm-cache[.]com:443/router, un domaine enregistré le 22/05/2026 qui semble ne servir à aucune fin légitime. Ce domaine est récupéré dynamiquement à partir d'un smart contract Ethereum situé à 0xE1f2395ee43e45A1556EC6438a88c31B83493103, permettant à l'attaquant de faire pivoter son infrastructure à tout moment sans modifier la charge utile.
Ver auto-réplicatif
Au-delà du vol d'identifiants, la charge utile les utilise activement pour propager le malware à d'autres mainteneurs et dépôts. Elle possède deux vecteurs d'infection distincts.
Infection par archive tarball npm
À l'aide du jeton npm dérobé, la charge utile appelle https://registry.npmjs[.]org/-/npm/v1/tokens pour lister l'ensemble des paquets auxquels ce token a le droit de publier, puis récupère et décompresse l'archive tarball actuelle pour chacun d'eux. Avant de republier, il apporte les modifications suivantes :
- Incrémente la version de correctif (patch) d'une unité (par exemple,
1.2.3devient1.2.4) - Ajoute
"preinstall": "node setup.mjs"aux scripts du paquet - Injecte
setup.mjsetmath_init.js(fonctionnellement identique àMath_Symbol.js) dans le paquet
Il réempaquète et publie ensuite l'archive modifiée sur le registre.
C'est ainsi que le ver se propage au-delà du mainteneur initial. Après la compromission initiale du keyv mainteneur, nous avons observé plus de 400 paquets infectés par contamination au sein de la communauté. Ces infections de deuxième génération sont identifiables par l'utilisation de math_init.js plutôt que Math_Symbol.js, puisqu'elles ont été amorcées par le ver d'injection de tarball npm.
Infection de dépôt GitHub
Lorsque la charge utile (payload) trouve un ghs_ token, elle effectue des commits sur toutes les branches accessibles, jusqu'à 50 branches par dépôt, en traitant d'abord les branches les plus récemment actives et en sautant dependabot et copilot les branches. Elle ajoute des hooks malveillants à .claude/settings.json et .vscode/tasks.json de sorte que la charge utile s'exécute automatiquement la prochaine fois qu'un développeur ouvre le dépôt dans VS Code ou lance une session Claude Code à l'intérieur, sans qu'aucun npm install ne soit requis. Les commits sont signés par claude avec l'adresse e-mail claude@users.noreply.github[.]com et portent le message chore: update config, se fondant ainsi parmi de vrais commits.
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 (IoC)
Fichiers
setup.mjs- SHA-256
54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668
- SHA-256
setup.mjs(version à propagation communautaire)- SHA-256
fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb
- SHA-256
Math_Symbol.jsetmath_init.js- SHA-256
9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc
- SHA-256
Réseau
https://npm-cache[.]com:443/router— point de terminaison d'exfiltrationeth-mainnet.nodereal[.]iorequête contenant0xE1f2395ee43e45A1556EC6438a88c31B83493103
Autre
- Tout dépôt GitHub public dont la description contient
"Shai-Hulud: Here We Go Again"— il s'agit des dépôts contrôlés par l'attaquant utilisés pour l'exfiltration d'identifiants.
