flawopen.com/Incidents/shai-hulud-npm-worm-september-2025
Shai-Hulud : le ver npm de septembre 2025 et la confiance lors de l’installation
En septembre 2025, la campagne Shai-Hulud a utilisé des comptes de mainteneurs npm compromis et des hooks postinstall malveillants pour voler des identifiants et publier d’autres paquets infectés.[1]
Imaginez une usine qui reçoit une caisse de pièces avec des instructions de déballage s’exécutant seules. Elles copient les badges d’expédition de l’usine et les utilisent pour envoyer des caisses truquées ailleurs. Désactiver ces instructions automatiques ferme cette entrée, mais les pièces doivent encore être inspectées avant emploi.[1]
Concepts Clés et Termes
Lifecycle hook- En septembre, le hook npm postinstall exécutait du code malveillant durant l’installation. C’est une fonction fournie par le paquet, pas une vulnérabilité mémoire de npm avec CVE.
Publishing credential- Un jeton npm accorde les droits configurés pour son compte ou son paquet. Les droits de publication volés ont permis la propagation ; durée, portée et MFA dépendent de la politique.
Trusted publishing / OIDC- OIDC permet à un workflow CI autorisé d’obtenir des droits de publication temporaires sans conserver de jeton npm. Un workflow autorisé compromis reste dangereux.
Correspondances de référentiels
Chronologie de l'Incident
GitHub indique avoir été averti de Shai-Hulud.[1]
GitHub a annoncé avoir supprimé plus de 500 paquets compromis et bloqué les téléversements correspondant aux indicateurs du programme.[1]
Déroulement de l'Attaque Étape par Étape
Code Source : Vulnérable vs Sécurisé
Comparaison sûre de plans d’installation en Python : aucune fonction ne lance npm, ne lit de secret ou ne contacte le registre. Elle suit la documentation npm v10. Le plan renforcé exige un verrou de dépendances relu, désactive les scripts de cycle de vie et ne transmet pas l’environnement hérité. Un véritable exécuteur doit fournir un npm connu, un répertoire personnel et une configuration isolés sans secrets ni volumes sensibles. --ignore-scripts ne sécurise ni les imports ultérieurs ni les scripts lancés explicitement ; un verrou ne certifie pas l’innocuité du code.
def install_plan(reviewed_lockfile, inherited_env):
# Renvoie uniquement un plan : aucun paquet n’est installé et aucune commande n’est exécutée.
return {"argv": ["npm", "ci"],
"env": dict(inherited_env)}
def install_plan(reviewed_lockfile, inherited_env):
# Renvoie uniquement un plan : aucun paquet n’est installé et aucune commande n’est exécutée.
if reviewed_lockfile is not True:
raise ValueError("Review the lockfile before installation")
return {"argv": ["npm", "ci", "--ignore-scripts"],
"env": {}}
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Exigez un verrou relu et npm ci --ignore-scripts dans la tâche d’installation ; testez la commande et l’absence de secrets dans l’environnement CI. Exécutez les hooks nécessaires après revue, dans une tâche isolée.[3]
- ✓Écartez les secrets de publication et du cloud de la tâche des dépendances, y compris fichiers personnels, volumes montés et identités de l’exécuteur. Testez qu’elle ne peut obtenir ces droits.[1], [2]
- ✓Utilisez la publication de confiance si possible, limitez workflow, branche et environnement autorisés, et revoyez leurs modifications. Exigez un MFA résistant à l’hameçonnage et révoquez les jetons inutilisés.[1]
- ✓En cas d’exposition confirmée, isolez et reconstruisez les exécuteurs, révoquez les secrets exposés et auditez versions et workflows. Examinez le code des dépendances : verrouillage et délai d’adoption ne garantissent pas la sécurité.[1], [2]