Résumé — Le 30 mars 2026, l'écosystème npm subissait l'une des attaques par chaîne d'approvisionnement les plus significatives de son histoire. Deux versions malveillantes du package Axios (1.14.1 et 0.30.4), publié par centaines de millions de développeurs, ont introduit une dépendance fictive servant de dropper multiplateforme pour un RAT (Remote Access Trojan). Décryptage technique complet d'une opération qui a contourné les pipelines CI/CD en ciblant les identifiants npm directement.
Introduction
Axios n'est pas un package comme les autres. Avec des centaines de millions de téléchargements hebdomadaires, c'est le client HTTP de facto de l'écosystème JavaScript. Presque chaque projet Node.js ou application front-end l'utilise ou l'a utilisé. C'est précisément cette omniprésence qui en faisait une cible de choix.
Le 30 mars 2026, la communauté découvrait avec stupeur que deux versions d'Axios — 1.14.1 (branche principale) et 0.30.4 (branche de maintenance) — avaient été publiées sur le registre npm avec un contenu malveillant. L'attaque n'était pas le fruit d'une vulnérabilité dans le code d'Axios lui-même, mais d'un compromis des identifiants npm du mainteneur principal, le compte "jasonsaayman".
Le chercheur en sécurité Ashish Kurmi de StepSecurity est à l'origine de la découverte et de la divulgation de cette attaque.
Cet article propose une analyse technique approfondie du mécanisme d'attaque, de la chaîne d'infection complète, et des mesures de détection.
Chronologie de l'attaque
timeline
title Chronologie de l'attaque — Mars 2026
section 19 Mars
TeamPCP compromet Trivy (Aqua Security)
: Injection de malware de vol de credentials
: dans les releases et GitHub Actions
section 22 Mars
TeamPCP étend son attaque à npm
: avec CanisterWorm
section 31 Mars
Attaque supply chain sur Axios
: Versions 1.14.1 et 0.30.4 publiées
: avec dépendance malveillante
section 31 Mars
Ashish Kurmi (StepSecurity)
: Découvre et signale l'attaque
La chaîne d'attaque — Vue d'ensemble
L'attaque s'articule autour de plusieurs étapes précises. Comprendre chacune d'elles est essentiel pour appréhender la sophistication de l'opération.
flowchart TD
A["🔓 Vol credentials"] --> B["📦 Versions malveillantes"]
B --> C["npm install axios"]
C --> D["Injection\nplain-crypto-js"]
D --> E["postinstall"]
E --> F["🦠 Dropper"]
E --> G["Obfuscation"]
F --> H["📡 C2"]
H --> I["⬇️ Payload"]
I --> J["🔄 Replacement"]
G --> K["🧹 Anti-forensique"]
style A fill:#4AD0B0,color:#1a1a2e,stroke:#4AD0B0
style D fill:#e74c3c,color:#fff,stroke:#c0392b
style E fill:#e74c3c,color:#fff,stroke:#c0392b
style K fill:#e74c3c,color:#fff,stroke:#c0392b
Étape 1 — Le compromis des identifiants npm
Le point d'entrée de cette attaque n'est pas technique mais humain. L'attaquant a obtenu les identifiants npm du compte "jasonsaayman", mainteneur principal d'Axios. Ces identifiants donnaient un accès direct en écriture au registre npm pour les packages sous son contrôle.
Pourquoi c'est crucial
npm authentifie les publications de packages par token d'authentification ou par login/password. Un mainteneur avec les droits de publication sur un package peut publier n'importe quelle version, sans validation supplémentaire du registre. Il n'y a pas de revue de code obligatoire côté npm pour les nouvelles versions de packages existants.
flowchart LR
M["Mainteneur\nlégitime"] -->|"npm publish\n--tag latest"| T["Auth\nToken"]
T --> R["Registre\nnpm"]
R --> P["Package\ndisponible\npubliquement"]
style T fill:#e74c3c,color:#fff
style R fill:#4AD0B0,color:#1a1a2e
⚠️ Points critiques du modèle de publication npm :
- ❌ Pas de revue de code par npm
- ❌ Pas de vérification antivirus intégrée
- ❌ Pas de validation d'intégrité Git vs. npm
- ⚠️ Seul le token est nécessaire
Étape 2 — Publication des versions malveillantes
Une fois en possession des identifiants, l'attaquant a publié deux versions :
| Version | Branche | Objectif probable |
|---|---|---|
1.14.1 |
main (stable) |
Cible principale — majorité des utilisateurs |
0.30.4 |
maintenance | Maximiser la portée sur les projets non migrés |
La modification clé consistait à injecter une nouvelle dépendance dans le fichier package.json :
// package.json — version malveillante d'Axios
{
"name": "axios",
"version": "1.14.1",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.1",
"proxy-from": "^2.0.0",
// ⚠️ Dépendance malveillante injectée
"plain-crypto-js": "4.2.1"
}
}
Le package "plain-crypto-js" — un cheval de Troie ingénieux
Le nom "plain-crypto-js" a été choisi avec soin pour paraître légitime. Il évoque immédiatement une bibliothèque de chiffrement, un besoin courant dans les projets JavaScript. En réalité, ce package n'avait aucune fonctionnalité cryptographique. Son existence entière était dédiée à une seule chose : exécuter un script postinstall.
// [email protected] — package.json (reconstitution)
{
"name": "plain-crypto-js",
"version": "4.2.1",
"description": "A lightweight cryptography library", // description trompeuse
"main": "index.js",
"scripts": {
// ⚠️ Le script postinstall s'exécute automatiquement à l'installation
"postinstall": "node setup.js"
}
}
Pourquoi le postinstall est si dangereux
Le lifecycle postinstall de npm s'exécute automatiquement et silencieusement chaque fois qu'un package est installé — que ce soit via npm install, npm ci, ou comme dépendance transitive. C'est l'une des vecteurs d'attaque les plus connus et les plus redoutés dans l'écosystème npm :
flowchart LR
A["📦 npm install\[email protected]"] --> B["axios"]
B --> C["dépendances"]
C --> D["plain-crypto-js\n⚠️ 4.2.1"]
D --> E["lifecycle npm"]
E --> F["⚠️ postinstall"]
F --> G["node setup.js\n🦠 DROPPER"]
G --> H["✅ Installation\nterminée"]
style A fill:#1a2e2a,color:#4AD0B0,stroke:#4AD0B0
style D fill:#f39c12,color:#1a1a2e,stroke:#f39c12
style F fill:#e74c3c,color:#fff,stroke:#c0392b,stroke-width:3px
style G fill:#e74c3c,color:#fff,stroke:#c0392b,stroke-width:3px
La plupart des développeurs ne lisent pas les scripts de lifecycle de leurs dépendances — et encore moins de leurs dépendances transitives. L'installation d'un package aussi répandu qu'Axios ne soulève a priori aucun soupçon.
Étape 3 — Le dropper multiplateforme
Le script setup.js de plain-crypto-js agissait comme un dropper — un programme dont le but unique est de télécharger et d'exécuter une charge utile secondaire. Sa conception était explicitement multiplateforme, ciblant Windows, macOS et Linux.
flowchart LR
A["setup.js"] --> B["Détection OS"]
B --> C["Win"]
B --> D["macOS"]
B --> E["Linux"]
C & D & E --> F["📡 C2"]
F --> G["Téléchargement\npayload"]
G --> H["🦠 RAT"]
H --> I["Anti-forensique"]
I --> I1["Suppression\ndropper"]
I --> I2["Remplacement\nprocessus"]
style A fill:#4AD0B0,color:#1a1a2e,stroke:#4AD0B0
style B fill:#4AD0B0,color:#1a1a2e,stroke:#4AD0B0
style F fill:#e74c3c,color:#fff,stroke:#c0392b
style H fill:#e74c3c,color:#fff,stroke:#c0392b
style I fill:#e74c3c,color:#fff,stroke:#c0392b
Architecture du C2 et second-stage payload
Le dropper ne contient pas la charge utile finale. Il agit comme un stager :
- Détection de l'environnement — identification de l'OS, de l'architecture, et potentiellement de l'environnement de développement.
- Contact C2 — connexion à un serveur de commandement et contrôle (C2) actif pour récupérer le payload approprié.
- Livraison — téléchargement d'un binaire ou script spécifique à la plateforme.
- Exécution — lancement du second-stage payload (RAT).
- Effacement — suppression des traces du dropper et remplacement du processus pour échapper à l'analyse.
Cette approche en deux étapes présente un avantage tactique majeur pour l'attaquant : le stager (le dropper dans plain-crypto-js) est léger et peut être analysé en détail, mais il ne révèle pas la nature réelle du malware final. Le payload de second stade peut être modifié, mis à jour ou différencié selon la cible, sans nécessiter une nouvelle publication sur npm.
Le contournement du pipeline CI/CD — Un point critique
L'un des aspects les plus importants de cette attaque est sa bypass du pipeline CI/CD. Beaucoup de projets open source majeurs, y compris Axios, utilisent GitHub Actions pour automatiser leurs processus de build, de test et de publication.
flowchart TD
subgraph LEGIT["✅ Pipeline Légitime"]
direction LR
A1["git push"] --> A2["GitHub Actions\ntests, checks, signatures"]
A2 --> A3["npm publish"]
A3 -.-> A4["Code publié = code du repo"]
end
subgraph ATTACK["❌ Attaque"]
direction LR
B1["npm publish direct\navec token volé"] --> B2["npm"]
B2 -.-> B3["Pas de GitHub Actions"]
B2 -.-> B4["Pas de tests"]
B2 -.-> B5["Code npm ≠ code Git"]
end
style LEGIT fill:#1a3a2e,color:#4AD0B0,stroke:#4AD0B0
style ATTACK fill:#3a1a1a,color:#e74c3c,stroke:#e74c3c
Le mécanisme est simple mais dévastateur :
- Normalement, un mainteneur push du code vers GitHub, les tests s'exécutent via GitHub Actions, et si tout passe, le pipeline publie automatiquement la nouvelle version sur npm. Le code publié correspond exactement à ce qui est dans le dépôt Git.
- Dans cette attaque, l'attaquant a publié directement sur npm en utilisant les identifiants volés, sans passer par le pipeline CI/CD. Le code publié sur npm ne correspondait donc pas au code source dans le dépôt GitHub d'Axios.
C'est un point fondamental : la confiance dans un package npm ne devrait jamais se baser uniquement sur le nom ou la réputation du projet. Le registre npm et le dépôt Git sont deux systèmes distincts, et la correspondance entre les deux n'est pas garantie par défaut.
Comparaison avec l'attaque TeamPCP sur Trivy
Cette attaque n'est pas un incident isolé. En mars 2026, le groupe TeamPCP a mené une série d'attaques par chaîne d'approvisionnement qui partagent des patterns frappants.
Attaque Trivy — 19 mars 2026
Le 19 mars 2026, TeamPCP a compromis Trivy, le scanner de vulnérabilités d'Aqua Security. L'attaque a injecté un malware de vol d'identifiants (credential stealer) directement dans les releases officielles de Trivy et dans ses workflows GitHub Actions.
Expansion sur npm — 22 mars 2026
Trois jours plus tard, le 22 mars, TeamPCP a étendu son opération à l'écosystème npm avec CanisterWorm, démontrant une capacité à opérer sur plusieurs registres de packages et plusieurs écosystèmes.
Tableau comparatif
| Caractéristique | Attaque Trivy (TeamPCP, 19 mars) | Attaque Axios (31 mars) |
|---|---|---|
| Cible | Trivy | Axios |
| Écosystème | Go / GitHub | JavaScript / npm |
| Vecteur | Releases GitHub + GitHub Actions | npm publish direct (token compromis) |
| Objectif | Vol de credentials | RAT multiplateforme (C2 alive) |
| Technique clé | Injection dans les artefacts | Dépendance fictive + postinstall |
| Anti-forensique | Non documenté | Suppression dropper + replacement proc |
| Découvreur | Aqua Security | Ashish Kurmi (StepSecurity) |
Patterns communs
Plusieurs éléments rapprochent ces attaques :
- Ciblage de projets populaires — Trivy et Axios sont tous deux des outils extrêmement répandus dans leurs écosystèmes respectifs.
- Compromis des identifiants de publication — dans les deux cas, l'attaquant n'a pas exploité une vulnérabilité technique du code, mais a obtenu un accès légitime aux mécanismes de publication.
- Contournement des contrôles CI/CD — les artefacts malveillants ont été publiés en dehors des pipelines de vérification.
- Fenêtre temporelle serrée — les attaques se sont succédées sur moins de deux semaines, suggérant une campagne coordonnée.
Comment détecter ce type d'attaque
Vérification immédiate : êtes-vous concerné ?
La première étape est de vérifier si votre projet utilise l'une des versions compromises :
# Vérifier la version d'Axios installée
npm ls axios
# Ou avec yarn
yarn why axios
# Chercher spécifiquement les versions malveillantes
npm ls axios | grep -E "1\.14\.1|0\.30\.4"
Vérifier la présence de la dépendance malveillante
# Chercher plain-crypto-js dans l'arbre des dépendances
npm ls plain-crypto-js
# Vérifier dans le lockfile
grep -r "plain-crypto-js" package-lock.json
Audit npm
npm dispose d'outils d'audit intégrés :
# Lancer un audit de sécurité npm
npm audit
# Audit avec vérification des signatures (si supporté)
npm audit signatures
Vérifier la cohérence entre npm et Git
Une vérification essentielle consiste à comparer le code publié sur npm avec le code source Git :
# Télécharger le package depuis npm
npm pack [email protected]
# Extraire le tarball
tar -xzf axios-1.14.1.tgz
# Comparer les dépendances déclarées avec le package.json du repo Git
diff <(jq '.dependencies' package/package.json) \
<(curl -s https://raw.githubusercontent.com/axios/axios/main/package.json | jq '.dependencies')
Si les dépendances diffèrent, c'est un signal d'alarme critique.
Vérifier les permissions npm
# Lister les tokens npm actifs sur la machine
npm token list
# Révoquer tout token suspect
npm token revoke <token_id>
Monitoring du postinstall
Pour détecter les exécutions de scripts postinstall suspectes :
# Désactiver temporairement les scripts lifecycle pour un install
npm install --ignore-scripts
# Puis inspecter manuellement les scripts postinstall
find node_modules -name "package.json" -exec sh -c \
'jq -r ".scripts.postinstall // empty" "$1" 2>/dev/null | grep -q "." && echo "POSTINSTALL: $1"' _ {} \;
Outils de détection avancés
Plusieurs outils peuvent aider à renforcer la sécurité de votre chaîne de dépendances :
# Socket.dev — analyse des dépendances
npx @socketsecurity/cli audit
# lockfile-lint — vérification de l'intégrité du lockfile
npx lockfile-lint --path package-lock.json \
--type npm \
--validate-https \
--allowed-hosts npm
Leçons et recommandations
Pour les mainteneurs de packages
flowchart LR
R1["🔐 2FA sur npm\nTOTP, pas SMS"]
R2["🔄 Tokens npm\nà durée limitée"]
R3["🔑 Pas de tokens\ndans le code"]
R4["📋 Require 2FA\nfor publishing"]
R5["✅ npm provenance\n/ signatures"]
R6["🛡️ Limiter les\nmainteneurs pub"]
R7["🔍 Surveiller les\npublications"]
R8["📦 Cohérence\nnpm vs Git"]
style R1 fill:#4AD0B0,color:#1a1a2e
style R2 fill:#4AD0B0,color:#1a1a2e
style R3 fill:#4AD0B0,color:#1a1a2e
style R4 fill:#4AD0B0,color:#1a1a2e
style R5 fill:#4AD0B0,color:#1a1a2e
style R6 fill:#4AD0B0,color:#1a1a2e
style R7 fill:#4AD0B0,color:#1a1a2e
style R8 fill:#4AD0B0,color:#1a1a2e
Pour les équipes de développement
--ignore-scriptsen CI : dans les pipelines CI/CD, envisagez d'installer les dépendances avec--ignore-scriptssauf besoin explicite. Cela neutralise la quasi-totalité des droppers par postinstall.- Lockfiles : commitez toujours
package-lock.jsonet utiliseznpm cien production plutôt quenpm install. - Audit automatisé : intégrez
npm auditdans vos pipelines CI/CD avec des seuils de criticité bloquants. - Analyse SCA : utilisez un outil d'analyse de composition logicielle (SCA) qui vérifie non seulement les vulnérabilités connues, mais aussi la provenance et le comportement des dépendances.
- Moindre privilège : les builds CI/CD ne doivent pas avoir accès à des tokens de publication npm en clair. Privilégiez les OIDC tokens ou les secrets court-lived.
Pour l'écosystème
Cette attaque, combinée à l'offensive TeamPCP contre Trivy et npm, met en lumière une vérité inconfortable : le modèle de confiance des registres de packages est fondamentalement fragile. La réputation d'un package ne protège pas contre le compromis de ses identifiants de publication.
Des initiatives comme npm provenance, les signatures de package, et l'adoption du modèle S2 (Sigstore) sont des pas dans la bonne direction, mais elles nécessitent une adoption massive pour être véritablement efficaces.
Conclusion
L'attaque supply chain contre Axios du 31 mars 2026 marque un tournant dans la sécurisation de l'écosystème JavaScript. En ciblant l'un des packages les plus téléchargés de npm via le compromis des identifiants du mainteneur, les attaquants ont démontré que aucun projet, aussi populaire soit-il, n'est à l'abri.
La technique employée — injection d'une dépendance fictive dont le seul but est un script postinstall servant de dropper multiplateforme — est à la fois simple et redoutablement efficace. Le contournement du pipeline CI/CD en publiant directement via des identifiants compromis rend cette attaque invisible aux mécanismes de vérification classiques basés sur le code source.
Dans un contexte où les attaques de TeamPCP sur Trivy et npm se multiplient, la question n'est plus de savoir si une nouvelle attaque de ce type se produira, mais quand. La préparation, la surveillance et l'adoption de bonnes pratiques de gestion des dépendances sont désormais des impératifs, pas des options.
Sources
- The Hacker News — Axios Supply Chain Attack Pushes Cross-Platform RAT Dropper via NPM
- Wiz Blog — Trivy Compromised: TeamPCP Supply Chain Attack
Publié par Louis BEDESCHI sur Kitsune — Cybersecurity Blog