Skip to main content

Mini Shai-Hulud frappe AntV : plus de 300 paquets npm malveillants publiés via le compte compromis d’un mainteneur

Écrit par
blog feature supply chain sbom

18 mai 2026

0 minutes de lecture

Une attaque de la chaîne d’approvisionnement visant l’écosystème de visualisation de données @antv et des paquets npm associés se propage actuellement dans le registre npm. Attribuée à un groupe malveillant appelé TeamPCP et présentée comme une nouvelle vague de la campagne Mini Shai-Hulud, l’attaque a publié plus de 300 versions malveillantes réparties sur 323 paquets lors d’une vague automatisée de 22 minutes, le 19 mai 2026. Ces paquets représentent au total environ 16 millions de téléchargements par semaine.

Le vecteur d’attaque était un compte npm de mainteneur compromis. Le malware intégré aux paquets concernés dérobe les secrets des développeurs et les identifiants cloud, établit un accès C2 persistant et tente de se propager à d’autres paquets à l’aide de jetons npm volés.

En bref

Type d’attaque

Chaîne d’approvisionnement, compte de mainteneur compromis

Acteur malveillant

TeamPCP (alias : DeadCatx3, PCPcat)

Campagne

Mini Shai-Hulud (en cours depuis sept. 2025)

Date de l’incident

19 mai 2026, 01:39–02:06 UTC

Paquets compromis

637 versions malveillantes réparties sur 323 paquets

Téléchargements hebdomadaires estimés

~16 millions

Comportement du malware

Vol d’identifiants, collecte de secrets cloud, persistance, propagation du ver

Couverture Snyk

Avis dans la Snyk Vulnerability Database ; rapport Zero Day disponible dans l’application

Mesures immédiates

Revenir à des versions antérieures au 19 mai, exécuter npm install --ignore-scripts, renouveler tous les identifiants

Paquets concernés

Le compte npm compromis atool gérait 547 paquets. La fenêtre de publication malveillante en a touché plus de 300, en deux vagues :

  • Première vague : 01:39–01:56 UTC (~317 versions)

  • Deuxième vague : 02:05–02:06 UTC (~314 versions)

Parmi les paquets concernés, voici ceux qui enregistrent le plus de téléchargements :

Parmi les principaux paquets @antv compromis figurent @antv/g2, @antv/g6, @antv/x6, @antv/l7, @antv/s2, @antv/f2, @antv/g, @antv/g2plot, @antv/graphin et @antv/data-set, ainsi que des paquets sans portée, comme echarts-for-react, timeago.js, size-sensor et canvas-nest.js.

AntV est une suite de visualisation de données issue d’Alibaba, largement utilisée dans les tableaux de bord d’entreprise, les outils de reporting financier et les plateformes d’analyse de graphes. Son adoption étendue fait du portefeuille de paquets du compte de mainteneur une cible particulièrement intéressante.

Fonctionnement de l’attaque

Étape 1 : compromission du compte de mainteneur

L’attaque commence par la compromission du compte npm atool. Les circonstances de cette compromission font toujours l’objet d’une enquête. Une fois le compte sous son contrôle, l’attaquant pouvait publier les 547 paquets qu’il gérait.

Étape 2 : publication automatisée de paquets malveillants

L’attaquant a publié des versions malveillantes en deux vagues rapprochées, en poussant la plupart des paquets deux fois (un petit nombre de paquets utilisés lors des premiers tests a reçu trois versions). Chaque archive tar de paquet malveillant contient deux ajouts :

  • Un fichier index.js: à la racine : une charge utile JavaScript Bun de 498 Ko fortement obfusquée

  • Une modification de package.json ajoutant : "preinstall": "bun run index.js"

Le hook de cycle de vie preinstall se déclenche automatiquement lorsqu’un développeur exécute npm install, avant toute autre opération d’installation.

Étape 3 : injection d’un commit orphelin pour la provenance Sigstore

L’une des techniques les plus subtiles de cette vague consiste à injecter une dépendance facultative pointant vers un commit orphelin dans le dépôt légitime antvis/G2 :

"optionalDependencies": {
  "@antv/setup": "github:antvis/G2#1916faa365f2788b6e193514872d51a242876569"
}

Il est important de noter que la dépendance provenant de GitHub reprend la même méthode d’attaque que celle utilisée lors de la précédente attaque de la chaîne d’approvisionnement TanStack Shai-Hulud.

Le commit a été créé par l’attaquant, mais falsifié pour sembler provenir de huiyu.zjt <huiyu.zjt@ant.com> (un véritable mainteneur). Aucun accès en écriture au dépôt ciblé n’était nécessaire. L’attaquant a forké antvis/G2, créé un commit orphelin contenant la charge utile, puis supprimé le fork. Le stockage d’objets de GitHub conserve les commits des forks supprimés jusqu’au passage du ramasse-miettes, ce qui laisse le commit malveillant accessible par son hash.

La récupération de ce commit permet d’exécuter la charge utile avec des identifiants issus de Git, tout en donnant l’impression de faire référence à un dépôt de confiance.

À l’aide de jetons OIDC GitHub Actions volés, le malware peut ensuite demander des certificats de signature à Fulcio (https://fulcio.sigstore.dev) et créer des attestations de provenance in-toto via Rekor (https://rekor.sigstore.dev), produisant ainsi des paquets accompagnés d’attestations SLSA Build Level 3 cryptographiquement valides.

Le point essentiel, ici, est que les signatures sont légitimes parce que le pipeline de build lui-même a été compromis. La provenance Sigstore indique quel pipeline a produit un artefact, mais pas si ce pipeline fonctionnait comme prévu. Il est donc erroné de croire que les preuves d’attestation, à elles seules, garantissent l’authenticité des paquets publiés.

Étape 4 : collecte des identifiants

Lorsque bun run index.js s’exécute sur la machine d’un développeur ou dans un exécuteur CI, la charge utile cible plus de 80 variables d’environnement et plus de 100 chemins de fichiers. Les types d’identifiants ciblés comprennent :

  • AWS : clés d’accès (AKIA[0-9A-Z]{16}), jetons de session, EC2 IMDS (169.254.169.254), métadonnées ECS (169.254.170.2), Secrets Manager

  • GCP : fichiers JSON de comptes de service, identifiants par défaut des applications

  • Azure : identifiants de principaux de service

  • GitHub : PAT et jetons OIDC (gh[op]_[A-Za-z0-9]{36,})

  • npm : jetons de publication avec la portée bypass_2fa

  • Infrastructure : jetons de service Kubernetes, jetons HashiCorp Vault

  • Bases de données : chaînes de connexion MongoDB, MySQL, PostgreSQL et Redis

  • Services : clés Stripe, jetons Slack, configurations d’authentification Docker

  • Clés SSH : ~/.ssh/id_*

Dans les environnements GitHub Actions, la charge utile tente de lire directement les secrets dans la mémoire du processus Runner.Worker via /proc/{pid}/mem, en contournant complètement le masquage des secrets.

Étape 5 : exfiltration des données

Tous les identifiants collectés sont sérialisés au format JSON, compressés avec gzip, chiffrés avec AES-256-GCM, puis la clé de chiffrement est protégée à l’aide de RSA-OAEP et d’une clé publique de l’attaquant codée en dur. Les défenseurs qui découvrent les données exfiltrées ne peuvent donc pas savoir ce qui a été volé.

L’exfiltration emprunte deux canaux :

  1. C2 principal : https://t[.]m-kosche[.]com:443/api/public/otel/v1/traces (dissimulé sous forme de données de traces OpenTelemetry)

  2. Point de dépôt GitHub secondaire : si un jeton GitHub est trouvé, la charge utile crée des dépôts sur le compte de la victime avec des noms inspirés de Dune (combinaisons de mots comme sardaukar, fremen, atreides avec sandworm, ornithopter ou stillsuit, suivies d’un nombre de 0 à 999). Ces dépôts contiennent des fichiers README avec des phrases caractéristiques des vagues précédentes de la campagne Shai-Hulud, écrites à l’envers (par exemple : « niagA oG eW ereH :duluH-iahS »). Les données volées sont ajoutées sous le nom results/results-<timestamp>-<counter>.json. Les requêtes HTTP utilisent "python-requests/2.31.0" comme User-Agent.

Étape 6 : persistance

La charge utile installe plusieurs mécanismes de persistance qui résistent à une simple commande npm uninstall :

  • Détournement d’agents de codage IA : crée un fichier .claude/settings.json contenant un hook SessionStart qui exécute node .claude/setup.mjs. Le malware s’exécute à nouveau chaque fois qu’un développeur ouvre une nouvelle session Claude Code dans un répertoire concerné. Notons qu’il s’agit d’un schéma similaire à celui que nous avons observé lors de précédentes attaques de la chaîne d’approvisionnement, comme TanStack.

  • Hooks d’IDE : modifie .vscode/tasks.json avec "runOn": "folderOpen" pour se déclencher à l’ouverture du projet.

  • Service au niveau du système d’exploitation : installe ~/.local/share/kitty/cat.py, un service d’arrière-plan qui interroge toutes les heures l’API de recherche de commits GitHub à la recherche de commandes contenant le mot-clé firedalazer. Les commandes sont signées avec RSA-PSS à l’aide d’une clé publique codée en dur de 4096 bits, ce qui permet à l’attaquant d’envoyer du code Python arbitraire à exécuter sur les machines compromises, longtemps après l’infection initiale. Le service est enregistré comme service utilisateur systemd sous Linux (~/.config/systemd/user/kitty-monitor.service) et comme LaunchAgent sous macOS (~/Library/LaunchAgents/com.user.kitty-monitor.plist).

Surveillance des jetons : ~/.local/bin/gh-token-monitor.sh vérifie les jetons GitHub volés toutes les 60 secondes, permettant à l’attaquant de réagir rapidement lorsqu’un jeton est sur le point d’expirer.

Étape 7 : propagation du ver

La charge utile recherche les jetons npm disposant de la portée bypass_2fa et les utilise pour republier d’autres paquets que le compte compromis est autorisé à publier. Dans les environnements GitHub Actions, elle échange le jeton OIDC Actions contre des jetons npm de publication propres à chaque paquet via :

POST https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/<package-name>

Elle injecte également un workflow GitHub Actions dans une branche nommée chore/add-codeql-static-analysis, avec un fichier de workflow intitulé "Run Copilot" (.github/workflows/codeql.yml). Le workflow exporte toJSON(secrets) dans un artefact nommé format-results.txt, puis supprime ses traces en effaçant l’exécution du workflow.

Analyse de l’impact

Le périmètre d’impact direct comprend tout environnement de développeur ou de CI ayant exécuté npm install avec une version de paquet concernée entre 01:39 et environ 02:18 UTC le 19 mai 2026.

Les environnements CI/CD présentent un risque accru. Dans ces contextes, la charge utile peut lire tous les secrets du processus de l’exécuteur, et pas seulement ceux explicitement transmis comme variables d’environnement. Tout secret auquel l’exécuteur GitHub Actions a accès — notamment les jetons OIDC, les secrets du dépôt et les secrets de l’organisation associés à ce dépôt — doit être considéré comme compromis si une version concernée a été installée.

Les machines des développeurs présentent également un risque important. Les mécanismes de persistance font que la suppression des paquets concernés ne suffit pas à éliminer la menace. Le hook .claude/settings.json, la tâche VS Code et le service système continuent tous de fonctionner tant qu’ils ne sont pas supprimés explicitement.

Le composant d’auto-propagation signifie que tout jeton npm récupéré sur une machine de développeur ou un exécuteur CI pourrait servir à contaminer d’autres paquets en dehors du portefeuille initial du compte atool, étendant ainsi le périmètre d’impact au-delà d’AntV et des paquets associés.

Détection

Vérifiez votre fichier de verrouillage. Si votre fichier package-lock.json ou yarn.lock référence un paquet géré par le compte atool, vérifiez si la version résolue a été publiée entre 01:39 et 02:18 UTC le 19 mai 2026.

Analysez vos projets avec Snyk. Snyk a publié des avis dans la Snyk Vulnerability Database concernant les versions de paquets affectées et a déployé une notification dans l’application ainsi qu’un rapport Zero Day pour aider ses clients à analyser leur exposition à l’échelle de leur organisation.

snyk test

Pour analyser rapidement un paquet précis dans votre arborescence :

snyk test --file=package-lock.json

Recherchez les traces de persistance. Si vous avez installé des paquets concernés, vérifiez la présence des éléments suivants :

# AI agent hook
cat .claude/settings.json 2>/dev/null | grep -A5 SessionStart

# VS Code task hook
cat .vscode/tasks.json 2>/dev/null | grep "folderOpen"

# C2 daemon
ls ~/.local/share/kitty/cat.py 2>/dev/null
ls ~/.local/bin/gh-token-monitor.sh 2>/dev/null
systemctl --user status kitty-monitor 2>/dev/null  # Linux
launchctl list com.user.kitty-monitor 2>/dev/null   # macOS

# Dead-drop repositories on your GitHub account
gh repo list --json name,description | grep -E "sardaukar|mentat|fremen|atreides|harkonnen|gesserit|fedaykin|tleilaxu"

Indicateurs au niveau des paquets :

  • Présence du script preinstall bun run index.js dans node_modules/<package>/package.json

  • SHA256 de la charge utile : a68dd1e6a6e35ec3771e1f94fe796f55dfe65a2b94560516ff4ac189390dfa1c

  • Dépendance facultative faisant référence à github:antvis/G2#1916faa365f2788b6e193514872d51a242876569 (ou aux commits 7cb42f57561c / dc3d62a2181b)

Indicateurs réseau :

  • Requêtes sortantes vers 169.254.169.254 ou 169.254.170.2 (points de terminaison des métadonnées cloud) depuis des environnements hors cloud

  • Requêtes HTTP vers t.m-kosche.com:443 avec des chemins OpenTelemetry

  • Appels à l’API GitHub avec l’User-Agent python-requests/2.31.0 ne provenant pas de véritables processus Python

Mesures correctives

Si vous ne savez pas si vous avez été touché, considérez qu’il s’agit d’une compromission avérée. Le chiffrement RSA des données exfiltrées vous empêche de récupérer ce qui a été dérobé.

Étape 1 : supprimez les mécanismes de persistance avant de révoquer les jetons.

Le démon gh-token-monitor.sh vérifie les jetons toutes les 60 secondes. Si un jeton GitHub expire ou est révoqué alors que le démon est en cours d’exécution, cela peut déclencher d’autres actions malveillantes. Arrêtez et supprimez d’abord les mécanismes de persistance :

# Remove systemd service (Linux)
systemctl --user stop kitty-monitor
systemctl --user disable kitty-monitor
rm -f ~/.config/systemd/user/kitty-monitor.service

# Remove LaunchAgent (macOS)
launchctl unload ~/Library/LaunchAgents/com.user.kitty-monitor.plist
rm -f ~/Library/LaunchAgents/com.user.kitty-monitor.plist

# Remove C2 daemon and monitor
rm -f ~/.local/share/kitty/cat.py
rm -f ~/.local/bin/gh-token-monitor.sh

# Remove editor hooks
rm -f .claude/settings.json  # or edit to remove SessionStart hook
# Edit .vscode/tasks.json to remove any "runOn": "folderOpen" tasks

# Check for injected GitHub workflows
git log --oneline --all | grep -i codeql

Étape 2 : nettoyez npm et réinstallez avec des versions saines.

# Remove node_modules
rm -rf node_modules

# Downgrade affected packages to pre-May 19 versions in package.json, then:
npm install --ignore-scripts

L’option --ignore-scripts empêche l’exécution des scripts du cycle de vie preinstall, postinstall ou prepare pendant l’installation. Elle devrait de toute façon être utilisée par défaut dans les environnements CI.

Étape 3 : renouvelez tous vos identifiants.

Partez du principe que tout ce qui est accessible depuis une machine ou un exécuteur CI ayant installé un package concerné est compromis :

  • Jetons de publication npm

  • Jetons d’accès personnels GitHub et secrets Actions

  • Clés d’accès AWS (et tous les rôles IAM accessibles depuis les exécuteurs concernés)

  • Clés de comptes de service GCP

  • Principaux de service Azure

  • Jetons de comptes de service Kubernetes

  • Jetons HashiCorp Vault

  • Clés SSH présentes sur les machines concernées

  • Chaînes de connexion aux bases de données

  • Toutes les clés API ou tous les jetons de service stockés dans des variables d’environnement ou des fichiers de configuration

Étape 4 : vérifiez sur GitHub la présence de workflows injectés et de dépôts de dépôt clandestin.

# Check for attacker-injected branches
git branch --all | grep "codeql-static-analysis"

# Check for Dune-named repositories created on your account
gh repo list --json name | grep -E "sardaukar|mentat|fremen|atreides|harkonnen"

# Check npm audit log for unexpected publishes
npm access ls-packages

Étape 5 : évitez toute exposition future.

  • Activez l’authentification à deux facteurs npm avec la protection de publication pour tous les packages de votre organisation.

  • Ajoutez npm install --ignore-scripts à la configuration CI par défaut.

  • Verrouillez les dépendances sur des versions précises à l’aide de fichiers de verrouillage avec vérification de l’intégrité.

  • Envisagez une politique de mise en attente dans le registre : signalez et retenez les packages publiés au cours des 7 derniers jours.

  • Utilisez Snyk pour surveiller en continu votre arbre de dépendances et repérer les packages malveillants dès qu’ils sont signalés.

  • Nous vous recommandons vivement de consulter le dépôt des bonnes pratiques de sécurité npm et de les appliquer afin d’adopter les contrôles et pratiques de sécurité nécessaires pour éviter de futurs incidents liés aux logiciels malveillants.

La campagne dans son ensemble : les vagues Shai-Hulud

L’attaque contre AntV est la dernière vague d’une campagne menée par TeamPCP depuis septembre 2025. Son évolution témoigne d’une escalade constante de son ampleur, de sa persistance, de sa sophistication et de l’exploitation d’infrastructures de confiance :

Vague

Date

Cible principale

Ampleur

Technique caractéristique

Vague 1 (Shai-Hulud)

Sept. 2025

npm (général)

~4 packages

Premier ver npm à propagation autonome

Vague 2 (SHA1-Hulud)

Nov. 2025

Zapier, Posthog, Postman

Plus de 600 packages

Évasion de conteneurs ; logiciel destructeur d’effacement

Vague 3 (Mini, SAP)

Avr. 2026

SAP CAP-JS, MBT

4 packages

Injection d’un hook Claude Code SessionStart

Vague 4 (TanStack)

11 mai 2026

@tanstack/*

84 versions/42 packages

Première provenance SLSA valide obtenue par détournement d’OIDC

Vague 5 (AntV)

19 mai 2026

@antv/* + 310 autres

637 versions/323 packages

Compte de responsable de maintenance compromis ; C2 étendu

Snyk a analysé en détail les vagues précédentes :

Chaque vague a réutilisé et enrichi la charge utile obfusquée basée sur l’environnement d’exécution Bun, en ajoutant de nouveaux mécanismes de persistance (le hook Claude Code SessionStart est apparu pour la première fois lors de la vague 3), de nouvelles infrastructures d’exfiltration et de nouvelles méthodes pour falsifier une provenance de confiance. Le problème de provenance SLSA mérite tout particulièrement votre attention : une attestation Sigstore valide confirme quel pipeline a produit un package, mais pas si ce pipeline a été compromis. Se fier à la provenance comme signal de confiance sans auditer également la configuration du pipeline sous-jacent laisse une faille que cette campagne a désormais exploitée lors de deux vagues consécutives.

Pour découvrir en vidéo comment utiliser Snyk pour remédier aux problèmes liés à l’ensemble de la campagne Shai-Hulud :

Shai-Hulud NPM Attack: Remediation with Snyk

Attaque Shai-Hulud contre NPM : mesures correctives avec Snyk — Présentation de l’identification et de la correction des packages compromis à l’aide des outils Snyk.

Chronologie de l’attaque (19 mai 2026, UTC)

Heure

Événement

01:39

Publication de la première version malveillante depuis le compte atool

01:56

Fin de la première vague de publications (~317 versions)

02:05

Début de la deuxième vague

02:06

Fin de la deuxième vague (~314 versions supplémentaires)

~02:18

Détection ; les chercheurs en sécurité commencent à signaler l’incident

En cours

L’enquête se poursuit ; d’autres packages pourraient être identifiés

Couverture par Snyk

Snyk a publié des avis concernant les versions de packages touchées dans la Snyk Vulnerability Database. Les clients Snyk peuvent utiliser le rapport Zero Day intégré à l’application pour identifier les projets concernés au sein de leur organisation. Les entrées de la Snyk Vulnerability Database consacrées aux packages concernés présentent leur état de santé actuel.

Pour en savoir plus sur cette catégorie d’attaques, la leçon de Snyk Learn sur la compromission de packages légitimes explique comment la compromission d’un compte de responsable de maintenance s’inscrit dans le contexte plus large des menaces visant la chaîne logistique logicielle.

Sécurisez votre chaîne d’approvisionnement avec Snyk

87 % des personnes interrogées ont été touchées par des problèmes de sécurité de la chaîne d’approvisionnement. Protégez la vôtre avec Snyk.