Dans les entrailles d'une supply chain attack : REX de Shai-Hulud 2.0

mai 2026

Cet article est la transcription de la conférence que j’ai donnée à la Nuit des Communautés en 2026. J’ai repris mes slides et mes notes d’orateur pour en faire un texte suivi. Le talk était en deux parties : l’attaque, puis les enseignements. J’ai gardé ce découpage.

C’est quoi une supply chain attack ?

Vous n’attaquez pas votre cible directement. Vous compromettez un de ses fournisseurs.

  1. Un attaquant compromet un paquet npm populaire.
  2. Ce paquet est installé par un développeur ou une CI.
  3. L’exécution donne accès aux secrets, aux tokens et aux infrastructures cloud.

C’est indirect, ça passe sous les radars, et ça touche potentiellement tous ceux qui installent le paquet.

Shai-Hulud 2.0 — « The Second Coming »

Une première vague, baptisée Shai-Hulud, a eu lieu en septembre 2025. Deux mois plus tard, une seconde version a frappé, plus agressive.

MétriqueValeur
Paquets npm compromis796
Téléchargements hebdomadaires cumulés20 millions+
Dépôts GitHub exposés25 000+
Secrets exfiltrés294 842
Durée de l’attaque48 h (21–23 novembre)

Parmi les victimes notables : Zapier, PostHog, Postman, ENS Domains.

Anatomie technique

Stage 0 — le point d’entrée

Compromettre un premier paquet npm, généralement par ingénierie sociale (phishing d’un mainteneur, prise de contrôle d’un compte).

Stage 1 — le preinstall

Le paquet compromis contient un script preinstall dans son package.json :

{
	"scripts": {
		"preinstall": "node setup_bun.js"
	}
}

Pourquoi preinstall ? Parce qu’il s’exécute avant l’installation, et même si l’installation échoue ensuite. Le fichier setup_bun.js est volontairement non obfusqué et bien commenté : il se fait passer pour un installeur légitime du runtime Bun. À la première lecture, le code a l’air normal. L’attaquant joue entièrement sur cette confiance.

Stage 2 — la charge utile

setup_bun.js télécharge et exécute bun_environment.js : un fichier de 10 Mo, lui bien obfusqué. Ce qu’il fait :

  1. Il énumère les variables d’environnement : tokens npm, GitHub, AWS, Azure…
  2. Il scanne les fichiers SSH, les .env, les configs git, l’historique shell.
  3. Il télécharge et exécute Trufflehog — un outil légitime de détection de secrets — pour ratisser tout le répertoire personnel.
  4. Il regroupe tout dans des fichiers JSON structurés : cloud.json, environment.json, actionsSecrets.json.

Auto-réplication

S’il trouve un token npm valide, le malware republie les paquets accessibles à ce token, payload inclus. C’est ce qui en fait un ver : il se propage de mainteneur en mainteneur.

Exfiltration

Pas de serveur de commande et contrôle (C2) classique. À la place :

  1. Création d’un dépôt GitHub public nommé « Sha1-Hulud: The Second Coming ».
  2. Push des secrets volés dedans.

L’intérêt est redoutable : le trafic vers github.com n’est jamais bloqué par un firewall, il n’y a aucun domaine suspect à repérer, et comme les dépôts sont publics, les attaquants récupèrent les secrets en temps réel. En prime, le malware enregistre la machine victime comme GitHub self-hosted runner — ce qui permet d’exécuter des workflows arbitraires à distance, même après la suppression du paquet.

Le payload destructif

Si le malware ne trouve ni token npm, ni token GitHub — donc rien à voler et rien à propager :

  • il détruit le répertoire personnel de l’utilisateur ;
  • il écrase puis supprime chaque fichier accessible en écriture.

C’est la première supply chain attack npm dotée d’un mécanisme de sabotage punitif.

Environ 20 % des machines compromises étaient des runners CI/CD : des pipelines entières ont été détruites.

Pourquoi ça nous concerne

On utilise tous npm. Et si vous n’utilisez pas npm, vous utilisez un autre écosystème avec exactement les mêmes mécanismes.

Luck isn’t a security strategy.

— Ryan Sobol

Comment l’attaque a-t-elle été détectée ?

Trois leviers, dans l’ordre où ils ont joué :

  • Connaître son écosystème. Savoir quels paquets on installe, qui les maintient, ce qu’un postinstall est censé faire.
  • Faire de la veille. Hacker News a relayé l’alerte très tôt.
  • S’outiller. Analyse de vulnérabilités dans la CI, Dependabot, Artifactory et registres internes.

Remédiation — les premières heures

Si vous découvrez que vous êtes compromis :

ActionDétail
Ne pas supprimer le dépôt malveillantLe passer en privé immédiatement pour stopper l’exposition, sans détruire les traces
Désactiver les workflows malicieuxEt vérifier tous les self-hosted runners
Supprimer les paquets infectésRevenir aux dernières versions saines connues, surtout sur les registres internes
Révoquer TOUS les secretsPas seulement ceux que vous avez trouvés — Trufflehog en a peut-être ramassé que vous n’avez pas vus
Auditer les accèsQui a installé quoi, quand, sur quelle machine

Se protéger

Les basiques

  • Des jetons scopés et distincts (poste local vs CI).
  • Signature des commits obligatoire.
  • Passphrases sur les clés.
  • Publier toujours depuis une CI, jamais depuis un poste.
  • Utiliser les Trusted Publishers de npm.

Le gestionnaire de paquets est la première ligne de défense

Le comportement par défaut face aux lifecycle scripts varie énormément :

GestionnaireApproche par défaut
Yarn (v2, 2020)Scripts désactivés, activation par dépendance
Bun (v1, 2023)367 bibliothèques « connues » sur liste blanche, le reste bloqué
pnpm (v10, 2025)strictDepBuilds fait échouer l’installation si un script non approuvé est présent
npmTout s’exécute, sauf --ignore-scripts configuré à la main

Gérer les exceptions explicitement

Contrôler quels paquets ont le droit d’exécuter des scripts : enableScripts (Yarn), trustedDependencies (Bun), onlyBuiltDependencies (pnpm). L’avantage : la liste est explicite, approuvée manuellement, et visible en revue de code.

minimumReleaseAge

Bloquer l’installation des versions publiées trop récemment, le temps que la communauté détecte les paquets malicieux :

# .npmrc (pnpm)
minimum-release-age=3d
minimum-release-age-exclude=my-org

Les 796 paquets compromis ont été publiés et détectés en 48 h. Un délai de 3 jours les aurait tous bloqués.

Disponible sur pnpm (v10.16, sept. 2025), Yarn (npmMinimalAgeGate, sept. 2025), Bun (v1.3, oct. 2025) et npm (v11.11, 2026).

trustPolicy

pnpm propose trustPolicy: "no-downgrade", qui bloque l’installation quand une nouvelle version d’un paquet présente une authentification plus faible que les versions précédentes.

npm en CI/CD

npm config set ignore-scripts true   # désactive tous les lifecycle scripts
npm install --ignore-scripts         # au cas par cas

En CI/CD, builder systématiquement avec --ignore-scripts et n’exécuter les scripts nécessaires qu’après audit.

Ce qu’il faut retenir

  1. npm (et ses équivalents) est un vecteur d’attaque majeur.
  2. Les preinstall / postinstall scripts sont dangereux par conception.
  3. Les tokens sont la cible n°1 : courts, scopés, protégés par MFA.
  4. La CI/CD est une surface d’attaque à part entière — traitez les runners comme des machines sensibles.
  5. La sécurité se joue par empilement de protections, pas par une mesure unique.
  6. Shai-Hulud 2.0 a introduit le sabotage : l’attaquant détruit ce qu’il ne peut pas voler. Le dev container prend tout son sens.

Pour aller plus loin