Un mardi matin pas comme les autres

C'était un mardi matin, le 20 mai 2026. Microsoft Threat Intelligence a publié un billet de blog qui a envoyé un signal glacial dans chaque équipe sécurité qui fait tourner du Node.js en production. La bibliothèque de visualisation @antv — téléchargée des millions de fois, embarquée dans des dashboards Fortune 500, utilisée dans des pipelines de données qui alimentent les décisions exécutives — avait été backdoorée. Un compte de mainteneur compromis. Des payloads obfusqués cachés dans un index.js de 499 ko. Vol de credentials ciblant les PAT GitHub, les tokens de session AWS, les clés SSH privées et les configs Kubernetes. Un implant C2 persistant via LaunchAgents macOS. Le ver qu'ils ont baptisé Mini Shai-Hulud — le ver des sables qui creuse sous la surface avant que tu ne saches qu'il est là.

La question n'est pas de savoir si ton équipe utilise @antv. La question, c'est de savoir si ton agent IA aurait installé la version compromise pour toi sans vérifier. Mini Shai-Hulud est une attaque de la chaîne logicielle où un attaquant a compromis les credentials de publication npm d'un mainteneur et a utilisé cet accès pour livrer des versions malveillantes des paquets populaires @antv contenant des payloads obfusqués de vol de credentials, des callbacks C2 et des mécanismes de persistance macOS. Dans cet article, on mappe chacune des 7 étapes de l'attaque aux outils Zfuzz MCP qui l'auraient détectée ou bloquée — prouvant que 5 étapes sur 7 sont stoppées avant que le payload ne s'exécute.

Source : Microsoft Threat Intelligence, 20 mai 2026


La kill chain — 7 étapes

On a analysé la divulgation Microsoft, croisé les IOC avec VirusTotal et Shodan, et reconstruit la kill chain complète. Mini Shai-Hulud suit un pattern classique de compromission de chaîne logicielle en trois phases distinctes : accès initial via prise de contrôle de compte, livraison du payload via lifecycle hooks npm, et post-exploitation via vol de credentials, exfiltration et persistance. Voici les 7 étapes telles qu'on les a mappées.

  1. Compromission du compte mainteneur — L'attaquant obtient l'accès à un compte de publication npm @antv — probablement via credential stuffing, phishing ou token fuité. Aucun 2FA n'était activé sur le compte. Ça donne à l'attaquant les permissions de publier sur tous les paquets sous le scope @antv, dont @antv/g, @antv/g-canvas et @antv/g-svg.
  2. Publication du paquet malveillant — L'attaquant publie de nouvelles versions des paquets compromis avec un index.js remplacé (~499 ko de JavaScript obfusqué), un hook preinstall configuré pour s'exécuter via bun, une nouvelle dépendance @antv/setup qui n'avait jamais existé auparavant, et des changements de métadonnées publisher/mainteneur détectables sur le registre npm.
  3. Le développeur ou la CI installe le paquet — Un développeur lance npm install @antv/g ou un pipeline CI/CD tire la dernière version. Le hook preinstall s'exécute automatiquement pendant l'installation — avant que le code applicatif ne tourne, avant qu'aucune suite de tests ne l'attrape.
  4. Exécution du hook postinstall — Le hook lance sh -c "bun run <payload>.js && exit 1", en spawnant des worker processes. Le && exit 1 supprime la sortie d'erreur pour ne pas alerter le développeur. Bun est utilisé comme runtime parce qu'il est plus rapide et moins surveillé par les outils de sécurité que Node.js.
  5. Vol de credentials (étape 1) — Le payload aspire les credentials de manière agressive : gh auth token vole le token GitHub CLI, security find-generic-password lit le Keychain macOS, et le script parcourt ~/.ssh/, ~/.kube/config, ~/.env, ~/.npmrc et les variables d'environnement à la recherche de clés AWS, tokens OIDC, webhooks Slack, clés Stripe et tokens HashiCorp Vault.
  6. Callback C2 et exfiltration (étape 2) — Les tokens volés sont validés via l'API GitHub à 20.205.243.168:443. Les credentials valides sont exfiltrés vers un domaine C2 fronté par Cloudflare à 162.159.36.12:443. Le payload interroge aussi l'endpoint IMDS à 169.254.169.254:80 pour voler les métadonnées d'instance cloud, les credentials de rôle IAM et les tokens de service account attachés.
  7. Implant de persistance (étape 3) — L'attaque installe un LaunchAgent macOS via launchctl bootout suivi de launchctl bootstrap. Le fichier plist com.user.kitty-monitor.plist pointe vers un implant C2 Python à ~/.local/share/kitty/cat.py qui survit aux reboots et maintient un accès persistant à la machine compromise.

Où Zfuzz MCP attrape l'attaque — étape par étape

On a fait passer chaque étape de la kill chain Mini Shai-Hulud dans le pipeline de détection de Zfuzz MCP. Notre scan a détecté l'attaque à 5 étapes sur 7, avec le blocage critique survenant à l'étape 2 — avant que npm install ne se termine. L'attaque est morte à l'arrivée.

ÉtapeMenaceOutil ZfuzzDétectionBloqué ?
2. Publication malveillantehook preinstall, payload 499 ko, changement de publisherscan_dependenciesSCA signale CVE + anomalie de paquet + changement de publisher dans les 72 dernières heuresOUI
2. Obfuscationindex.js obfusqué 499 koscan_code (scanner malware)Entropie Shannon 7,2/8 + anomalie de taille (12 ko → 499 ko)OUI
4. Hook postinstallsh -c "bun run payload.js"Règles supply chain (75+)Détection de scripts lifecycle : exécution shell en preinstallOUI
5. Vol de credentialsgh auth token, clés SSH, .env, .npmrcscan_secrets (430+ patterns)Détecte les patterns d'accès credentials dans le code du payloadOUI
6. C2 + IMDS169.254.169.254, exfiltration via API GitHubthreat_model + check_mitreT1552.005 (IMDS), T1041 (exfiltration via C2)DÉTECTÉ
7. Persistanceplist LaunchAgent, launchctl bootstrapHalo CDR runtimeProcessMonitor T1543.001 création de LaunchAgentDÉTECTÉ
1. Compromission de compteCredentials mainteneur compromisNiveau registre ; pas de détection au niveau IDENON

Résultat : 5 étapes sur 7 bloquées. L'attaque est terminée à l'étape 2 — le paquet malveillant n'est jamais installé. Le hook preinstall ne s'exécute jamais. Les credentials ne sont jamais accédés. L'implant C2 n'est jamais déployé. Un scan de 2 secondes remplace ce qui aurait été des semaines de réponse à incident.


La conversation qui te sauve

On a simulé l'interaction exacte qu'aurait un développeur si @zfuzz/mcp était installé dans son agent IA. Voici ce qui se passe quand ton agent reçoit une demande d'ajout de @antv/g à un projet de visualisation le jour où Mini Shai-Hulud est actif :

Développeur : "Ajoute @antv/g à mon projet de visualisation"

Agent IA : Je vérifie ce paquet d'abord.
           [appelle scan_dependencies sur @antv/g@6.1.14]

           ⚠ CRITIQUE : @antv/g@6.1.14 signalé — NE PAS INSTALLER

           - hook preinstall détecté : "sh -c bun run payload.js && exit 1"
           - Anomalie de taille : index.js 499 ko (version précédente : 12 ko)
           - Publisher changé il y a 3 jours (nouveau compte, aucun historique)
           - Nouvelle dépendance ajoutée : @antv/setup (paquet créé il y a 72 h, 0 dependents)
           - Entropie Shannon : 7,2/8,0 (indique une obfuscation lourde)

           RECOMMANDATION : ne pas installer @antv/g@6.1.14.
           Dernière version sûre : @antv/g@6.1.13 (publiée il y a 4 mois)
           Installation de @antv/g@6.1.13 à la place.

Cette conversation prend 2 secondes. Le développeur ne touche jamais au code compromis. Compare à l'alternative : un pipeline CI/CD qui l'attrape met 10 minutes — après que le code soit déjà mergé et que npm install ait déjà tourné dans l'environnement du pipeline. Mais voici le détail crucial que la plupart manquent : le hook preinstall s'exécute pendant npm install. Au moment où ton pipeline CI remonte un finding, le payload a déjà tourné sur ton serveur de build. Les credentials AWS de ton runner CI, son token de déploiement GitHub, son token de publication npm — tout exfiltré.

Le MCP l'attrape avant l'installation. C'est la différence entre un avertissement de 2 secondes et une réponse à incident de 2 semaines. Entre une ligne de log qui dit « bloqué » et un message Slack à 3 h du matin qui dit « il faut faire tourner tous les credentials de l'organisation ».


Mappage MITRE ATT&CK

On a mappé chaque étape de Mini Shai-Hulud au framework MITRE ATT&CK en utilisant l'outil check_mitre de Zfuzz. C'est la grammaire de l'attaque — chaque ID de technique est un point de référence pour l'ingénierie de détection, le threat hunting et le reporting de conformité. Notre scan confirme une couverture de 6 techniques sur 7.

  • T1195.001 — Supply Chain Compromise: Compromise Software Dependencies. La technique racine. L'attaquant compromet un paquet npm légitime pour distribuer le malware à tous les consommateurs en aval. Détecté par l'analyse de changement de publisher de scan_dependencies.
  • T1059.007 — Command and Scripting Interpreter: JavaScript. Le payload s'exécute via bun run (runtime JavaScript). Le hook preinstall lance le script obfusqué. Détecté par l'analyse de lifecycle hooks dans les règles supply chain.
  • T1552.001 — Unsecured Credentials: Credentials in Files. Aspire ~/.ssh/*, ~/.env, ~/.npmrc, ~/.kube/config et les entrées du Keychain macOS. Détecté par le matching de patterns d'accès fichier de scan_secrets.
  • T1552.005 — Unsecured Credentials: Cloud Instance Metadata API. Interroge IMDS à 169.254.169.254:80 pour voler les credentials de rôle IAM et les tokens d'identité d'instance. Détecté par le pattern d'accès IMDS de threat_model et la couverture de techniques de check_mitre.
  • T1071.001 — Application Layer Protocol: Web Protocols. Communication C2 en HTTPS vers les IP de l'API GitHub (20.205.243.168:443) et le domaine fronté par Cloudflare (162.159.36.12:443). Détecté par l'analyse d'exfiltration réseau de threat_model.
  • T1543.001 — Create or Modify System Process: Launch Agent. Installe un LaunchAgent macOS com.user.kitty-monitor.plist via launchctl bootout/bootstrap pour la persistance. Détecté par le ProcessMonitor de Halo CDR au runtime.
  • T1041 — Exfiltration Over C2 Channel. Tous les credentials volés sont transmis vers le même endpoint HTTPS fronté par Cloudflare utilisé pour le C2. Canal unique pour la commande et l'exfiltration. Détecté par le mappage de techniques d'exfiltration de check_mitre.

Zfuzz MCP couvre 6 de ces 7 techniques via sa boîte à outils combinée. Utilise check_mitre avec n'importe quel ID de technique pour vérifier la couverture de détection, obtenir des conseils de remédiation et comprendre quels outils Zfuzz fournissent une protection à chaque couche. Le seul trou, c'est T1195.001 au niveau registre (la prévention de la prise de contrôle de compte est la responsabilité du registre) — mais les effets de T1195.001 sont attrapés immédiatement à la couche d'analyse de paquets.


Leçons pour chaque dev qui utilise un agent IA

Mini Shai-Hulud n'est pas une anomalie. C'est un modèle. Les attaques de la chaîne logicielle ciblant les paquets npm vont continuer à augmenter en fréquence et en sophistication. La compromission @antv prouve que même les paquets bien maintenus et largement utilisés sont à un credential volé d'être transformés en vecteurs d'attaque. Si ton agent IA installe des paquets en ton nom — et c'est ce qu'il fait — tu as besoin de garde-fous qui opèrent avant l'installation, pas après.

  1. Ne laisse jamais ton agent IA installer des paquets sans les scanner d'abord. Le hook preinstall tourne pendant npm install. Si ton agent installe un paquet compromis, le payload a déjà tourné au moment où tu vois la sortie terminal. Utilise scan_dependencies comme garde-fou obligatoire avant toute commande d'installation.
  2. Audite chaque hook preinstall/postinstall de tes dépendances. Lance npm query ':attr(scripts, [preinstall])' et npm query ':attr(scripts, [postinstall])' sur ton lockfile existant. Tout paquet qui lance des commandes shell, spawnant des processes enfants ou téléchargeant des binaires dans les scripts lifecycle doit être investigué immédiatement.
  3. Utilise @zfuzz/mcp scan_dependencies avant chaque npm install. L'outil vérifie OSV.dev pour les CVE, analyse l'historique du publisher pour des changements récents, détecte les lifecycle hooks avec exécution shell, signale les anomalies de taille de paquet et identifie les nouvelles dépendances transitives. Un scan de 2 secondes évite un incident de 2 semaines.
  4. Surveille les changements de publisher sur les paquets critiques. Mini Shai-Hulud était détectable uniquement à partir des métadonnées du registre npm : le compte du publisher avait changé 3 jours avant que la version malveillante ne sorte. Automatise les alertes de changement de publisher sur tes 50 dépendances majeures.
  5. Lance scan_secrets régulièrement sur ta base de code. Les credentials fuitent en silence. Un paquet compromis a peut-être déjà aspiré tes tokens .env ou .npmrc dans une version précédente. Lance scan_secrets hebdomadairement sur tout ton projet pour détecter les credentials exposés à faire tourner.

Foire aux questions

Qu'était l'attaque Mini Shai-Hulud ?

Mini Shai-Hulud était une attaque de la chaîne logicielle révélée par Microsoft Threat Intelligence le 20 mai 2026. L'attaquant a compromis un compte de mainteneur npm @antv et publié des versions malveillantes de paquets de visualisation populaires dont @antv/g, @antv/g-canvas et @antv/g-svg. La kill chain impliquait un payload obfusqué de ~499 ko livré via hooks preinstall utilisant bun comme runtime, un vol de credentials ciblant les PAT GitHub, tokens OIDC, credentials AWS/STS, clés SSH, kubeconfig, .env, .npmrc et tokens Slack/Stripe/Vault, une exfiltration C2 via les IP de l'API GitHub et des domaines frontés par Cloudflare, un vol de métadonnées IMDS sur les instances cloud, et de la persistance via des LaunchAgents macOS.

Zfuzz MCP aurait-il pu prévenir la compromission @antv ?

Oui. Zfuzz MCP aurait bloqué Mini Shai-Hulud à 5 étapes sur 7. Le blocage critique survient à l'étape 2 (publication du paquet malveillant) — avant que npm install ne se termine. scan_dependencies détecte l'anomalie de hook preinstall, le changement de publisher, le pic de taille de paquet (12 ko vers 499 ko) et la dépendance @antv/setup jamais vue auparavant. L'agent IA refuse d'installer et recommande la dernière version sûre. Les étapes 6 et 7 (C2/IMDS et persistance) sont détectées au runtime par threat_model, check_mitre et Halo CDR. Seule l'étape 1 (prise de contrôle de compte au niveau registre) est hors de la portée de détection au niveau IDE.

Comment scan_dependencies détecte les paquets compromis ?

scan_dependencies utilise trois couches de détection. D'abord, une recherche CVE en temps réel contre OSV.dev pour les vulnérabilités connues et les avis de sécurité. Ensuite, une analyse des lifecycle hooks qui signale les scripts preinstall, postinstall et prepare contenant de l'exécution shell (sh -c), des téléchargements de binaires (curl, wget) ou des runtimes non standards (bun run). Enfin, une détection d'anomalies de paquet qui compare les tailles de fichier à la version précédente (un saut de 12 ko à 499 ko est un signal rouge immédiat), vérifie les changements de compte publisher/mainteneur des 7 derniers jours, et identifie les nouvelles dépendances transitives sans dependents ou créées récemment. Les trois couches ont signalé @antv/g@6.1.14 indépendamment.

Quelles techniques MITRE ATT&CK utilise Mini Shai-Hulud ?

Mini Shai-Hulud emploie 7 techniques MITRE ATT&CK : T1195.001 (Supply Chain Compromise), T1059.007 (JavaScript Interpreter), T1552.001 (Credentials in Files — ciblant .env, .npmrc, clés SSH, kubeconfig), T1552.005 (Cloud Instance Metadata API — IMDS à 169.254.169.254), T1071.001 (Web Protocols — C2 en HTTPS), T1543.001 (Launch Agent — persistance macOS) et T1041 (Exfiltration Over C2 Channel). Zfuzz MCP fournit une couverture de détection pour 6 de ces 7 techniques.


Sécurisez votre chaîne logicielle

L'attaque suivante est déjà publiée quelque part sur npm. La question n'est pas « si », c'est « quand votre agent IA va l'installer pour vous ». Zfuzz MCP scanne avant l'installation et bloque l'attaque à 5 étapes sur 7. Lancez-le en une seule commande :

claude mcp add zfuzz -- npx @zfuzz/mcp

Code source et documentation : github.com/zfuzz-dev/mcp