- Le package npm malveillant Shai-Hulud 2.0 a compromis la machine d’un développeur et permis de s’emparer de l’accès à l’organisation GitHub de Trigger.dev
- L’infection a commencé lorsque le développeur a exécuté
pnpm install, ce qui a déclenché le scriptpreinstalldu package malveillant ; l’outil TruffleHog a ensuite été utilisé pour voler des identifiants - Pendant 17 heures, l’attaquant a cloné 669 dépôts, puis a tenté en 10 minutes de forcer des pushs sur 199 branches et de fermer 42 PR
- Les packages et les systèmes de production n’ont pas été compromis ; l’attaque a été détectée en 4 minutes et l’accès au compte a été bloqué
- Après l’incident, l’équipe a renforcé sa sécurité avec la désactivation des scripts npm, la mise à niveau vers pnpm 10, le déploiement npm via OIDC et la protection systématique des branches
Vue d’ensemble de l’attaque
- Le 25 novembre 2025, pendant un débogage interne sur Slack, une anomalie a été repérée : plusieurs dépôts recevaient un commit « init » au nom de Linus Torvalds
- L’enquête a montré que le ver de supply chain Shai-Hulud 2.0 avait compromis la machine d’un développeur et volé des identifiants GitHub
- Ce ver aurait infecté plus de 500 packages npm et affecté plus de 25 000 dépôts
- Les packages npm officiels de Trigger.dev (
@trigger.dev/*, CLI) n’ont pas été infectés
Chronologie de l’attaque
- 24 novembre, 04:11 UTC : début de la diffusion du package malveillant
- 20:27 UTC : infection de la machine d’un développeur en Allemagne
- 22:36 UTC : premier accès de l’attaquant et début du clonage massif de dépôts
- 15:27~15:37 UTC (25 novembre) : phase d’attaque destructrice pendant 10 minutes
- 15:32 UTC : détection de l’anomalie et blocage de l’accès en 4 minutes
- 22:35 UTC : restauration complète de toutes les branches
Processus d’infection
- Quand le développeur a exécuté
pnpm install, le scriptpreinstalldu package malveillant s’est lancé, a téléchargé puis exécuté TruffleHog - TruffleHog a analysé et exfiltré les tokens GitHub, identifiants AWS, tokens npm, variables d’environnement, etc.
- Le répertoire
.trufflehog-cacheet des fichiers associés ont été retrouvés sur la machine infectée - Le package à l’origine de l’infection ayant été supprimé, il n’a pas pu être retracé
Activité de l’attaquant
- Après l’infection, une phase de reconnaissance a duré 17 heures
- 669 dépôts ont été clonés via une infrastructure située aux États-Unis et en Inde
- Les activités du développeur ont été surveillées afin de conserver l’accès via le token GitHub
- Un dépôt nommé « Sha1-Hulud: The Second Coming » a été créé, probablement pour stocker des identifiants
- Puis, une phase de destruction de 10 minutes a suivi
- Tentatives de push forcé sur 199 branches dans 16 dépôts
- Fermeture de 42 PR, certaines ayant été bloquées par la protection de branche
- Tous les commits apparaissaient sous la forme « Linus Torvalds <email> / init »
Détection et réponse
- L’anomalie a été détectée en temps réel via des alertes Slack
- En 4 minutes, l’accès GitHub du compte compromis a été bloqué, puis tous les accès aux services AWS, Vercel, Cloudflare, etc. ont été révoqués
- L’analyse des logs AWS CloudTrail a montré uniquement des appels API en lecture seule, sans accès aux données de production
- AWS a aussi détecté séparément une activité suspecte liée à Shai-Hulud et envoyé une alerte
Impact et restauration
- 669 dépôts clonés, 199 branches visées par des pushs forcés, 42 PR fermées
- L’absence de reflog côté serveur sur GitHub a compliqué la restauration, mais l’équipe a pu tout rétablir en 7 heures grâce à l’Event API et aux reflogs locaux
- Les packages npm et l’infrastructure de production n’ont pas été compromis
Exposition d’une clé GitHub App
- Pendant l’enquête, une clé privée GitHub App a été retrouvée dans la corbeille du laptop du développeur
- Cette clé disposait de droits read/write sur les dépôts clients et a été immédiatement renouvelée
- La base de données (où sont stockés les ID d’installation) n’ayant pas été compromise, aucune preuve d’accès aux dépôts clients n’a été trouvée, sans qu’un risque puisse être totalement exclu
- Des logs supplémentaires ont été demandés au support GitHub et un e-mail d’information a été envoyé aux clients
Analyse technique de Shai-Hulud
- Lors de l’exécution de
setup_bun.js, le runtime Bun est installé etbun_environment.jsest lancé en arrière-plan - TruffleHog est utilisé pour collecter les identifiants présents dans le répertoire
$HOME - Les données collectées (
contents.json,cloud.json,truffleSecrets.json, etc.) sont téléversées vers des dépôts GitHub aléatoires avec un encodage base64 triple - Si un token npm est présent, le ver modifie et republie les packages du compte infecté afin de se propager
- En l’absence d’identifiants, il tente de supprimer le répertoire personnel
- Fichiers indicateurs de compromission :
setup_bun.js,bun_environment.js,.trufflehog-cache/, etc.
Mesures de renforcement de la sécurité
- Désactivation complète des scripts npm (
ignore-scripts=true) - Mise à niveau vers pnpm 10 : scripts désactivés par défaut, configuration de
minimumReleaseAge(3 jours) pour retarder l’installation de nouveaux packages - Adoption de npm Trusted Publishers basé sur OIDC pour éliminer les tokens de longue durée
- Protection de branche activée sur tous les dépôts
- Déploiement de Granted sur AWS SSO, avec chiffrement des tokens de session
- Dans GitHub Actions, approbation désormais requise avant d’exécuter les workflows des contributeurs externes
Enseignements pour les autres équipes
- Le simple fait que l’installation npm puisse exécuter du code arbitraire constitue en soi une surface d’attaque
- Il faut activer
ignore-scripts=trueet ne mettre en liste blanche que les packages réellement nécessaires pnpm minimumReleaseAgepermet de retarder l’installation des nouveaux packages- La protection de branche et le déploiement via OIDC sont des mesures de sécurité indispensables
- Il ne faut pas stocker d’identifiants longue durée sur les machines locales ; les déploiements doivent passer uniquement par la CI
- Le bruit des alertes Slack s’est révélé être la clé de la détection
Dimension humaine
- Le développeur infecté n’est pas en faute : il a été compromis pour avoir simplement exécuté
npm install - Pendant l’attaque, il a aussi été constaté que le compte concerné avait automatiquement ajouté en favori des centaines de dépôts aléatoires
- L’incident révèle non pas une erreur individuelle, mais une vulnérabilité structurelle de l’écosystème
Indicateurs récapitulatifs
- Délai entre l’infection initiale et la première attaque : environ 2 heures
- Durée de maintien de l’accès par l’attaquant : 17 heures
- Durée des actions destructrices : 10 minutes
- 5 minutes jusqu’à la détection, 4 minutes jusqu’au blocage
- 7 heures jusqu’à la restauration complète
- Dépôts clonés : 669 / branches affectées : 199 / PR fermées : 42
Ressources de référence
- Socket.dev : Shai-Hulud Strikes Again V2
- Rapports d’analyse de PostHog, Wiz, Endor Labs et HelixGuard
- Documentation npm Trusted Publishers, pnpm
onlyBuiltDependencies,minimumReleaseAge, Granted
3 commentaires
Avec pnpm, la structure semble exiger par défaut d’autoriser post-install individuellement, mais au final les développeurs finissent probablement eux aussi par l’autoriser inconsciemment.
Je comprends qu’ils ont renforcé la sécurité en passant à pnpm, puisque npm s’exécute par défaut, afin de le désactiver par défaut.
Commentaires sur Hacker News
Exécuter
npm installn’est pas de la négligenceLe vrai problème, c’est un écosystème qui autorise l’exécution de code arbitraire pendant l’installation des paquets
Mais l’échec de sécurité fondamental, c’est d’utiliser un gestionnaire de paquets qui permet à un tiers d’injecter du code dans mon produit sans aucune vérification
Au final, nous dépendons indéfiniment de la bonne volonté et des compétences des gestionnaires de paquets et de leurs mainteneurs
Et l’OP semble aussi laisser entendre qu’il stockait des identifiants en clair sur le système de fichiers
On peut concevoir, au niveau du langage, une structure qui limite le code à lire des entrées, consommer des ressources et ne produire que des sorties correctement typées
Ce n’est pas une solution complète au problème de la supply chain, mais cela réduit fortement la surface d’exposition
C’est du genre : « ce n’est pas une erreur qu’une personne utilise cet outil, mais si tout le monde l’utilise alors l’écosystème devient problématique »
Le fait que beaucoup d’outils de développement aient déjà des failles de sécurité a été démontré à maintes reprises
Si on s’en soucie vraiment, il faut le montrer par des actes
Avec VS Code, c’était pénible de devoir installer un plugin fait par on ne sait qui juste pour ajouter une petite fonctionnalité
J’ai l’impression que, de toute façon, on finit toujours par exécuter du code qu’on est obligé de considérer comme fiable
netrcpour l’authentification httpSi on utilise git via http, ce vecteur d’exposition d’identifiants en clair existe presque toujours
Le mainteneur de pnpm avait proposé il y a un an de bloquer par défaut les scripts post-install
Du point de vue de l’utilisateur, cela aurait été inconfortable, mais il pensait que tout le monde finirait par s’en féliciter à long terme
PR associée : pnpm/pnpm#8897
C’est encore un cas où la praticité l’a emporté sur la sécurité
Ils ont dit que « la base de données n’avait pas été compromise », mais si l’attaquant a eu accès à AWS et aux secrets, alors pour moi il y a déjà eu compromission
S’il y avait une possibilité d’accès, cela doit être considéré comme une compromission
Une fois le malware exécuté, il devient presque impossible de remonter à la source
pnpm installse termine normalement, donc c’est difficile à détecterAvec un EDR comme Sentinel One ou CrowdStrike, il y aurait probablement eu plus de pistes pour l’enquête
Le passage disant « 669 repo ont été clonés au total » m’a marqué
Je me demande s’il est normal qu’une entreprise de moins de 100 employés ait plus de 600 repo
Le nombre de repo dépend davantage de l’ancienneté et du cycle de vie des projets que de la taille de l’équipe
pnpm a déjà cessé d’exécuter automatiquement les scripts de cycle de vie comme
preinstall, donc ils semblent avoir utilisé une ancienne versionVoir la PR associée
postinstallpnpm bloque les scripts des dépendances, mais exécute encore les scripts au niveau du projet
Merci d’avoir partagé un post-mortem transparent
Ce genre de cas est important pour toute l’industrie
Je me demande si le trafic d’attaque était identifiable par rapport au trafic de développement normal
Nous essayons nous aussi de renforcer le filtrage egress dans l’environnement de dev, mais
npm installcasse souvent, donc c’est compliquéJe réfléchis à la sécurité de git sur mon ordinateur portable personnel
Pour l’instant, je fais les push avec une clé SSH stockée en local
J’ai aussi des droits administrateur, donc c’est risqué. Je me demande comment rendre ça plus sûr
Cela permet de séparer les clés de signature et les clés d’accès, et de gérer séparément le compte administrateur
Documentation associée : 1Password SSH Agent, Git Commit Signing, GitHub OAuth, GitHub CLI Login
L’avantage, c’est que la clé ne peut pas être exfiltrée, mais si un malware tourne sur ma machine, cela reste dangereux
Exemple Linux, exemple macOS
On peut compliquer un peu les choses avec un TPM ou une Yubikey, mais une protection totale est impossible
Le plus sûr est d’effectuer les tâches d’administration sur une machine dédiée séparée
gpg-agentpour l’authentification SSHOn la déverrouille en saisissant un PIN au moment du push ou du commit
Le commit au nom de Torvalds était une signature fréquemment observée après infection
C’est aussi mentionné dans l’analyse officielle de Microsoft
Ce ver était très bruyant, et certains attaquants ont exploité des identifiants exposés pour rendre publics des repo privés ou modifier des readme à des fins promotionnelles
Je me demande si une telle détection aurait été possible si l’attaquant s’était contenté d’exfiltrer discrètement des informations sans sabotage