Attaque Supply Chain Axios — Analyse Technique Complète du Compromis npm (Mars 2026)

Pas de résumé disponible.

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 :

  1. Détection de l'environnement — identification de l'OS, de l'architecture, et potentiellement de l'environnement de développement.
  2. Contact C2 — connexion à un serveur de commandement et contrôle (C2) actif pour récupérer le payload approprié.
  3. Livraison — téléchargement d'un binaire ou script spécifique à la plateforme.
  4. Exécution — lancement du second-stage payload (RAT).
  5. 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 :

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 :

  1. Ciblage de projets populaires — Trivy et Axios sont tous deux des outils extrêmement répandus dans leurs écosystèmes respectifs.
  2. 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.
  3. Contournement des contrôles CI/CD — les artefacts malveillants ont été publiés en dehors des pipelines de vérification.
  4. 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

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


Publié par Louis BEDESCHI sur Kitsune — Cybersecurity Blog

Articles similaires

Continuez votre exploration dans la catégorie Cybersécurité