Au cœur de la compromission de la chaîne d'approvisionnement d'Axios - un RAT pour les dominer tous

Elastic Security Labs analyse la compromission de la chaîne d'approvisionnement du paquet axios npm, qui fournit un RAT multiplateforme unifié.

Elastic Security Labs a publié les premières règles de triage et de détection pour la compromission de la chaîne d'approvisionnement d'Axios. Il s'agit d'une analyse détaillée de la RAT et des charges utiles.

Introduction

Elastic Security Labs a identifié une compromission de la chaîne d'approvisionnement du paquet axios npm, l'un des paquets les plus dépendants de l'écosystème JavaScript avec environ 100 millions de téléchargements hebdomadaires. L'attaquant a compromis le compte d'un responsable et publié des versions antidatées qui délivrent un cheval de Troie d'accès à distance multiplateforme aux systèmes macOS, Windows et Linux par le biais d'un crochet post-installation malveillant.

Principaux points abordés dans cet article

  • Un compte npm maintainer compromis (jasonsaayman) a été utilisé pour publier deux versions malveillantes du client HTTP Axios largement utilisé - 1.14.1 (tagged latest) et 0.30.4 (tagged legacy) - ce qui signifie qu'un npm install axios par défaut s'est soldé par un paquetage rétroactif.
  • Le JavaScript malveillant déploie des implants de stade 2 spécifiques à chaque plateforme pour macOS, Windows et Linux
  • Les trois charges utiles de niveau 2 sont des implémentations du même RAT - protocole C2, jeu de commandes, cadence des balises et agent utilisateur usurpé identiques, écrits en PowerShell (Windows), C++ (macOS) et Python (Linux).
  • Le dropper effectue un nettoyage anti-forensic en se supprimant lui-même et en échangeant son package.json avec une copie propre, effaçant ainsi les preuves du déclenchement post-installation de node_modules

Préambule

Le mars 30, 2026, Elastic Security Labs a détecté une compromission de la chaîne d'approvisionnement ciblant le paquet axios npm grâce à une surveillance automatisée de la chaîne d'approvisionnement. L'attaquant a pris le contrôle du compte npm appartenant à jasonsaayman, l'un des principaux mainteneurs du projet, et a publié deux versions rétroactives en l'espace de 39 minutes.

Le paquet axios est l'une des bibliothèques client HTTP les plus utilisées dans l'écosystème JavaScript. Au moment de la découverte, les étiquettes des versions les plus récentes et les plus anciennes indiquaient des versions compromises, ce qui garantissait que la majorité des nouvelles installations utilisaient une version rétroactive.

Les versions malveillantes ont introduit une seule nouvelle dépendance : plain-crypto-js, un paquetage spécifique dont le crochet post-installation télécharge et exécute silencieusement des implants RAT de stade 2 spécifiques à la plate-forme à partir de sfrclak[.]com:8000.

Ce qui rend cette campagne remarquable au-delà de son rayon d'action, c'est l'outillage de niveau 2. L'attaquant a déployé trois implémentations parallèles du même RAT - une pour Windows, une pour macOS et une pour Linux - qui partagent toutes un protocole C2, une structure de commande et un comportement de balise identiques. Il ne s'agit pas de trois outils différents, mais d'un seul cadre d'implantation multiplateforme avec des implémentations natives de la plateforme.

Elastic Security Labs a déposé un avis de sécurité GitHub sur le dépôt axios le 31 mars, 2026 à 01:50 AM UTC pour coordonner la divulgation et s'assurer que les mainteneurs et le registre npm puissent agir sur les versions compromises.

La communauté ayant signalé la compromission sur les médias sociaux, Elastic Security Labs a partagé publiquement les premières conclusions afin d'aider les défenseurs à réagir en temps réel.

Ce billet couvre l'ensemble de la chaîne d'attaque : de la compromission de la chaîne d'approvisionnement au niveau npm, en passant par le dropper obscurci, jusqu'à l'architecture du RAT multiplateforme et les différences significatives entre ses trois variantes.

Aperçu de la campagne

Le compromis est évident dans les métadonnées du registre npm. L'adresse électronique du responsable est passée de jasonsaayman@gmail[.]com - présente dans toutes les versions légitimes antérieures - à ifstap@proton[.]me dans les versions malveillantes. La méthode de publication a également changé :

VersionPublié parMethodProvenance
axios@1.14.0 (légitime)jasonsaayman@gmail[.]comActions GitHub OIDCAttestations de provenance SLSA
axios@1.14.1 (compromis)ifstap@proton[.]mePublication directe CLIAucune
axios@0.30.4 (compromis)ifstap@proton[.]mePublication directe CLIAucune

Le passage d'un flux d'éditeurs OIDC de confiance avec une provenance SLSA à une publication CLI directe avec un e-mail modifié est un indicateur clair d'un accès non autorisé.

Chronologie

  • 2026-02-18 17:19 UTC - axios@0.30.3 publié légitimement par jasonsaayman@gmail[.]com
  • 2026-03-27 19:01 UTC - axios@1.14.0 publié légitimement via GitHub Actions OIDC
  • 2026-03-30 05:57 UTC - plain-crypto-js@4.2.0 publié par nrwise (nrwise@proton.me) - clean decoy to build registry history
  • 2026-03-30 23:59 UTC - plain-crypto-js@4.2.1 publié par nrwise - version malveillante avec postinstall backdoor
  • 2026-03-31 00:21 UTC - axios@1.14.1 publié par un compte compromis - tagué latest
  • 2026-03-31 01:00 UTC - axios@0.30.4 publié par un compte compromis - tagué legacy

Paquets concernés

  • axios@1.14.1 - Malveillante, étiquetée latest au moment de la découverte
  • axios@0.30.4 - Malveillante, étiquetée legacy au moment de la découverte
  • plain-crypto-js@4.2.0 - Leurre propre, publié pour construire l'historique du registre
  • plain-crypto-js@4.2.1 - Véhicule malveillant de livraison de charge utile (postinstall backdoor)

Versions sûres : axios@1.14.0 (dernière version légitime 1.x avec provenance SLSA) et axios@0.30.3 (dernière version légitime 0.30.x ).

L'attaquant a marqué les canaux les plus récents et les plus anciens, maximisant ainsi le rayon d'action des projets utilisant l'API axios actuelle ou ancienne.

Analyse du code

Étape 1 : Le compte-gouttes plain-crypto-js

L'ensemble de la chaîne de livraison repose sur le crochet de cycle de vie post-installation de npm. L'installation de l'une ou l'autre des versions compromises d'axios tire plain-crypto-js@^4.2.1 comme dépendance, qui déclare :

"scripts": {
  "postinstall": "node setup.js"
}

Cela fait en sorte que setup.js s'exécute automatiquement pendant l'installation de npm - aucune interaction de la part de l'utilisateur n'est requise.

Le fichier setup.js utilise un système d'encodage à deux niveaux pour dissimuler son comportement :

  • Couche 1 : Inversion de la chaîne de caractères suivie d'un décodage Base64
  • Couche 2 : Chiffrement XOR utilisant la clé OrDeR_7077 avec un indice dépendant de la position (7 * i² % 10)

Toutes les chaînes critiques, les noms de modules, les URL, les commandes shell sont stockés dans un tableau codé stq[] et décodés au moment de l'exécution. Le contenu décodé révèle l'infrastructure opérationnelle :

Livraison spécifique à la plate-forme

Après avoir décodé sa table de chaînes, le dropper vérifie os.platform() et passe à l'une des trois routines de livraison. Chacun envoie un POST HTTP à http://sfrclak[.]com:8000/6202033 avec un corps spécifique à la plateforme - packages.npm.org/product0 (macOS), packages.npm.org/product1 (Windows), packages.npm.org/product2 (Linux) - permettant au C2 de servir la bonne charge utile à partir d'un seul point d'extrémité. Le site packages.npm.org/ est une tentative délibérée de faire apparaître le trafic sortant comme une communication bénigne du registre npm dans les journaux du réseau :

PlatformMéthode de livraisonLieu de l'étape 2Déguisement
macOSAppleScript via osascript télécharge un binaire avec curl/Library/Caches/com.apple.act.mondDémon du système Apple
WindowsTéléchargements VBScript .ps1 via curl, s'exécute via PowerShell renommé (%PROGRAMDATA%\wt.exe)%TEMP%\6202033.ps1 (transitoire)Terminal Windows
LinuxTéléchargement direct de curl et exécution de python3/tmp/ld.pyAucune

Anti-forensics

Le compte-gouttes effectue deux opérations de nettoyage :

  1. Auto-effacement : setup.js se supprime lui-même via fs.unlink(__filename)
  2. Échange de manifeste de paquet : Un fichier propre nommé package.md (contenant une configuration bénigne de la version 4.2.0 sans crochet de post-installation) est renommé en package.json, l'écrasement de la version malveillante

L'inspection post-incident de node_modules/plain-crypto-js/package.json ne révèle aucune trace du déclencheur post-installation. Le fichier malveillant setup.js a disparu. Seuls le fichier de verrouillage et les journaux d'audit npm conservent des preuves.

Étape 2 : RAT multiplateforme

Les trois charges utiles de l'étape 2 : PowerShell pour Windows, C++ compilé pour macOS, Python pour Linux ne sont pas trois outils différents. Il s'agit de trois implémentations de la même spécification RAT, partageant un protocole C2, un ensemble de commandes, un format de message et un comportement opérationnel identiques. Cette cohérence indique clairement qu'il s'agit d'un développeur unique ou d'une équipe étroitement coordonnée travaillant à partir d'un document de conception commun.

Architecture partagée

Les propriétés suivantes sont identiques pour les trois variantes :

  • Transport C2 : HTTP POST
  • Encodage du corps : JSON encodé en base64
  • User-Agent : mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)
  • Intervalle entre les balises : 60 secondes
  • Session UID : chaîne alphanumérique aléatoire de 16 caractères, générée par exécution.
  • Types de messages sortants : FirstInfo, BaseInfo, CmdResult
  • Types de commandes entrantes : kill, peinject, runscript, rundir
  • Types de commandes de réponse : rsp_kill, rsp_peinject, rsp_runscript, rsp_rundir

La chaîne usurpée du user-agent d'IE8/Windows XP est particulièrement remarquable, elle est anachronique sur les trois plateformes, et sa présence sur un hôte macOS ou Linux est un indicateur de détection fort.

Initialisation et reconnaissance

Au démarrage, chaque variante :

  1. Génère un UID de session - 16 caractères alphanumériques aléatoires, inclus dans chaque message C2 ultérieur.
  2. Détecte le système d'exploitation et l'architecture - signale les identifiants spécifiques à la plate-forme (par exemple, windows_x64, macOS, linux_x64)
  3. Enumère les répertoires initiaux d'intérêt (profil de l'utilisateur, documents, bureau, répertoires de configuration).
  4. Envoie une balise FirstInfo contenant l'UID, l'identifiant du système d'exploitation et l'instantané du répertoire.

Après l'initialisation, l'implant entre dans la boucle principale. Le premier battement de cœur de BaseInfo comprend un profil complet du système. Les mêmes catégories de données sont collectées sur toutes les plateformes, bien que les API sous-jacentes diffèrent :

Données collectéesSource WindowsmacOS SourceLinux Source
Nom d'hôte%COMPUTERNAME% env vargethostname()/proc/sys/kernel/hostname
Nom d'utilisateur%USERNAME% env vargetuid() + getpwuid()os.getlogin()
Version du système d'exploitationWMI / registresysctlbyname("kern.osproductversion")plateforme.système() + platform.release()
Fuseau horaireFuseau horaire du systèmelocaltime_r()datetime.timezone
Temps de démarrageDurée de fonctionnement du systèmesysctl("kern.boottime")/proc/uptime
Date d'installationRegistre / WMIstat("/") ou sysctlctime de /var/log/installer ou /var/log/dpkg.log
Modèle de matérielWMIsysctlbyname("hw.model")/sys/class/dmi/id/nom_du_produit
Type de CPUWMIsysctlbyname()plateforme.machine()
Process listPID complet, session, nom, cheminpopen("ps") (jusqu'à 1000)Énumération complète de /proc (PID, PPID, utilisateur, cmdline)

Les battements de cœur suivants sont légers et ne contiennent qu'un horodatage confirmant que l'implant est en vie.

Envoi des commandes

La réponse C2 est analysée sous forme de JSON, et le champ type détermine l'action. Les trois variantes mettent en œuvre les mêmes quatre commandes :

kill - Auto-termination. Envoie un accusé de réception rsp_kill et quitte. Le mécanisme de persistance de la variante Windows (clé de registre + fichier batch) survit à la commande kill à moins d'être explicitement nettoyé ; les variantes macOS et Linux n'ont pas de persistance propre.

runscript - Exécution d'un script ou d'une commande. La commande d'interaction principale de l'opérateur. Accepte un champ Script (code à exécuter) et un champ Param (arguments). Lorsque Script est vide, Param est exécuté directement comme une commande. Le mécanisme d'exécution est indépendant de la plate-forme :

PlatformMécanisme d'exécution
WindowsPowerShell avec -NoProfile -ep Bypass
macOSAppleScript via /usr/bin/osascript
LinuxShell via subprocess.run(shell=True) ou Python via python3 -c

peinject - Livraison de charges utiles binaires. Malgré la dénomination centrée sur Windows ("PE inject"), les trois plates-formes l'utilisent comme moyen de déposer et d'exécuter des charges utiles binaires :

PlatformImplementation
WindowsChargement réfléchi d'un assemblage .NET via [System.Reflection.Assembly]::Load()
macOSDécode en base64 et dépose un binaire, s'exécute avec les paramètres fournis par l'opérateur.
LinuxBase64-décode un binaire vers /tmp/.<random Chaîne de 6 caractères> (fichier caché), lancée via subprocess.Popen().

L'implémentation Windows dispose d'une exécution en mémoire sans dépôt de fichier mais sans désactiver AMSI qui sera certainement signalé lors du chargement de l'assemblage. Les variantes macOS et Linux adoptent l'approche la plus simple, qui consiste à écrire un binaire sur le disque et à l'exécuter directement.

rundir - Énumération des répertoires. Accepte les chemins d'accès et renvoie une liste détaillée des fichiers (nom, taille, type, date de création/modification, nombre d'enfants pour les répertoires). Permet à l'opérateur de parcourir le système de fichiers de manière interactive.

Résumé des capacités

CapacitéWindows (PowerShell)macOS (C++)Linux (Python)
PersistanceClé d'exécution du registre + .bat cachéAucuneAucune
Exécution du scriptPowerShellAppleScript via osascriptShell ou Python en ligne
Injection binaireInjection de charge .NET réfléchie dans cmd.exeBinary drop + executeBinary drop to /tmp/ + executez
Anti-forensicsFenêtres cachées, nettoyage des fichiers temporairesTemp caché .scptFichiers cachés /tmp/.XXXXXX

Attribution

Le binaire macOS Mach-O fourni par le crochet de post-installation plain-crypto-js présente des similitudes importantes avec WAVESHAPER, une porte dérobée C++ repérée par Mandiant et attribuée à UNC1069, un groupe de menaces lié à la République populaire démocratique de Corée (RPDC).

Conclusion

Cette campagne démontre l'attrait continu de l'écosystème npm en tant que vecteur d'attaque de la chaîne d'approvisionnement. En compromettant le compte d'un seul responsable de l'un des paquets les plus dépendants de l'écosystème JavaScript, l'auteur de l'attaque a obtenu un mécanisme de diffusion susceptible d'atteindre des millions d'environnements.

L'indicateur de détection le plus fiable de la boîte à outils est aussi son choix de conception le plus curieux : la chaîne du user-agent d'IE8/Windows XP codée en dur de manière identique sur les trois variantes de plate-forme. Bien qu'il fournisse une empreinte protocolaire cohérente pour le routage côté serveur C2, il est trivialement détectable sur n'importe quel réseau moderne - et constitue une anomalie immédiate sur les hôtes macOS et Linux.

Elastic Security Labs continuera à surveiller ce groupe d'activités et mettra à jour cet article en cas de nouvelles découvertes.

MITRE ATT&CK

Elastic utilise le cadre MITRE ATT& CK pour documenter les tactiques, techniques et procédures communes que les menaces persistantes avancées utilisent contre les réseaux d'entreprise.

Tactiques

Les tactiques représentent le pourquoi d'une technique ou d'une sous-technique. Il s'agit de l'objectif tactique de l'adversaire : la raison pour laquelle il effectue une action.

Techniques

Les techniques représentent la manière dont un adversaire atteint un objectif tactique en effectuant une action.

Observations

Les observables suivants ont été examinés dans le cadre de cette recherche.

ObservableTypeNomRéférence
617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101SHA-2566202033.ps1Charge utile Windows
92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645aSHA-256com.apple.act.mondCharge utile MacOS
fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cfSHA-256ld.pyCharge utile Linux
sfrclak[.]comDomainC2
142.11.206[.]73ipv4-addrC2

Références

Les éléments suivants ont été référencés tout au long de la recherche ci-dessus :

Partager cet article