Attaque « Shai-Hulud » via npm : keyv, versions concernées, mesures à prendre

Les correctifs php2shell (CVE-2026-109618 et CVE-2026-109619) pour les versions de PHP en fin de vie sont désormais disponibles. Les réponses du service commercial étant plus lentes que d'habitude en raison d'une forte demande, le moyen le plus rapide d'obtenir les paquets corrigés est d'acheter ELS for Runtimes en ligne (s'ouvre dans un nouvel onglet). Consulter la FAQ sur les correctifs

Accédez directement au résumé généré par l'IA de cet article de blog

  • Résumez cet article avec Perplexity
  • Résume cet article avec Gemini

En bref

L'attaque « Shai-Hulud » visant npm a refait surface le 4 août 2026, s'introduisant dans le registre via keyv et le cacheable famille de bibliothèques de mise en cache, avant de se propager de manière autonome à des centaines d’autres paquets. Il s’agit d’un voleur d’identifiants capable de s’autoreproduire. Les chercheurs qui l’ont analysé en détail ont découvert que la campagne s’identifiait dans les descriptions du dépôt GitHub sous le nom de « Shai-Hulud : Here We Go Again », ce qui la place dans la même lignée que les attaques de la chaîne d’approvisionnement npm de septembre 2025 et celles de Les campagnes « Shai-Hulud » et « Megalodon » que nous avons analysées en juin.

Les chiffres varient selon les sources et la période considérée. JFrog a recensé plus de 400 paquets répartis sur plus de 1 700 versions. CyberScoop a indiqué que la première vague avait touché 440 paquets en moins de quatre heures, et que le total final dépassait les 860 paquets, totalisant à eux tous deux milliards d’installations mensuelles. On peut donc estimer ce nombre à plus de 400 paquets, voire à plus de 800 selon certaines sources.

Ce qui mérite qu'on s'y attarde, ce n'est pas le nombre de paquets, mais ces quatre heures. Un compte de responsable de maintenance a été piraté, et en l'espace d'un seul après-midi, la faille s'était propagée à des centaines de projets sans rapport les uns avec les autres, gérés par des personnes qui n'avaient rien fait de mal et n'avaient aucun moyen de s'en rendre compte.

Ce qui rend cette campagne intéressante à étudier, c’est qu’elle a franchi deux portes totalement différentes, et que la plupart des outils de défense du secteur ne visent aucune d’entre elles. Pour y pénétrer, il a fallu compromettre un responsable de maintenance et injecter du code malveillant dans un arborescence de sources. La propagation n’a nécessité l’écriture d’aucun code. Elle n’a exigé que la capacité de publier.

Ce sont deux problèmes distincts. Ils nécessitent deux réponses différentes.

Suis-je concerné par l'attaque npm « Shai-Hulud » ?

Commencez par lire ceci avant toute autre chose.

Vous êtes potentiellement exposé si vous avez installé, ou si votre intégrateur a installé, une version quelconque de keyv, cacheable, cacheable-request, cache-manager, flat-cache, file-entry-cache, ou tout ce qui se trouve sous la rubrique @cacheable à compter du 4 août 2026. Les rejets contaminés recensés à ce jour sont les suivants : keyv 6.0.0, cacheable 2.5.1, @cacheable/memory 2.2.1, @cacheable/net 2.1.1, @cacheable/node-cache 3.1.2 et @cacheable/utils 2.5.1 d'après l'analyse de JFrog, avec cache-manager 7.2.10 et cacheable-request 13/01/2020, confirmé par Wiz. Selon CyberScoop flat-cache et file-entry-cache dans les trente minutes environ qui ont suivi la première publication. Vérifiez ce que votre propre fichier de verrouillage a résolu, car chaque liste publiée continuait de s'allonger au fur et à mesure de sa diffusion.

Vous êtes exposé à la deuxième vague si vous consommez des produits relevant de la @onereach, @or-sdk, @nebula.js, @qlik, @ornikar ou @servicetitan scopes, ou n'importe lequel parmi les plusieurs centaines d'autres paquets dont les responsables disposaient de jetons npm stockés sur une machine infectée par le ver. Considérez toute commande « npm install » exécutée le 4 août ou après cette date comme suspecte jusqu'à ce que vous ayez vérifié ce qu'elle a réellement installé.

Un détail réduit considérablement le rayon d'action de l'explosion. La charge utile provenait d'un npm preinstall hook de cycle de vie ; or, à partir de la version 12 de npm, ces hooks ne s'exécutent pas par défaut. Si vos machines de construction et de développement utilisent npm 12 ou une version plus récente avec la politique de script par défaut, ou si vous effectuez l'installation avec --ignore-scripts, le dropper ne se serait pas exécuté, même si le fichier tarball corrompu s'était retrouvé dans node_modules, à moins que les scripts de cycle de vie n'aient été explicitement réactivés pour ces paquets. JFrog le souligne clairement, et c'est sans conteste la meilleure nouvelle qui ressort de cet incident.

Un détail suffit à l'élargir. keyv et flat-cache Ce ne sont pas des paquets que la plupart des équipes choisissent. Ils apparaissent en tant que dépendances transitives d’ESLint et des bibliothèques de clients HTTP, plusieurs niveaux plus bas, dans des projets qui ignorent totalement leur présence.

C’est pourquoi celle-ci a eu un impact plus profond que ses prédécesseurs. Les données de télémétrie de Wiz citées par CyberScoop ont révélé que le paquet compromis le plus répandu était présent dans environ 46 % des environnements cloud qu’ils peuvent observer, contre environ 28 % pour la vague précédente de Shai-Hulud 2.0. L’attaque npm Shai-Hulud a pu étendre sa portée en s’introduisant via une infrastructure à laquelle personne ne pense.

Comment s'est déroulée l'attaque « Shai-Hulud » visant npm

Le point d'entrée était le compte GitHub d'un responsable de maintenance. Wiz décrit le keyv et cacheable des paquets compromis « à la suite de la compromission du compte d’un responsable GitHub, qui a entraîné la publication de versions malveillantes de ces paquets », et retrace la chronologie des événements : l’attaquant a d’abord utilisé l’identité compromise pour introduire des charges utiles de persistance dans l’IDE au sein du keyv le dépôt lui-même, puis a publié peu après une nouvelle version de keyv transportant la charge utile.

Cet ordre a son importance. Le code a été intégré au projet avant d’être ajouté au registre ; ainsi, la version publiée sur npm est issue du processus de publication propre au projet, et non d’un processus parallèle. D’après ce que ce processus a pu constater, une identité autorisée avait déposé du code dans le dépôt et publié une version. C’est précisément la raison d’être de ce processus.

Chaque colis contenait deux fichiers. Le premier, setup.mjs, est un petit dropper obfusqué intégré à package.json comme "preinstall": "node setup.mjs". Il ne contient pas la charge utile. Il télécharge le moteur d'exécution JavaScript Bun, le décompresse, puis l'utilise pour exécuter le deuxième fichier : environ 710 à 728 kilo-octets de code JavaScript fortement obscurci, nommé math_init.js dans certains paquets et Math_Symbol.js chez les autres.

Le choix de recourir à Bun plutôt qu’au runtime Node déjà présent est délibéré. Cela offre à la charge utile un environnement d’exécution prévisible, quelle que soit la version de Node utilisée par la victime, et permet d’effectuer le travail proprement dit dans un processus qui échappe à la surveillance du gestionnaire de paquets.

La première porte : un arbre-source empoisonné

Pour keyv et le cacheable Dans le cas de ces paquets, le code malveillant se trouvait dans le dépôt Git en amont. Cela a plus d'importance qu'il n'y paraît.

Cela signifie que ce compromis résiste à tout ce qui repose sur l’hypothèse selon laquelle le code source est fiable et que seul l’artefact publié est suspect. Si vous recompilez à partir du code source, vous recréez la porte dérobée. Si vous vérifiez l’artefact par rapport au code source, la vérification s’avère positive. Vérifiez l’attestation de provenance : elle est authentique, car la compilation s’est bel et bien déroulée selon le workflow indiqué, à partir du commit indiqué. L’attestation lie l’artefact à un code source et à un processus de compilation, qui étaient tous deux réels ; le code source était simplement déjà corrompu.

La campagne a poussé ce raisonnement jusqu'à son terme en visant une cible différente. JFrog a découvert une logique de cas particulier ciblant OpenSearch opensearch-js dépôt, qui utilise GitHub Actions OIDC pour une publication sécurisée : au sein d'une exécution de workflow dont le dépôt était opensearch-js et dont GITHUB_WORKFLOW_REF correspondant release-drafter.yml, la charge utile a demandé un jeton OIDC « Actions » dont la portée est limitée à npm:registry.npmjs.org, l'ont échangé contre un jeton de publication, ont publié un fichier tar malveillant, puis ont généré un bundle Sigstore à l'aide de Fulcio et de Rekor afin que le résultat présente une provenance valide issue d'un contexte de workflow fiable.

La meilleure défense contre cette faille consiste à ce que quelqu'un ou quelque chose lise le fichier diff.

La deuxième porte : 400 paquets pour lesquels personne n'a écrit de code

C'est là que la campagne a pris de l'ampleur, et c'est là que les mécanismes diffèrent de ceux du début, au point de modifier les moyens de s'y opposer.

La charge utile récupère les jetons npm à partir de ~/.npmrc et les valide par rapport au registre. Lorsqu'il détecte un jeton disposant d'un droit d'écriture et bypass_2fa Lorsqu'il est défini sur « true », il effectue les opérations suivantes, selon la description de la routine fournie par JFrog : il télécharge le fichier actuel latest fichier tar, copie l'exemple en cours d'exécution vers package/math_init.js, écrit le chargeur dans package/setup.mjs, remplace l'intégralité du paquet scripts objet et ensembles scripts.preinstall à node setup.mjs, incrémente la version de correctif, puis recompile et publie via une connexion authentifiée directe PUT dans le registre npm.

Relisez cette séquence et remarquez ce qui manque. Pas de commit. Pas de pull request. Pas d’exécution de CI. Aucun accès au dépôt, d’aucune sorte. L’arborescence source de chacun de ces plusieurs centaines de paquets secondaires est restée intacte tout au long du processus et l’est toujours, car le code malveillant n’existait qu’à l’intérieur d’une archive tar créée sur la machine de l’attaquant et directement poussée dans le registre à l’aide d’identifiants volés.

C'est ce qui représente la majeure partie de cet incident en nombre de paquets : des centaines de responsables de paquets dont les dépôts n'ont jamais été modifiés, dont la révision du code n'a jamais été contournée, dont l'intégration continue n'a jamais détecté d'anomalie, et dont les utilisateurs ont tout de même installé des logiciels malveillants.

Ce que la charge utile a emporté

La liste des identifiants est si exhaustive qu’elle semble avoir été constituée au fil de plusieurs campagnes plutôt que d’avoir été rédigée spécifiquement pour celle-ci. Il parcourt des centaines de chemins configurés sous Linux, macOS et Windows, récupérant les jetons des gestionnaires de paquets, les identifiants cloud issus des fichiers de configuration et des services de métadonnées d’instances, les jetons des comptes de service Kubernetes, l’intégralité du magasin KV HashiCorp Vault, les clés SSH, les profils VPN, les magasins d’identifiants des navigateurs, les jetons Stripe et Slack, ainsi que les clés des services d’IA, notamment OpenAI, Anthropic, Cursor et Gemini.

Sur les runners GitHub Actions, il effectue également une vidange de la mémoire du processus pour les secrets que le runner a déjà déchiffrés. Derrière tout cela se cache un balayage de plus de 200 modèles glob à la recherche de .env fichiers, clés privées, état Terraform et bases de données KeePass.

Les résultats chiffrés sont stockés dans des dépôts GitHub publics créés sous le compte de la victime elle-même, avec des descriptions suivant le modèle « Shai-Hulud : Here We Go Again ». Ox Security a recensé plus de 800 dépôts de ce type accessibles sur GitHub. Un canal HTTP de secours pointe vers un domaine enregistré le 22 mai 2026, soit plus de deux mois avant sa première utilisation.

Ox Security fait également état d'un mécanisme de sabotage que les autres analyses ne mentionnent pas : si le jeton GitHub volé est révoqué, le logiciel malveillant envoie des commandes de suppression à la machine infectée. Considérez cette information comme provenant d'un seul fournisseur plutôt que comme ayant été confirmée de manière indépendante, mais tenez-en compte dans l'organisation de vos mesures correctives. La révocation des identifiants avant d'avoir isolé l'hôte peut avoir des conséquences.

Il s'en est également pris à votre rédacteur en chef

Du côté de GitHub, la propagation cible les machines des développeurs plutôt que le registre, et c'est cette partie qui a le plus de chances de passer encore inaperçue dans un dépôt quelque part.

À l'aide de jetons d'accès personnels GitHub volés, la charge utile effectue des commits de cinq fichiers sur un maximum de 50 branches par dépôt : .vscode/tasks.json, .vscode/setup.mjs, .claude/math_init.js, .claude/settings.json et .claude/setup.mjs. Le tasks.json Cette entrée définit une tâche nommée « Configuration de l'environnement » qui s'exécute node .claude/setup.mjs, et le settings.json l'entrée enregistre un SessionStart hook. L'exécution a lieu lorsqu'un développeur ouvre le dépôt dans VS Code ou lance une session de codage assistée par IA.

Les commits sont intitulés chore: update config et comportent une mention de co-auteur falsifiée les attribuant à [email protected]. Lorsque le jeton volé dispose également d'une portée de workflow, la charge utile crée un workflow GitHub Actions qui exfiltre les secrets d'Actions en les intégrant dans un artefact, puis en le téléchargeant sur une branche nommée dependabot/github_actions/format/setup-formatter.

Chacun de ces noms est choisi pour passer inaperçu lors d'une lecture rapide. Une branche qui ressemble à « Dependabot », un workflow qui ressemble à « CodeQL », un commit qui ressemble à une mise à jour de configuration, un artefact qui ressemble à un formateur, et une ligne de co-auteur qui ressemble à un assistant IA — autant d'éléments que de nombreuses équipes intègrent désormais légitimement dans leurs dépôts.

Pourquoi les scanners traditionnels n'ont pas détecté l'attaque « Shai-Hulud » sur npm

Les outils sur lesquels s'appuient la plupart des organisations ont été conçus pour répondre à une question que cette attaque ne pose pas.

Les scanners de vulnérabilités comparent votre arborescence de dépendances à des bases de données répertoriant les CVE connus. Le 4 août, il n'existait aucun CVE pour keyv 6.0.0, il n'y avait donc rien de compatible. Lorsque les avis de sécurité ont été publiés, le délai de quatre heures était déjà écoulé et le ver était déjà passé à autre chose.

Il ne s'agit pas là d'une critique à l'égard de ces scanners. La détection des vulnérabilités connues relève d'une autre mission, et c'est une tâche qu'ils accomplissent avec brio.

Les contrôles des comptes au niveau du registre ont également rempli leur fonction. L'authentification à deux facteurs protège un compte de publication contre toute personne ne disposant que d'un mot de passe. Elle ne protège pas contre un jeton généré par ce compte à des fins d'automatisation, et le ver recherchait spécifiquement les jetons marqués pour contourner l'authentification à deux facteurs, précisément parce que ce sont ces jetons-là qui fonctionnent sans intervention humaine. De nombreuses configurations automatisées de publication sur npm ont historiquement reposé sur ce type d'identifiant, à savoir un jeton permettant de publier sans intervention humaine.

La provenance et la signature constituent le cas le plus complexe, car dans cette campagne, elles ont fonctionné exactement comme prévu et ont tout de même abouti à un résultat « vert ». La tentative OpenSearch en est l’exemple le plus parlant : une archive tar malveillante publiée via un workflow véritablement fiable, accompagnée d’un bundle Sigstore généré via Fulcio et Rekor pour correspondre.

La provenance permet de déterminer si « cet artefact provient bien de la source et de la chaîne d'approvisionnement indiquées », et la réponse était oui. Cette question mérite d'être posée et n'est pas sans intérêt dans ce contexte. Cependant, une coche verte vous indique d'où provient un artefact, mais ne vous dit pas si l'objet provenant de cette source est sûr.

L'analyse d'accessibilité présente le même type de lacune. Il est utile de vérifier qu'un chemin d'exécution vulnérable n'est jamais appelé pour établir les priorités dans le traitement des CVE. A preinstall Il n'est pas nécessaire que ce hook soit accessible depuis votre application. Il s'exécute dans le cadre de l'installation de la dépendance, avant même que votre application ne l'appelle.

Deux portes nécessitent deux serrures différentes

Les deux voies d'accès de cette campagne sont contrecarrées par des éléments totalement différents ; il est donc important de préciser quel mécanisme de TuxCare bloque quelle porte.

SecureChain pour npm fournit des composants open source que TuxCare recompile à partir du code source au sein de sa propre infrastructure de compilation, puis publie dans son propre catalogue, accompagnés d’attestations de provenance et d’une SBOM CycloneDX pour chaque artefact. Les clients configurent leur gestionnaire de paquets pour qu’il se connecte à SecureChain plutôt qu’au registre public ; TuxCare devient ainsi la source par défaut à partir de laquelle leurs compilations sont résolues.

Il existe deux façons de procéder. Soit on utilise uniquement ce qui figure dans le catalogue SecureChain, soit on installe un gestionnaire de registre en amont, de sorte que tout ce qui ne figure pas encore dans le catalogue soit redirigé vers le registre public.

Une deuxième porte se ferme sur les droits d'édition

Commençons par le mécanisme de propagation, car c’est la partie la plus importante de l’incident et celle qui est la plus claire. La méthode utilisée par le ver consistait à récupérer une archive tar publiée, à y injecter deux fichiers, puis à remplacer le scripts bloquer, modifier la version du correctif et envoyer le résultat au registre à l'aide d'un jeton volé. Cette attaque nécessite avant tout une chose : la capacité à placer une nouvelle version d'un paquet à l'endroit où votre résolveur ira la chercher.

Pour tout composant du catalogue SecureChain, la version utilisée par votre build est celle que TuxCare a compilée à partir du code source de ce projet dans son propre environnement d’intégration continue (CI). L'archive republiée par l'attaquant ne se trouve pas dans ce chemin d'accès, et aucune publication dans le registre public ne permet de l'y placer. Les fichiers malveillants contenus dans ces quelques centaines de paquets secondaires n'existaient qu'au sein d'une archive poussée vers npm. Ils n'ont jamais figuré dans le code source. Une compilation à partir du code source ne les reproduit pas, et il n'y a rien à détecter puisqu'il n'y a rien à cet endroit.

Il convient d'expliquer clairement ce mécanisme. La technique qui a permis à cette campagne de prendre de l'ampleur nécessite un accès à la publication, quelle que soit la source à partir de laquelle votre build est généré. S'il s'agit du registre public, un jeton volé suffit. S'il s'agit du catalogue SecureChain, un jeton npm volé ne sert à rien à l'attaquant, car la publication sur npm n'ajoute pas de version à votre build.

La première étape consiste à comparer les différences

La question du point d'entrée est plus épineuse, et c'est celle que le secteur a généralement évitée, car, pour être honnête, il faut bien que quelqu'un examine le code.

SecureChain intègre la détection de code malveillant dans son pipeline d'intégration des paquets. Le détecteur analyse les différences de code source entre une nouvelle version d'un paquet et la version précédente, à la recherche de schémas comportementaux associés à une compromission de la chaîne d'approvisionnement : exécution suspecte lors de la compilation, obfuscation, exfiltration de données et autres indicateurs d'intention malveillante. Les hooks d'exécution lors de la compilation sont explicitement pris en compte, ce qui couvre npm preinstall et postinstall, Python setup.py les chemins d'exécution et les appels aux plugins Maven.

Une version marquée n'est pas intégrée à la version finale. Elle n'est pas ajoutée au catalogue, ce qui signifie qu'elle n'est jamais intégrée à la version d'un client.

Imaginez ce que ce détecteur aurait pu observer le 4 août. La différence entre la dernière image « propre » keyv et keyv La version 6.0.0 contient un nouveau fichier nommé setup.mjs, un deuxième nouveau fichier d'environ 710 kilo-octets contenant du code JavaScript fortement obscurci, et un preinstall entrée figurant dans package.json là où une bibliothèque de mise en cache n'en disposait pas auparavant.

Chacun de ces éléments est un signal que le détecteur est conçu pour détecter : du contenu obscurci apparaissant dans une arborescence source, un hook d’exécution au moment de la compilation qui surgit là où il n’y en avait pas auparavant, et ces deux éléments se retrouvant dans la même version d’une bibliothèque dont la seule fonction est de stocker des paires clé-valeur. Le détecteur se concentre sur l’effet d’une modification plutôt que sur la correspondance avec des fichiers connus pour être malveillants ; c’est pourquoi le fait de réutiliser la même technique sous des noms de fichiers différents ne permet pas de passer entre les mailles du filet.

La détection au niveau du code source est le mécanisme de SecureChain qui s'attaque directement à la première faille. La recompilation à partir du code source ne sert à rien lorsque c'est justement ce code qui a été corrompu. C'est l'argument que nous avions avancé en juin au sujet de la provenance, et cette campagne n'y a rien changé. Elle l'a même renforcé, en mettant en évidence les deux modes de défaillance au cours d'un même après-midi.

Mesures d'atténuation sans SecureChain

Si vos builds proviennent directement du registre public, voici les contrôles qui auraient permis de limiter cette campagne, classés par ordre d'efficacité.

Assurez-vous que npm 12 ou une version ultérieure soit installé sur chaque machine de développement et chaque serveur d'intégration continue. Le preinstall La modification par défaut correspond à la différence entre un fichier tar corrompu se trouvant dans node_modules et un fichier tarball corrompu qui s'est exécuté. Si vous ne pouvez pas encore effectuer la mise à jour, procédez à l'installation avec --ignore-scripts et traiter les paquets qui nécessitent réellement des étapes de compilation comme des exceptions nommées.

Définissez un délai minimum de publication afin que vos builds n'installent jamais une version le jour même de sa sortie. Les quatre principaux gestionnaires de paquets intègrent désormais cette fonctionnalité de manière native : npm dispose de min-release-age À partir de la version 11.10.0, Bun dispose de minimumReleaseAge À partir de la version 1.3.0, pnpm et yarn proposent tous deux des options équivalentes permettant de définir des exclusions par paquet. Une période de trois à sept jours ne vous coûtera presque rien et aurait couvert l'intégralité de la fenêtre de quatre heures de cette campagne. Si vous ne devez retenir qu'un seul changement de configuration de cet article, c'est bien celui-là.

Vérifiez les jetons npm pour les droits d'écriture associés à un contournement de l'authentification à deux facteurs (2FA), ce qui correspond exactement à la combinaison ciblée par le ver. Les jetons de publication doivent avoir une durée de vie limitée, être restreints aux paquets qui en ont besoin et être attribués à des processus automatisés plutôt qu'à des personnes. Privilégiez la publication de confiance lorsque le workflow le permet, tout en gardant à l'esprit, comme l'a montré le cas d'OpenSearch, que cela déplace la limite de confiance vers vos définitions de workflow plutôt que de la supprimer.

Considérez les runners CI comme des systèmes de production qui gèrent des secrets, car cette charge utile a vidé la mémoire du processus du runner. Limitez la portée des politiques de confiance OIDC à des dépôts et à des fichiers de workflow spécifiques plutôt qu’à l’ensemble d’une organisation, et exigez une révision des modifications apportées sous .github/workflows.

Passez en revue les opérations que votre éditeur et vos outils de codage IA effectuent automatiquement à partir d'un dépôt. Le .vscode/tasks.json et .claude/settings.json Vector fonctionne car l'ouverture d'un projet lance la configuration fournie avec celui-ci. Lors de la révision du code, considérez ces fichiers comme du contenu exécutable, car c'est effectivement le cas.

Télécharger SecureChain →

Détection

Recherchez les cinq fichiers de hook dans toutes les branches de chaque dépôt, et pas seulement dans les branches par défaut : .vscode/tasks.json contenant une tâche « Configuration de l'environnement », .vscode/setup.mjs, .claude/setup.mjs, .claude/math_init.js, et .claude/settings.json avec un SessionStart hook. Recherchez les commits intitulés chore: update config, notamment lorsqu'un coauteur est mentionné comme suit : [email protected].

Recherchez les noms de fichiers dans les arborescences de dépendances et les caches de paquets setup.mjs, math_init.js, Math_Symbol.js et router_runtime.js. JFrog a publié la valeur de hachage SHA-256 de la charge utile qu'il a extraite de keyv 6.0.0 ; privilégiez donc cette version plutôt que les noms de fichiers seuls, dans la mesure du possible.

Dans les journaux d'audit de GitHub, recherchez le dependabot/github_actions/format/setup-formatter branche, exécutions de workflow nommées codeql_analysis.yml ou « Run Copilot », qui permettent de créer et de supprimer rapidement un artefact, ainsi que tout artefact nommé format-results. Recherchez les dépôts créés sous les comptes de votre organisation dont les descriptions correspondent au modèle « Shai-Hulud : Here We Go Again ».

Du côté du réseau, recherchez les connexions vers npm-cache.com, ainsi que pour le trafic sortant vers des points de terminaison RPC publics d'Ethereum provenant d'une infrastructure de développement qui n'a aucune raison de communiquer avec une blockchain. La charge utile lit sa liste actuelle de domaines C2 à partir d'un contrat du réseau principal d'Ethereum ; ces deux éléments constituent donc des signaux de détection utiles, mais aucun d'entre eux ne constitue un bloc fiable.

Liste de contrôle pour la correction des vulnérabilités liées à Shai-Hulud sur npm, classée par ordre de priorité

  1. Déterminez si preinstall les scripts auraient pu s'exécuter. Vérifiez la version de npm sur chaque machine et chaque exécuteur ayant installé des dépendances à partir du 4 août 2026, et si --ignore-scripts était en vigueur. Si les hooks ne pouvaient pas s'exécuter, le travail qui vous reste à faire relève davantage de la gestion des dépendances que de la gestion des incidents.
  2. Isolez avant de révoquer. Commencez par sauvegarder les archives tar, les journaux npm, les journaux CI, les journaux d'audit GitHub et les images des runners. Compte tenu du comportement de suppression signalé lors de la révocation des jetons, procédez à l'isolation séquentielle avant la rotation des identifiants sur tout hôte susceptible d'avoir exécuté la charge utile.
  3. Fixe les fichiers de verrouillage aux versions publiées avant le 4 août 2026 pour chaque paquet concerné, et vide les caches du gestionnaire de paquets afin que les archives tar corrompues ne restent pas stockées localement.
  4. Commencez par révoquer les jetons npm, en priorisant ceux qui disposent d'un droit d'écriture et permettent de contourner l'authentification à deux facteurs (2FA), puis les jetons d'accès personnels GitHub et les jetons Actions.
  5. Faites tourner tout ce que le collecteur pourrait lire, en partant du principe qu’il l’a lu : identifiants cloud, jetons de compte de service Kubernetes, contenu de Vault, identifiants de base de données, clés SSH, profils VPN, jetons Stripe et Slack, ainsi que les clés API pour les services d’IA.
  6. Parcourez toutes les branches de tous les dépôts à la recherche des cinq fichiers de hook et des commits marqueurs, puis supprimez-les.
  7. Recréez les serveurs CI et les environnements de développement à partir d'images vierges. Le fait de nettoyer des fichiers individuels ne garantit pas que la machine soit propre.
  8. Examinez les éditeurs de confiance npm et les politiques de confiance OIDC de GitHub, en accordant une attention particulière aux workflows d'automatisation des mises en production, car ceux-ci ont été spécifiquement ciblés.
  9. Ce n'est qu'ensuite qu'il faudra passer à la prévention : seuils d'âge de publication, npm 12, délimitation des jetons, exigences en matière de révision des workflows.

Pourquoi ça continue de marcher ?

L'attribution de la responsabilité n'est pas encore établie. Les chercheurs cités par CyberScoop ont indiqué ne pas disposer pour l'instant de liens concrets permettant d'établir un lien avec TeamPCP, l'acteur qui a compromis plus d'un millier de paquets au début de l'année 2026, bien que cette possibilité ne soit pas écartée. Plusieurs entreprises ont signalé la présence de la même charge utile et du même schéma dans tous les paquets affectés, ce qui suggère l'intervention d'un seul acteur ou d'un groupe étroitement coordonné, plutôt que celle d'imitateurs.

La question de l'attribution est moins intéressante que celle de la structure. Cette technique continue de fonctionner parce qu'il ne s'agit pas vraiment d'une technique, mais d'un constat sur la manière dont les écosystèmes de paquets sont construits : toute personne capable de publier une version d'un paquet peut exécuter du code sur chaque machine qui l'installe. Ce n'est pas un bug de npm. C'est la raison d'être d'un script de cycle de vie, et des milliers de paquets légitimes dépendent de ce comportement.

Ajoutez à cela le fait que les droits de publication sont détenus par des particuliers, que ces derniers possèdent des jetons, que ces jetons se trouvent dans des fichiers stockés sur des ordinateurs portables, et que la première action de la charge utile consiste à rechercher ces fichiers. La propagation du ver n’est pas une fonctionnalité inhabituelle ; il s’agit simplement du processus habituel de publication, exécuté par un logiciel malveillant à la place d’un administrateur.

En juin, l'argument était qu'une vérification de provenance « verte » constituait un élément parmi d'autres dans une décision de confiance, plutôt qu'un verdict en soi. En août, l'argument est plus restreint et plus mécanique : si la version à laquelle votre build se connecte est celle qu'un attaquant disposant d'un jeton volé a poussée en dernier, alors tous les autres contrôles dont vous disposez se situent en aval d'une décision que vous n'avez pas prise.

Prenez donc cette décision en toute connaissance de cause. Allez vérifier quelles versions vos builds ont résolues le 4 août, puis fixez un seuil d’ancienneté des versions avant la prochaine vague, car il y en aura une. Si vous préférez que les versions utilisées par vos builds proviennent d’un catalogue mis à jour et vérifié par quelqu’un d’autre plutôt que de la personne détenant un jeton de publication, contactez-nous pour en savoir plus sur SecureChain pour npm.

FAQ

S'agit-il de la même campagne que celle de Shai-Hulud de septembre 2025 ?

Il s'agit de la même lignée, qui se désigne d'ailleurs ainsi, mais ce n'est pas le même incident. L'attaque npm « Shai-Hulud » d'août 2026 a pénétré via keyv et cacheable plutôt que les paquets touchés précédemment, et CyberScoop la décrit comme une variante de Mini Shai-Hulud. D’après les chiffres de prévalence dans l’environnement fournis par Wiz, elle s’est propagée bien plus largement que ses prédécesseurs.

Le dépôt GitHub du paquet semble ne pas présenter de problèmes. Est-ce que cela signifie que la version que j'ai installée est elle aussi sans problème ?

Non, et c’est là le détail qui risque le plus d’échapper. Pour les quelques centaines de paquets secondaires, l’attaquant n’a jamais touché au dépôt source. Le code malveillant n’existait que dans l’archive tar publiée sur npm. Un dépôt « propre » ne vous apprend rien sur ce que votre gestionnaire de paquets a réellement téléchargé. Vérifiez la version à laquelle votre fichier de verrouillage a abouti, et non le code source.

Une signature valide ou une attestation d'origine garantit-elle la sécurité d'un colis ?

Non. Dans le cas d'OpenSearch, cette campagne a généré un véritable ensemble Sigstore, via Fulcio et Rekor, pour une archive tar malveillante publiée à partir d'un workflow de confiance. La provenance établit l'origine. Elle n'évalue pas le contenu.

Nous utilisons un registre privé qui sert de proxy à npm. Sommes-nous protégés ?

À condition qu'il ne se contente pas de relayer les données. Un proxy à mise en cache n'hésitera pas à mettre en cache et à servir la version corrompue, généralement plus rapidement que ne le ferait le serveur en amont. Ce qui importe, c'est de savoir si quelque chose inspecte, reconstruit ou retarde les données qui transitent par lui.

Nous utilisons un outil de détection des vulnérabilités à chaque version. Pourquoi n'a-t-il pas détecté cela ?

Aucune vulnérabilité connue n'a pu être détectée. Les scanners comparent votre arborescence de dépendances aux bases de données CVE, et le 4 août, les versions corrompues dataient de quelques heures seulement et ne faisaient l'objet d'aucun avis de sécurité. L'analyse des vulnérabilités connues et la détection de codes malveillants sont deux problèmes distincts qui nécessitent des outils différents.

Qu'aurait fait SecureChain dans ce cas ?

Il y a deux cas de figure différents selon les deux voies d'accès. Pour les quelques centaines de paquets compromis par la republication d'archives tar, la version corrompue ne se trouve pas dans le chemin d'accès utilisé par votre processus de compilation, car SecureChain fournit la version compilée par TuxCare à partir du code source, plutôt que de servir en proxy la dernière version publiée par l'amont. Pour la première keyv et cacheable compromis : lorsque le code est d'abord intégré au référentiel, le détecteur de code malveillant analyse les différences de version lors de l'intégration et bloque la publication, de sorte que la version corrompue n'entre jamais dans le catalogue. Ces deux mécanismes s'appliquent à l'ensemble du SecureChain catalogue, pour chaque composant qu'il contient.

Résumé
Attaque « Shai-Hulud » via npm : keyv, versions concernées, mesures à prendre
Nom de l'article
Attaque « Shai-Hulud » via npm : keyv, versions concernées, mesures à prendre
Description
L'attaque npm « Shai-Hulud » a touché plus de 400 paquets en quatre heures via keyv et cacheable. Versions concernées, indicateurs de compromission (IOC) et liste de mesures correctives classées par ordre de priorité.
Auteur
Nom de l'éditeur
de TuxCare
Logo de l'éditeur

Rejoignez plus de 4 500 professionnels de Linux et de l'Open Source!

2 fois par mois. Pas de spam.

Table des matières
Obtenez les réponses à vos questions sur la sécurité des logiciels libres

Rejoignez plus de 4 500 professionnels de Linux et de l'Open Source!

2 fois par mois. Pas de spam.