Aikido

Keyv et ses amis ont été victimes d'une attaque active de type « Shai-Hulud » visant la chaîne d'approvisionnement

Écrit par
Ilyas Makari

Le 4 août 2026, des pirates ont piraté le compte GitHub du responsable de la maintenance de keyv, une bibliothèque de stockage clé-valeur enregistrant environ 127 millions de téléchargements hebdomadaires sur npm, et a profité de cet accès pour injecter un ver destiné à voler des identifiants dans l'ensemble de la famille de paquets. Ce même responsable est également propriétaire de mémorisable en cache (29 millions de téléchargements par mois), cache plat (565 millions de téléchargements par mois), cache-d'entrée-de-fichier (557 millions de téléchargements par mois), ainsi que plusieurs autres utilitaires de mise en cache très répandus, qui ont tous été touchés par cette même attaque.

L'attaque a été menée en injectant directement des fichiers malveillants dans le principal branch, puis en créant immédiatement une nouvelle version, ce qui signifie que les versions corrompues ont été publiées sur npm avec une provenance valide signée par GitHub Actions.

Les paquets compromis sont les suivants :

  • keyv 6,0,0 (604 M/mois)
  • cache plat 6,1,24 (580 millions par mois)
  • cache-d'entrée-de-fichier 11,1,6 (571 millions par mois)
  • requête pouvant être mise en cache 13/01/20 (137 millions par mois)
  • mémorisable en cache 2,5,1 (30 millions par mois)
  • @cacheable/mémoire 2,2,1 (28 millions par mois)
  • gestionnaire de cache 7,2,10 (16 millions par mois)
  • @cacheable/node-cache 3,1,2 (6 millions par mois)
  • @cacheable/utils 2,5,1 (34 millions par mois)
  • @cacheable/net 2.1.1 (3,7 K/mois)
  • ecto 5,0,1 (4,5 K/mois)

Nous constatons également une propagation très active de ce ver de chaîne logistique au sein de la communauté, qui touche d'autres responsables de maintenance et paquets, y compris des organisations majeures :

  • @deliveroo/reevent 1.0.1
  • @or-sdk/invitations 1.4.9
  • @picsart/ai-sdk 3.32.2
  • @qlik/embed-runtime 1.6.4
  • picasso.js 2.11.6

Mise à jour — 4 août 2026, 13 h 37 CEST : Au moins 434 paquets (répartis sur 1 381 versions) ont été compromis par le ver, avec un total cumulé de plus de 2 milliards d'installations mensuelles à la date de rédaction de cet article.

Ce qui s'est passé

Chaque paquet de la famille a reçu deux nouveaux fichiers, setup.mjs et Math_Symbol.js, ainsi qu'un « preinstall » : « node setup.mjs » entrée ajoutée à chacun package.json. Quiconque a participé à npm install contre une version concernée aurait eu setup.mjs s'exécuter automatiquement avant la fin de leur installation.

setup.mjs Il s'agit d'un dropper fortement obscurci. Sa seule fonction consiste à télécharger discrètement le moteur d'exécution JavaScript Bun depuis github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/ et à l'utiliser pour exécuter la charge utile réelle, Math_Symbol.js:

execFileSync(<bun binary>, ['<script_dir>/Math_Symbol.js'], {
  stdio: 'inherit',
  cwd: <script_dir>
})

Le Math_Symbol.js Il s'agit d'un fichier JavaScript de 728 Ko fortement obfusqué, contenant des programmes de vol d'identifiants qui récupèrent des informations confidentielles dans l'environnement de la victime, chiffrent les données ainsi obtenues et les exfiltrent vers un dépôt GitHub public dont la description indique «Shai-Hulud : Et c'est reparti !». La charge utile contient également un mécanisme de propagation de type « ver » permettant d'infecter les paquets d'autres responsables ayant installé l'un des paquets compromis.

Ce qu'il dérobe

Le Math_Symbol.js Ce fichier met en œuvre 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 de tout autre .npmrc fichiers. Extraits authToken valeurs et tout //registry.*:_authToken=... entrées. Vérifie en temps réel la validité de chaque jeton par rapport à 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_...), jetons serveur à serveur de l'application GitHub (ghs_...), ainsi que les jetons JWT OIDC. Parmi les sources, on peut citer ~/.config/gh/hosts.yml, les variables d'environnement et une analyse du système de fichiers.

Sur les runners GitHub Actions, la charge utile exécute également une commande shell qui lit directement la mémoire du processus du runner afin d'extraire l'intégralité du magasin de secrets. Elle lit ACTIONS_ID_TOKEN_REQUEST_TOKEN et ACTIONS_ID_TOKEN_REQUEST_URL pour voler les jetons OIDC utilisés pour la publication sur npm.

Identifiants AWS

  • ~/.aws/credentials et ~/.aws/config, en analysant tous les profils nommés
  • AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, et AWS_SESSION_TOKEN variables d'environnement
  • Service de métadonnées des instances EC2 à l'adresse 169.254.169.254, en essayant d'abord IMDSv2, avec IMDSv1 comme solution de secours
  • Point de terminaison des métadonnées des conteneurs ECS à l'adresse 169.254.170.2
  • AWS Secrets Manager, appelant secretsmanager : ListSecrets dans plusieurs régions afin de recenser et d'extraire toutes les informations confidentielles qui y sont stockées

Secrets Kubernetes

Lit le jeton du compte de service, le certificat CA et l'espace de noms à partir de /var/run/secrets/kubernetes.io/serviceaccount/. Utilise le jeton du compte de service pour interroger directement l'API Kubernetes et récupérer tous les secrets du namespace. Cible également KUBECONFIG et ~/.kube/config.

Jetons HashiCorp Vault

Vérifie six sources par ordre de priorité : le VAULT_TOKEN variable d'environnement, ~/.vault-token, le chemin d'accès au runner GitHub Actions /home/runner/.vault-token, plusieurs chemins d'accès à des conteneurs bien connus, une connexion Kubernetes via le JWT du compte de service piraté, ainsi que le point de terminaison d'authentification AWS IAM de Vault à l'aide de n'importe quels identifiants AWS piratés. Après avoir obtenu un jeton, il répertorie tous les magasins clé-valeur via /v1/sys/mounts et lit tous les secrets des chemins KV v1 et v2.

Jetons Stripe et Slack

Recherche les clés API Stripe (de test et de production, sk_ et pk_ préfixes) et jetons Slack (xox[baprs]-...) dans tous les fichiers analysés par le scanner du système de fichiers.

Analyse générale du système de fichiers

Un scanner adapté à la plate-forme (macOS ou Linux) analyse le système de fichiers à l'aide d'environ 200 modèles « glob », en ciblant notamment :

  • .env, .env.*, et .envrc fichiers
  • Fichiers de clés privées (*.pem, *.key, *.p12, *.pfx, *.jks)
  • Clés SSH et configuration (id_rsa, id_ed25519, .ssh/config)
  • Les fichiers d'état Terraform et .tfvars
  • Fichiers d'identifiants du registre Docker (docker/config.json)
  • Bases de données KeePass (*.kdbx)
  • Configurations VPN (*.ovpn)
  • Fichiers de configuration de l'IDE, notamment .vscode/tasks.json et .claude/settings.json

Les fichiers de plus de 5 Mo sont ignorés. Jusqu’à 64 lectures simultanées sont effectuées. Un moteur d’expressions régulières générique est également appliqué à l’ensemble des fichiers analysés, afin de signaler 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 contenant des identifiants intégrés, ainsi que les éléments génériques clé=valeur modèles correspondant à des noms de champs secrets courants.

Exfiltration

Une fois les identifiants récupérés, la charge utile chiffre l'ensemble du paquet avant de l'envoyer où que ce soit. Seul le pirate, qui détient la clé privée correspondante de l'RSA , peut déchiffrer les données transférées. Cela signifie que les données volées se trouvent à la vue de tous sur une infrastructure publique, mais qu'elles sont illisibles pour toute autre personne.

La principale destination de l'exfiltration est un dépôt GitHub public dont la description contient la chaîne de caractères «Shai-Hulud : Here We Go Again ». À l'heure où nous écrivons ces lignes, GitHub compte environ 1 300 dépôts publics correspondant à cette chaîne de caractères, chacun servant de point de dépôt pour le paquet d'identifiants chiffrés d'une victime.

Si le téléchargement sur GitHub échoue, la charge utile revient alors à https://npm-cache[.]com:443/router, un nom de domaine enregistré le 22 mai 2026 qui ne semble avoir aucune finalité légitime. Ce nom de domaine est récupéré de manière dynamique à partir d'un contrat intelligent Ethereum à l'adresse 0xE1f2395ee43e45A1556EC6438a88c31B83493103, ce qui permet à l'attaquant de modifier l'infrastructure à tout moment sans toucher à la charge utile.

Ver informatique capable de s'autoreproduire

Au-delà du vol d'identifiants, la charge utile les utilise activement pour propager le logiciel malveillant à d'autres responsables de maintenance et à d'autres dépôts. Elle dispose de deux vecteurs d'infection distincts.

Infection d'un fichier tarball npm

À l'aide du jeton npm volé, la charge utile appelle https://registry.npmjs[.]org/-/npm/v1/tokens pour répertorier tous les paquets pour lesquels ce token dispose de droits de publication, puis récupère et décompresse l'archive tar correspondante pour chacun d'entre eux. Avant de les republier, il effectue les modifications suivantes :

  • Augmente la version du correctif d'une unité (par exemple : 1.2.3 devient 1.2.4)
  • Ajoute « preinstall » : « node setup.mjs » aux scripts du paquet
  • Injections setup.mjs et math_init.js (fonctionnellement identique à Math_Symbol.js) dans le colis

Il recompose ensuite l'archive tar modifiée et la publie dans le registre.

C'est ainsi que le ver se propage au-delà du responsable initial. Après la compromission initiale du keyv En tant que responsable de la maintenance, nous avons constaté que plus de 400 paquets avaient été infectés par propagation 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'ils ont été infectés par le ver « npm tarball injection ».

Infection d'un dépôt GitHub

Lorsque la charge utile détecte un ghs_ token, il effectue des commits dans toutes les branches auxquelles il peut accéder, jusqu’à 50 branches par dépôt, en traitant en priorité les branches les plus récemment actives et en ignorant dependabot et copilote branches. Il ajoute des hooks malveillants à .claude/settings.json et .vscode/tasks.json afin que le code s'exécute automatiquement la prochaine fois qu'un développeur ouvrira le dépôt dans VS Code ou lancera une session Claude Code depuis ce dernier, sans qu'il soit nécessaire d'effectuer une installation via npm. Les commits sont créés sous la forme claude par e-mail claude@users.noreply.github[.]com et transmettre le message tâche : mise à jour de la configuration, en se fondant parmi les véritables 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
  • setup.mjs (version « transmission communautaire »)
    • SHA-256 fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb
  • Math_Symbol.js et math_init.js
    • SHA-256 9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc

Réseau

  • https://npm-cache[.]com:443/router — point d'extraction
  • eth-mainnet.nodereal[.]io demande contenant 0xE1f2395ee43e45A1556EC6438a88c31B83493103

Autre

  • Tout dépôt GitHub public dont la description contient « Shai-Hulud : Et c'est reparti ! » — il s'agit des dépôts contrôlés par les attaquants et utilisés pour l'exfiltration d'identifiants.

Partager :

https://www.aikido.dev/blog/keyv-and-friends-compromised-in-npm-supply-chain-attack

Analyser les malwares

Commencer gratuitement
4,7/5
Fatigué des faux positifs ?
Essayez Aikido, comme 100 000 autres.
Commencez maintenant
Obtenez une démonstration personnalisée

Approuvé par plus de 100 000 équipes

Réserver maintenant
Analysez votre application à la recherche d'IDORs et de chemins d'attaque réels

Approuvé par plus de 100 000 équipes

Démarrer l'analyse
Découvrez comment le pentest IA teste votre application

Approuvé par plus de 100 000 équipes

Démarrer les tests

Sécurisez votre environnement dès maintenant.

Sécurisez votre code, votre cloud et votre environnement d’exécution dans un système centralisé unique.
Détectez et corrigez les vulnérabilités rapidement et automatiquement.

Aucune carte de crédit requise | Résultats en 32 secondes.