Elastic Security Labs publicó las reglas iniciales de triaje y detección para el compromiso de la cadena de suministro de Axios. Este es un análisis detallado del RAT y las cargas útiles.
Introducción
Elastic Security Labs identificó un compromiso en la cadena de suministro del paquete axios npm, uno de los paquetes más confiables del ecosistema JavaScript, con aproximadamente 100 millones de descargas semanales. El atacante comprometió una cuenta de mantenedor y publicó versiones con puerta trasera que entregaban un troyano de acceso remoto multiplataforma a sistemas macOS, Windows y Linux mediante un gancho malicioso de postinstalación.
Conclusiones clave
- Se empleó una cuenta de mantenedor npm comprometida (jasonsaayman) para publicar dos versiones maliciosas del cliente HTTP de Axios, ampliamente empleado — 1.14.1 (última etiquetada) y 0.30.4 (legado etiquetado) — lo que significa que una instalación predeterminada de npm se resolvió en un paquete backdoor
- El JavaScript malicioso despliega implantes de etapa 2 específicos de la plataforma para macOS, Windows y Linux
- Las tres cargas útiles de etapa 2 son implementaciones del mismo RAT — protocolo C2 idéntico, conjunto de comandos, cadencia de baliza y user-agent suplantado, escritos en PowerShell (Windows), C++ (macOS) y Python (Linux)
- El gotero realiza una limpieza antiforense eliminar a sí mismo y cambiando su package.json por una copia limpia, borrando la evidencia del disparador postinstalación de
node_modules
Preámbulo
El 30de marzo de 2026, Elastic Security Labs detectó una vulnerabilidad en la cadena de suministro dirigida al paquete Axios npm mediante monitorización automatizada de la cadena de suministro. El atacante obtuvo el control de la cuenta npm perteneciente a jasonsaayman, uno de los principales mantenedores del proyecto, y publicó dos versiones backdoor en un plazo de 39 minutos.
El paquete axios es una de las bibliotecas cliente HTTP más empleadas en el ecosistema JavaScript. En el momento del descubrimiento, tanto las últimas como las antigas dist-tags apuntaban a versiones comprometidas, cerciorando que la mayoría de las instalaciones nuevas tuvieran un lanzamiento por backdoor.
Las versiones maliciosas introdujeron una única nueva dependencia: plain-crypto-js, un paquete diseñado específicamente cuyo gancho postinstalación descargaba y ejecutaba silenciosamente implantes RAT de etapa 2 específicos de la plataforma desde sfrclak[.]com:8000.
Lo que hace que esta campaña sea notable más allá de su radio de explosión es la maquinaria de la fase 2. El atacante desplegó tres implementaciones paralelas del mismo RAT — una para Windows, una macOS y una para Linux — todas compartiendo un protocolo C2, estructura de comandos y comportamiento de beacon idénticos. No son tres herramientas diferentes; Es un único marco de implantes multiplataforma con implementaciones nativas de plataforma.
Elastic Security Labs presentó un Aviso de Seguridad de GitHub al repositorio axios el 31 de marzo de 2026 a las 01:50 AM UTC para coordinar la divulgación y cerciorar que los mantenedores y el registro de npm pudieran actuar sobre las versiones comprometidas.
Mientras la comunidad señalaba el compromiso en redes sociales, Elastic Security Labs compartió públicamente los primeros hallazgos para ayudar a los defensores a responder en tiempo real.
Esta publicación cubre toda la cadena de ataque: desde el compromiso de la cadena de suministro a nivel npm pasando por el dropper ofuscado, hasta la arquitectura del RAT multiplataforma y las diferencias significativas entre sus tres variantes.
Resumen de la campaña
El compromiso es evidente en los metadatos del registro npm. El correo del mantenedor cambió de jasonsaayman@gmail[.]com — presente en todas las versiones legítimas anteriores — a ifstap@proton[.]me en las versiones maliciosas. El método de publicación también cambió:
| Versión | Publicado por | Method | Procedencia |
|---|---|---|---|
axios@1.14.0 (legítimo) | jasonsaayman@gmail[.]com | OIDC de acciones en GitHub | Atestaciones de procedencia de SLSA |
axios@1.14.1 (comprometido) | ifstap@proton[.]me | Publicación directa de CLI | Ninguno |
axios@0.30.4 (comprometido) | ifstap@proton[.]me | Publicación directa de CLI | Ninguno |
El cambio de un flujo de editor OIDC confiable con procedencia SLSA a una publicación directa de CLI con un email modificado es un claro indicador de acceso no autorizado.
Línea de tiempo
- 2026-02-18 17:19 UTC —
axios@0.30.3publicado legítimamente porjasonsaayman@gmail[.]com - 2026-03-27 19:01 UTC —
axios@1.14.0publicado legítimamente a través de GitHub Actions OIDC - 2026-03-30 05:57 UTC —
plain-crypto-js@4.2.0publicado pornrwise(nrwise@proton.me) — limpiar señuelo para construir el historial del registro - 2026-03-30 23:59 UTC —
plain-crypto-js@4.2.1publicado pornrwise— versión maliciosa conpostinstallpuerta trasera - 2026-03-31 00:21 UTC —
axios@1.14.1publicado por cuenta comprometida — etiquetadolatest - 2026-03-31 01:00 UTC —
axios@0.30.4publicado por cuenta comprometida — etiquetadolegacy
Paquetes afectados
axios@1.14.1— Malicioso, etiquetadolatesten el momento del descubrimientoaxios@0.30.4— Malicioso, etiquetadolegacyen el momento del descubrimientoplain-crypto-js@4.2.0— Señuelo limpio, publicado para construir la historia del registroplain-crypto-js@4.2.1— Vehículo malicioso, de entrega de carga (postinstallpuerta trasera)
Versiones seguras: axios@1.14.0 (última versión legítima 1.x con procedencia de SLSA) y axios@0.30.3 (última versión legítima 0.30.x ).
El atacante etiquetaba tanto los canales más recientes como los heredados, maximizando el radio de explosión entre proyectos usando la API Axios actual o heredada.
Análisis de código
Etapa 1: El dropper de crypto-js simple
Toda la cadena de entrega depende del gancho del ciclo de vida posterior a la instalación de NPM. Instalar cualquiera de las versiones comprometidas de Axios plain-crypto-js@^4.2.1 atrae como dependencia, lo que declara:
"scripts": {
"postinstall": "node setup.js"
}
Esto hace que setup.js se ejecute automáticamente durante la instalación de npm — sin necesidad de interacción del usuario.
El archivo setup.js emplea un esquema de codificación de dos capas para ocultar su comportamiento:
- Capa 1: Reversión de cadenas seguida de decodificación Base64
- Capa 2: Cifrado XOR usando el OrDeR_7077 clave con un índice dependiente de la posición (7 * i²% 10)
Todas las cadenas críticas, nombres de módulos, URLs y comandos de shell se almacenan en un array codificado stq[] y se decodifican en tiempo de ejecución. El contenido decodificado revela la infraestructura operativa:
Entrega específica de la plataforma
Tras decodificar su tabla de cadenas, el dropper comprueba os.platform() y se ramifica en una de tres rutinas de entrega. Cada uno envía un HTTP POST a http://sfrclak[.]com:8000/6202033 con un cuerpo específico para cada plataforma — packages.npm.org/product0 (macOS), packages.npm.org/product1 (Windows), packages.npm.org/product2 (Linux) — permitiendo que el C2 sirviera la carga útil correcta desde un único punto final. El packages.npm.org/ El prefijo es un intento deliberado de hacer que el tráfico saliente aparezca como una comunicación benigna del registro NPM en los registros de red:
| Platform | Método de Administración | Ubicación de la Fase 2 | Disfraz |
|---|---|---|---|
| macOS | AppleScript mediante osascript descarga binario con curl | /Library/Caches/com.apple.act.mond | Demonio del sistema Apple |
| Windows | VBScript descargas .ps1 vía curl, se ejecuta mediante PowerShell renombrado (%PROGRAMDATA%\wt.exe) | %TEMP%\6202033.ps1 (transitorio) | Windows Terminal |
| Linux | Descarga directa de curl y ejecución de python3 | /tmp/ld.py | Ninguno |
Antiforense
El cuentagotas realiza dos acciones de limpieza:
- Autoeliminación: setup.js se elimina a sí mismo a través de fs.unlink(__filename)
- Intercambio de manifiestos de paquete: Un archivo limpio llamado package.md (que contiene una configuración benigna de la versión 4.2.0 sin gancho de postinstalación) se renombra como package.json, sobreescribir la versión maliciosa
La inspección posterior al incidente de node_modules/plain-crypto-js/package.json no revela rastro del disparador posterior a la instalación. La setup.js maliciosa desapareció. Solo los registros de lockfile y auditoría del NPM conservan pruebas.
Fase 2: RAT multiplataforma
Las tres cargas útiles de etapa 2: PowerShell para Windows, C++ compilado para macOS, Python para Linux no son tres herramientas diferentes. Son tres implementaciones de la misma especificación RAT, que comparten un protocolo C2, conjunto de comandos, formato de mensaje y comportamiento operativo idénticos. La consistencia indica claramente un solo desarrollador o un equipo estrechamente coordinado trabajando a partir de un documento de diseño compartido.
Arquitectura compartida
Las siguientes propiedades son idénticas en las tres variantes:
- Transporte C2: HTTP POST
- Codificación corporal: JSON codificado en Base64
- Agente de usuario:
mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0) - Intervalo de baliza: 60 segundos
- UID de sesión: cadena alfanumérica aleatoria de 16 caracteres, generada por ejecución
- Tipos de mensajes salientes:
FirstInfo,BaseInfo,CmdResult - Tipos de comandos entrantes:
kill,peinject,runscript,rundir - Tipos de comandos de respuesta:
rsp_kill,rsp_peinject,rsp_runscript,rsp_rundir
La cadena de usuario-agente falsificada de IE8/Windows XP es especialmente notable, es anacrónica en las tres plataformas y su presencia en un host de macOS o Linux es un fuerte indicador de detección.
Inicialización y reconocimiento
Al arrancar, cada variante:
- Genera un UID de sesión — 16 caracteres alfanuméricos aleatorios, incluidos en cada mensaje C2 posterior
- Detecta el sistema operativo y la arquitectura — informa de identificadores específicos de la plataforma (por ejemplo, windows_x64, macOS, linux_x64)
- Enumera los directorios iniciales de interés (perfil de usuario, documentos, escritorio, directorios de configuración)
- Envía una baliza FirstInfo que contiene el UID, el identificador del sistema operativo y la instantánea del directorio
Tras la inicialización, el implante entra en el bucle principal. El primer BaseInfo Heartbeat incluye un perfil completo del sistema. Las mismas categorías de datos se recopilan en todas las plataformas, aunque las APIs subyacentes difieren:
| Datos recopilados | Fuente de Windows | Fuente de macOS | Fuente de Linux |
|---|---|---|---|
| Nombre de host | %COMPUTERNAME% env var | gethostname() | /proc/sys/kernel/nombre de host |
| Nombre de usuario | %USERNAME% env var | getuid() + getpwuid() | os.getlogin() |
| Versión del sistema operativo | WMI / registro | sysctlbyname("kern.osproductversion") | plataforma.sistema() + plataforma.release() |
| Zona horaria | Huso horario del sistema | localtime_r() | fechatiempo.zona horaria |
| Tiempo de arranque | Tiempo de funcionamiento del sistema | sysctl("kern.boottime") | /proc/uptime |
| Fecha de instalación | Registro / WMI | stat("/") o sysctl | ctime de /var/log/installer o /var/log/dpkg.log |
| Modelo de hardware | WMI | sysctlbyname("hw.model") | /sys/class/dmi/id/product_name |
| Tipo de CPU | WMI | sysctlbyname() | plataforma.máquina() |
| Process list | PID completo, sesión, nombre, ruta | popen("ps") (hasta 1000) | Enumeración completa /proc (PID, PPID, usuario, cmdline) |
Los latidos posteriores son ligeros, conteniendo solo una marca temporal para confirmar que el implante está vivo.
Despacho de mando
La respuesta C2 se analiza como JSON, y el campo tipo determina la acción. Las tres variantes implementan los mismos cuatro comandos:
matar — Auto-terminación. Envía un rsp_kill acuse de recibo y se va. El mecanismo de persistencia de la variante de Windows (clave de registro + archivo batch) sobrevive al comando de eliminación a menos que se limpie explícitamente; las variantes de macOS y Linux no tienen persistencia propia.
runscript — Ejecución de script/comando. La orden principal de interacción del operador. Acepta un campo Script (código a ejecutar) y un campo Param (argumentos). Cuando Script está vacío, Param se ejecuta directamente como un comando. El mecanismo de ejecución es nativo de la plataforma:
| Platform | Mecanismo de ejecución |
|---|---|
| Windows | PowerShell con -NoProfile -ep Bypass |
| macOS | AppleScript vía /usr/bin/osascript |
| Linux | Shell vía subprocess.run(shell=True) o Python vía python3 -c |
peinject — Entrega binaria de carga útil. A pesar de la denominación centrada en Windows ("PE inject"), las tres plataformas implementan esto como una forma de eliminar y ejecutar cargas útiles binarias:
| Platform | Implementation |
|---|---|
| Windows | Carga de ensamblador reflective .NET mediante [System.Reflection.Assembly]::Load() |
| macOS | Base64-decodifica y elimina un binario, ejecutar con parámetros proporcionados por el operador. |
| Linux | Base64-decodifica un binario a /tmp/.<random La cadena de 6 caracteres> (archivo oculto), se inicia mediante subproceso. Popen(). |
La implementación de Windows tiene ejecución en memoria sin pérdida de archivo pero sin desactivar AMSI, que sin duda se detectará en la carga de ensamblador. Las variantes de macOS y Linux adoptan el enfoque más sencillo de escribir un binario en disco y ejecutarlo directamente.
rundir — Enumeración de directorios. Acepta rutas y devuelve listados detallados de archivos (nombre, tamaño, tipo, marcas de tiempo de creación/modificación, recuento de hijos para directorios). Permite al operador navegar interactivamente por el sistema de archivos.
Resumen de capacidades
| Capacidad | Windows (PowerShell) | macOS (C++) | Linux (Python) |
|---|---|---|---|
| Persistencia | Clave de ejecución del registro + .bat oculta | Ninguno | Ninguno |
| Ejecución de scripts | PowerShell | AppleScript vía osascript | Shell o Python en línea |
| Inyección binaria | Carga reflectiva de .NET inyectar en cmd.exe | Drop binario + ejecutar | Drop binario a /tmp/ + ejecutar |
| Antiforense | Ventanas ocultas, limpieza de archivos temporales | Temp oculto .scpt | /tmp/ oculto. Archivos XXXXXX |
Atribución
El binario Mach-O de macOS entregado por el gancho de postinstalación plain-crypto-js presenta una superposición significativa con WAVESHAPER, una puerta trasera de C++ rastreada por Mandiant y atribuida a UNC1069, un clúster de amenazas vinculado a la RPDC.
Conclusión
Esta campaña demuestra la continua atractividad del ecosistema de npm como vector de ataque a la cadena de suministro. Al comprometer una sola cuenta de mantenedor en uno de los paquetes más confiables del ecosistema JavaScript, el atacante obtuvo un mecanismo de entrega con potencial de alcance en millones de entornos.
El indicador de detección más fiable del kit de herramientas es también su elección de diseño más curiosa: la cadena de agente de usuario de IE8/Windows XP codificada de forma idéntica en las tres variantes de plataforma. Aunque proporciona una huella de protocolo consistente para el enrutamiento C2 en el lado del servidor, es fácilmente detectable en cualquier red moderna — y es una anomalía inmediata en hosts de macOS y Linux.
Elastic Security Labs continuará monitorizando este grupo de actividades y actualizará esta publicación con cualquier hallazgo adicional.
MITRE ATT&CK
Elastic usa el framework MITRE ATT&CK para documentar tácticas, técnicas y procedimientos comunes que las amenazas persistentes avanzadas emplean contra las redes empresariales.
Táctica
La táctica representa el porqué de una técnica o subtécnica. Es el objetivo táctico del adversario: la razón para realizar una acción.
Técnicas
Las técnicas representan cómo un adversario logra un objetivo táctico mediante la realización de una acción.
- Compromiso de la cadena de suministro: Comprometer dependencias de software
- Intérprete de comandos y secuencias de comandos: JavaScript
- Intérprete de comandos y scripting: PowerShell
- Intérprete de comandos y scripts: AppleScript
- Command and Scripting Interpreter: Unix Shell
- Command and Scripting Interpreter: Python
- Ejecución automática de arranque o inicio de sesión: Claves de ejecución del registro
- Información o archivos ofuscados
- Enmascaramiento
- Archivos y directorios ocultos
- Inyección de proceso
- Indicator Removal: File Deletion
- Detección de información del sistema
- Descubrimiento de procesos
- Descubrimiento de archivos y directorios
- Application Layer Protocol: Web Protocols
- Puerto no estándar
- Codificación de datos: Codificación estándar
- Transferencia de herramientas de ingreso
Observaciones
En esta investigación se discutieron los siguientes observables.
| Observable | Tipo | Nombre | Referencia |
|---|---|---|---|
617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101 | SHA-256 | 6202033.ps1 | Carga útil de Windows |
92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a | SHA-256 | com.apple.act.mond | Carga útil de MacOS |
fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf | SHA-256 | ld.py | Carga útil de Linux |
sfrclak[.]com | Dominio | C2 | |
142.11.206[.]73 | IPv4-ADDR | C2 |
Referencias
A lo largo de la investigación anterior se hizo referencia a lo siguiente:
