CI/CD a publié le chargeur de botnet AsyncAPI
Quatre paquets npm AsyncAPI publiés le 14 juillet 2026 avec une provenance OIDC valide ont pourtant livré un chargeur multi-étapes de botnet. L’intrusion a débuté dans l’intégration continue (CI) : un workflow pull_request_target mal configuré, suivi d’un push sur la branche de release via des identifiants liés à un bot. La fonctionnalité npm Trusted Publishing est restée techniquement intègre et n’a pas détecté cette faille en amont.
Points clés
- L’intrusion a débuté par une mauvaise configuration de la CI, bien avant le jeton de registre. Le workflow pull_request_target avec checkout de la branche non fiable, un GITHUB_TOKEN trop permissif et un push ultérieur par un bot : l’exfiltration de secrets et la compromission de branche vont de pair.
- La charge utile dans le require(), l’exécution active était plus étroite que le bundle. ShellExec, persistance et C2 fonctionnaient. Le harvesting, la propagation et l’évasion étaient inclus dans le paquet, mais désactivés dans cette livraison via un interrupteur.
- Les fichiers de verrouillage (lockfiles) entre 07:10 et 11:18 UTC restent risqués. npm a supprimé les versions concernées ; les références existantes et les caches CI ne le sont pas.
Articles associés :Un paquet npm ayant volé des clés privées / Mini Shai-Hulud : ver npm dans la chaîne d’approvisionnement et contre-mesures
Ce qui s’est exactement passé
Le 14 juillet 2026 à 06:58 UTC, un commit a été poussé sur la branche *next* du dépôt asyncapi/generator sous l’identité factice « Votre nom » / you@example.com. Douze minutes plus tard, le workflow légitime release-with-changesets.yml a publié trois paquets avec une provenance OIDC npm valide : @asyncapi/generator@3.3.1, @asyncapi/generator-helpers@1.1.1 et @asyncapi/generator-components@0.7.1.
Selon le blog de sécurité de Microsoft, l’accès initial s’est produit via la PR #2155 contre le workflow manual-netlify-preview.yml. Ce workflow utilisait pull_request_target et vérifiait le commit non fiable (untrusted) en provenance de la tête de la branche. Le GITHUB_TOKEN disposait de privilèges étendus ; les identifiants de checkout sont restés dans la configuration Git locale jusqu’au nettoyage post-job. Par la suite, des pushs ont été effectués depuis le compte asyncapi-bot. Microsoft reste prudent quant à la confirmation que ces identifiants ont été précisément volés. Cependant, l’enchaînement de l’attaque via la requête pwn et les pushs ultérieurs via le bot sont avérés et expliquent l’accès à la branche de release.
Entre 07:51 et 08:30 UTC environ, le même attaquant a ciblé asyncapi/spec-json-schemas (branche master). Via des commits avec le préfixe fix:, il a déclenché if-nodejs-release.yml et livré @asyncapi/specs@6.11.2-alpha.1 ainsi que @asyncapi/specs@6.11.2. StepSecurity, Socket, SafeDep et OX Security ont documenté la même infrastructure de dropper et de deuxième niveau (selon StepSecurity : famille Miasma-v3).
Pour contextualiser : aucun token npm de publication n’a été volé. Le processus de publication a emprunté le chemin OIDC Trusted Publishing. La faille de confiance se situe en amont, dans la CI et dans les droits de push sur la branche déclenchant le workflow de release.
Fenêtre d’exposition jusqu’au retrait des paquets du générateur
Source : Chronologie StepSecurity, 14.07.2026 UTC
Pourquoi la « provenance valide » peut être trompeuse dans ce cas
Le système npm Trusted Publishing avec OIDC et les attestations SLSA répondent à une question précise : cet artefact a-t-il été généré par le workflow GitHub autorisé de ce dépôt ? L’attestation des paquets générateurs indiquait exactement repo:asyncapi/generator:ref:refs/heads/next, le fichier du workflow, le commit SHA et l’URL de l’exécution.
En revanche, elle ne répond pas à une autre question cruciale : le commit ayant déclenché le workflow était-il légitime et intègre ? La provenance ne protège pas contre des identifiants de push compromis ni contre des jobs CI exécutant du code non fiable avec des jetons privilégiés. Lorsque pull_request_target est combiné avec un checkout de la tête de la PR, que des secrets sont laissés dans ces jobs et que la protection des branches est faible sur les branches de release, c’est précisément cette faille qui est exploitée.
Pour les RSSI (CISO) et les équipes AppSec, c’est le levier central : les signatures de registre et les indicateurs de provenance évaluent le cas AsyncAPI comme « vert », alors que la CI a déjà livré le dropper. Les contrôles doivent vérifier la source de vérité ainsi que les permissions des workflows.
Mécanisme de charge utile sans hook d’installation
Aucun des fichiers package.json concernés ne contenait de script preinstall/postinstall. Le dropper s’activait dès que le code du module empoisonné était chargé via require(), c’est-à-dire lors de l’exécution normale du générateur ou dans les tâches CI construisant des templates AsyncAPI.
L’étape 1 lance un processus Node détaché. L’étape 2 récupère depuis une passerelle IPFS un fichier sync.js, stocké dans des chemins spécifiques au système d’exploitation, se faisant passer pour une runtime NodeJS inoffensive (sous Linux, par exemple ~/.local/share/NodeJS/sync.js). L’étape 3 déchiffre un implant Miasma-v3 intégré. Dans la configuration livrée, les fonctions de shell à distance (ShellExec), de persistance et de balise C2 (command & control) s’exécutaient via plusieurs canaux (HTTP, Nostr, IPFS, DHT BitTorrent, dead-drop Ethereum).
Les analyses statiques menées par Aikido et Socket révèlent également des modules dans le bundle qui étaient désactivés dans cette livraison via des commutateurs : recon : false (le vol d’identifiants ne démarre pas), metamorphic : false (mutation, évasion et empoisonnement d’outils d’IA désactivés) ainsi que propagate pour npm/pypi/ruby/cargo systématiquement à false (aucune propagation latérale de paquets). Le magasin de l’artefact contenait l’implant. Cette vague d’attaque se limitait à une coquille distante avec persistance et C2. L’analyse forensique des hôtes reste indispensable, car l’accès shell et la persistance étaient activés.
| Paquet | Version malveillante | Version sécurisée |
|---|---|---|
| @asyncapi/generator | 3.3.1 | 3.3.0 |
| @asyncapi/generator-helpers | 1.1.1 | 1.1.0 |
| @asyncapi/generator-components | 0.7.1 | 0.7.0 / 1.0.0 |
| @asyncapi/specs | 6.11.2 / 6.11.2-alpha.1 | 6.11.1 |
Source : Tableau des paquets affectés de StepSecurity, au 14.07.2026
Ce que les équipes sécurité doivent vérifier dès maintenant
Commencez par une vérification des faits dans votre propre environnement : examinez les fichiers de verrouillage (Lockfiles) et les caches CI dans la fenêtre UTC du 14 juillet. Si vous utilisez @asyncapi/specs uniquement de manière transitive via le parseur, la version n’apparaît souvent pas dans l’arbre de dépendances direct. Les overrides et les analyses SBOM (Software Bill of Materials) sont ici obligatoires.
Ensuite, procédez à une analyse forensique des hôtes : recherchez les chemins de dépôt (Drop-paths) sous le nom trompeur « NodeJS », les processus Node détachés, les connexions sortantes vers des passerelles IPFS et des cibles HTTP-C2 inhabituelles. Troisièmement, effectuez une rotation des identifiants (Credentials) sur les runners et ordinateurs portables concernés. ShellExec peut accéder aux secrets d’environnement et aux tokens locaux, même si le module de récolte groupé était désactivé dans cette configuration. Priorité absolue : les GitHub PATs, les tokens npm et les clés cloud issues des environnements de build.
Mesures immédiates
- ✓Recréer les fichiers de verrouillage et définir des versions sûres pour les dépendances suspectes
- ✓Vérifier les chemins de dépôt et les processus Node détachés sur les hôtes de développement et CI
- ✓Effectuer une rotation des secrets sur les machines concernées
- ✓pull_request_target : éviter le checkout des branches non fiables avec des secrets ; appliquer le principe des privilèges minimaux pour les tokens
- ✓Branches de release : obligation de revue, commits signés, interdiction des push directs
- ✓Egress CI : limiter les destinations aux registries attendues, bloquer par défaut IPFS/DHT
Sur le plan structurel, il est judicieux de prévoir une période de refroidissement (Cooldown) pour les nouvelles versions publiées sur npm, ainsi que des contrôles d’egress en temps d’exécution dans les GitHub Actions. La provenance (Provenance) reste une couche utile en complément du durcissement CI, de la protection des branches, du refroidissement des dépendances et de la télémétrie comportementale dans le processus de build.
Contexte pour les entreprises du DACH
L’AsyncAPI s’intègre dans les générateurs d’API, les pipelines de documentation et les chaînes d’outils DevOps. Les entreprises qui exploitent des architectures pilotées par événements (Event-driven Architecture) et automatisent OpenAPI/AsyncAPI utilisent souvent ces outils de manière indirecte. La directive NIS2 et le règlement sur la résilience cyber (Cyber Resilience Act) renforcent les exigences en matière de gestion des risques liés à la chaîne d’approvisionnement. L’affirmation « nous vérifions la provenance » ne suffit plus comme unique mesure de contrôle si les permissions des workflows et la gouvernance sectorielle restent floues.
Ce cas met en lumière l’importance de la gouvernance au niveau de la source de vérité : la branche Git et les workflows CI qui alimentent la pipeline. Les attestations de registre (Registry-Attestationen) complètent cette couche, mais ne la remplacent pas en tant que preuve de confiance unique.
Foire aux questions
Chaque question est verrouillée. Un clic déverrouille la réponse.
L’attestation de provenance npm n’a-t-elle pas suffi comme preuve de confiance ?
Elle atteste du workflow autorisé et du contexte de build. Cependant, elle ne couvre ni la légitimité du commit déclencheur ni l’intégrité des identifiants CI en amont. En cas de compromission de branche, l’attestation peut être techniquement correcte tout en induisant en erreur quant à l’évaluation des risques.
Un bloqueur aurait-il protégé contre les scripts d’installation ?
Non. Le dropper était lié à l’appel de module (require). Les scripts de cycle de vie npm (npm-lifecycle-scripts) n’étaient pas concernés. Une protection efficace nécessite des contrôles comportementaux et de sortie (egress) pendant la phase de construction.
Les installations fraîches sont-elles encore dangereuses aujourd’hui ?
Les versions malveillantes ont été supprimées du registre Windows. Les fichiers de verrouillage (lockfiles) et les caches issus de la fenêtre d’exposition, ainsi que les hôtes ayant déjà exécuté la charge utile, restent cependant risqués.
Quelles informations d’identification faut-il d’abord faire pivoter ?
Sur les systèmes Dev/CI concernés : jetons GitHub et npm, clés SSH et identifiants cloud stockés dans l’environnement ou dans les secrets des runners. La raison ? Un accès shell combiné à une persistance des données. Dans cette version, le module de récolte était activé dans cette configuration.
Quelle est la mesure de gouvernance prioritaire ?
Audit des workflows privilégiés pull_request_target et apparentés (pas de checkout non fiable avec des secrets, tokens à privilèges minimaux), sécurisation des branches de release (revues, commits signés, blocage des pushs directs) et limitation de l’egress CI. Provenance complète le tout en fournissant une preuve du chemin de build.
Les choix de la rédaction
À lireCursor lance git.exe depuis la racine du dépôtÀ lirePoC LegacyHive et correctifs Windows récentsÀ lireDeux vulnérabilités Joomla ajoutées au catalogue CISA KEV
Plus du réseau MBF Media
cloudmagazinQuand les agents d’IA voyagent : la résidence des données comme défi opérationnelMyBusinessFuturePlus de faillites, des cas plus modestes : qu’est-ce qui compte




