12 puntos por GN⁺ 29 일 전 | 2 comentarios | Compartir por WhatsApp
  • Se publicaron en npm dos versiones maliciosas del cliente HTTP axios ampliamente usado, que al instalarse distribuían un troyano de acceso remoto (RAT)
  • Los atacantes robaron las credenciales de la cuenta del mantenedor para eludir GitHub Actions y subir manualmente los paquetes maliciosos
  • Las versiones maliciosas incluían una dependencia falsa plain-crypto-js@4.2.1 que instalaba el RAT mediante un script postinstall y luego borraba rastros
  • El RAT infecta macOS, Windows y Linux, y se comunica con el servidor C2 (sfrclak.com:8000) para descargar cargas adicionales
  • npm y GitHub retiraron rápidamente las versiones maliciosas, pero volvió a quedar en evidencia la importancia de reforzar la seguridad de la cadena de suministro y proteger las credenciales

Resumen del ataque a la cadena de suministro de axios en npm

  • El 31 de marzo de 2026 se publicaron en npm dos versiones maliciosas de la biblioteca cliente HTTP axios ampliamente utilizada (axios@1.14.1, axios@0.30.4)
  • Los atacantes robaron las credenciales npm de un mantenedor principal de axios, eludieron el pipeline CI/CD de GitHub Actions y publicaron manualmente los paquetes maliciosos
  • Ambas versiones insertaron una dependencia falsa plain-crypto-js@4.2.1; este paquete instalaba un troyano de acceso remoto (RAT) mediante un script postinstall
  • El RAT apunta a macOS, Windows y Linux, y se comunica con el servidor C2 (Command and Control) (sfrclak.com:8000) para descargar una carga de segunda etapa
  • Tras la instalación, borra el código malicioso y sus rastros, y reemplaza el archivo por un package.json limpio para evadir la detección forense

Línea de tiempo del ataque

  • 30 de marzo 05:57 UTC: se publica plain-crypto-js@4.2.0 (versión legítima)
  • 30 de marzo 23:59 UTC: se publica plain-crypto-js@4.2.1 (versión maliciosa), con gancho postinstall
  • 31 de marzo 00:21 UTC: se publica axios@1.14.1, con la dependencia maliciosa insertada
  • 31 de marzo 01:00 UTC: se publica axios@0.30.4, con la misma dependencia maliciosa insertada
  • 31 de marzo 03:15 UTC: npm elimina las dos versiones maliciosas
  • 31 de marzo 04:26 UTC: npm reemplaza plain-crypto-js por un stub de contención de seguridad (0.0.1-security.0)

Descripción general de axios

  • axios es el cliente HTTP más usado en el ecosistema JavaScript, y se utiliza tanto en Node.js como en navegadores
  • Con más de 300 millones de descargas semanales, una sola versión maliciosa ya tiene potencial de causar daños a gran escala
  • Para un desarrollador común es difícil darse cuenta de que se instala código malicioso durante npm install

Etapas del ataque

  • Etapa 1 — Compromiso de la cuenta del mantenedor

    • Los atacantes comprometieron la cuenta npm jasonsaayman y cambiaron el correo a ifstap@proton.me
    • Después publicaron builds maliciosos en las ramas de lanzamiento 1.x y 0.x
    • Las versiones legítimas se publican mediante OIDC Trusted Publisher de GitHub Actions, pero axios@1.14.1 se publicó manualmente, sin gitHead ni firma OIDC
    • Se presume que los atacantes usaron un token de acceso npm de larga duración
  • Etapa 2 — Despliegue previo de la dependencia maliciosa

    • plain-crypto-js@4.2.1 fue publicado desde la cuenta nrwise@proton.me
    • Se hacía pasar por crypto-js usando la misma descripción y la misma URL de repositorio
    • Incluía el gancho "postinstall": "node setup.js", que se ejecuta automáticamente al instalar
    • Tras el ataque, reemplazó package.md por package.json para preparar el borrado de evidencia
  • Etapa 3 — Inserción de la dependencia en axios

    • Se agregó plain-crypto-js@^4.2.1 como dependencia de runtime
    • Nunca se importa en el código → dependencia fantasma (phantom dependency)
    • Al ejecutar npm install, se instala automáticamente y corre el script postinstall

Análisis del dropper RAT (setup.js)

  • Técnicas de ofuscación

    • Las cadenas se guardan cifradas en el arreglo stq[] y se descifran con _trans_1 (XOR) y _trans_2 (Base64 + orden inverso)
    • La URL C2 es http://sfrclak.com:8000/6202033
    • Entre las cadenas descifradas hay identificadores de SO (win32, darwin), rutas de archivos y comandos de shell
  • Cargas por plataforma

    • macOS

      • Escribe un AppleScript en /tmp y lo ejecuta con osascript
      • Descarga el binario RAT desde el C2, lo guarda en /Library/Caches/com.apple.act.mond y lo ejecuta
      • El nombre del archivo se disfraza como si fuera un daemon del sistema de Apple
    • Windows

      • Busca la ruta de PowerShell y copia el archivo a %PROGRAMDATA%\\wt.exe
      • Mediante VBScript descarga y ejecuta un RAT en PowerShell desde el C2
      • Los archivos temporales (.vbs, .ps1) se eliminan tras ejecutarse
    • Linux

      • Descarga /tmp/ld.py con curl y lo ejecuta con nohup python3
      • El archivo /tmp/ld.py permanece en el sistema
      • En las tres plataformas usa cuerpos POST a packages.npm.org/product0~2 para disfrazarse como tráfico legítimo de npm
  • Autoborrado y ocultamiento

    • Elimina setup.js y package.json
    • Reemplaza package.md por package.json para hacerse pasar por un paquete legítimo
    • Después de eso, npm audit o una revisión manual ya no lo detectan
    • Aun así, la sola presencia de node_modules/plain-crypto-js/ es evidencia de infección

Verificación de ejecución con StepSecurity Harden-Runner

  • Harden-Runner registra en tiempo real eventos de red, procesos y archivos dentro de GitHub Actions
  • Al instalar axios@1.14.1 se detectaron dos conexiones al C2 (curl, nohup)
    • La primera conexión ocurrió 2 segundos después de iniciar npm install
    • La segunda ocurrió 36 segundos después y siguió ejecutándose como proceso en segundo plano
  • El análisis del árbol de procesos mostró que el proceso nohup quedó huérfano bajo PID 1 (init), confirmando que seguía en ejecución
  • En los logs de eventos de archivos, package.json fue sobrescrito dos veces
    • Primera: durante la instalación se escribió la versión maliciosa
    • Segunda: 36 segundos después se reemplazó por un stub limpio

Indicadores de compromiso (IOC)

  • Paquetes npm maliciosos

    • axios@1.14.1 · shasum: 2553649f232204966871cea80a5d0d6adc700ca
    • axios@0.30.4 · shasum: d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71
    • plain-crypto-js@4.2.1 · shasum: 07d889e2dadce6f3910dcbc253317d28ca61c766
  • Red

  • Rutas de archivos

    • macOS: /Library/Caches/com.apple.act.mond
    • Windows: %PROGRAMDATA%\\wt.exe
    • Linux: /tmp/ld.py
  • Cuentas del atacante

    • jasonsaayman (mantenedor comprometido)
    • nrwise (cuenta creada por el atacante)
  • Versión segura

    • axios@1.14.0 (legítima)

Verificación del impacto y procedimiento de respuesta

  • Revisar npm list axios o package-lock.json para confirmar 1.14.1 / 0.30.4
  • Verificar si existe node_modules/plain-crypto-js
  • Si aparecen archivos del RAT según el sistema operativo, debe considerarse una infección total del sistema
  • Revisar en los logs de CI/CD si se instalaron esas versiones y rotar todas las claves y tokens secretos

Pasos de recuperación

  1. Fijar axios en una versión segura (1.14.0 o 0.30.3)
  2. Eliminar la carpeta plain-crypto-js y reinstalar con npm install --ignore-scripts
  3. Si se detectan rastros del RAT, reconstruir el sistema
  4. Rotar todas las credenciales (AWS, SSH, CI/CD, etc.)
  5. Revisar el pipeline CI/CD y reemplazar secretos
  6. Usar la opción --ignore-scripts en builds automáticos
  7. Bloquear el dominio/IP C2 con el firewall o /etc/hosts

Funciones de StepSecurity Enterprise

  • Harden-Runner

    • Aplica una lista permitida de tráfico saliente en GitHub Actions
    • Bloquea tráfico anómalo y registra logs
    • Puede bloquear de antemano la conexión a sfrclak.com:8000
  • Dev Machine Guard

    • Monitorea en tiempo real los paquetes npm instalados en PCs de desarrolladores
    • Detecta de inmediato equipos con axios@1.14.1 o 0.30.4 instalados
  • npm Package Cooldown Check

    • Aplica un período temporal de bloqueo de instalación a paquetes recién publicados
    • Puede detectar publicaciones maliciosas rápidas como plain-crypto-js@4.2.1
  • Compromised Updates Check

    • Bloquea merges de PR con base en una base de datos en tiempo real de paquetes maliciosos
    • Registra de inmediato axios@1.14.1 y plain-crypto-js@4.2.1
  • Package Search

    • Busca en PR y repositorios de toda la organización dónde se introdujo un paquete específico
    • Permite identificar de inmediato el alcance del impacto (repositorios, equipos, PR)
  • AI Package Analyst

    • Monitorea en tiempo real el registro npm y realiza detección conductual de malware
    • Detectó ambas versiones maliciosas a los pocos minutos de su publicación
  • Threat Center Alert

    • Proporciona alertas de inteligencia de amenazas con resumen del ataque, IOC y procedimiento de respuesta
    • Asegura visibilidad en tiempo real mediante integración con SIEM

Agradecimientos

  • Los mantenedores de axios y la comunidad respondieron rápidamente a través del issue #10604 de GitHub
  • GitHub suspendió la cuenta comprometida y npm eliminó las versiones maliciosas y aplicó el security holder
  • La respuesta coordinada entre mantenedores, GitHub y npm ayudó a minimizar el daño a desarrolladores de todo el mundo

2 comentarios

 
chanapple 29 일 전

Nunca pensé que comprometerían un paquete de este tamaño; lo de axios está más allá de lo imaginable.

 
GN⁺ 29 일 전
Comentarios de Hacker News
  • npm, bun, pnpm y uv ahora admiten configurar un tiempo mínimo antes de aceptar una nueva versión de paquete
    Yo agregué ignore-scripts=true en ~/.npmrc, y solo con esa configuración ya se podía mitigar la vulnerabilidad
    bun y pnpm no ejecutan scripts de lifecycle por defecto
    Ejemplos de configuración para cada package manager:

    • uv: exclude-newer = "7 days"
    • npm: min-release-age=7
    • pnpm: minimum-release-age=10080
    • bun: minimumReleaseAge = 604800
      Curiosamente, cada uno usa una unidad de tiempo distinta
      Si usas agentes LLM, esta configuración puede causar fallas, así que conviene agregar instrucciones relacionadas en AGENTS.md o CLAUDE.md
    • A la frase “cada uno usa una unidad de tiempo distinta” le respondieron con la broma: “¿Es tu primer día usando JavaScript?
    • pnpm fue el primero en introducir esta función. npm solo la admite a partir de la versión 11.10.0, disponible desde el lanzamiento del 11 de febrero de 2026
    • También hubo quien opinó que sería mejor especificar la unidad en el nombre dentro del archivo de configuración, por ejemplo timeoutMinutes en lugar de timeout
    • Puede que npm no admita comentarios. Es posible que min-release-age=7 # days en realidad no funcione
    • En yarn berry se puede configurar en ~/.yarnrc.yml como npmMinimalAgeGate: "3d"
  • A varios les sorprendió enterarse de que Axios estuvo expuesto a un ataque a la cadena de suministro
    Axios no contenía código malicioso internamente, pero se le inyectó una dependencia falsa, plain-crypto-js@4.2.1, que ejecutaba un script de postinstall para instalar un RAT (troyano de acceso remoto)
    Es una buena noticia para quienes usan pnpm o bun y tienen que aprobar manualmente los scripts de postinstall

    • fetch viene incluido por defecto en Node.js recién desde v18, y se consideró estable a partir de v21. Axios existe desde mucho antes y sigue siendo muy usado porque aparece en muchos frameworks y tutoriales
    • Ante la afirmación de que “los usuarios de pnpm/bun están a salvo”, alguien planteó la duda de si en versiones anteriores probablemente ya lo habrían aprobado
    • También surgió la pregunta de si pnpm bloquea incluso los postinstall de dependencias transitivas
  • Se planteó que los package managers son un experimento fallido
    Existen bibliotecas C de alta calidad compuestas por un solo archivo .c, como SQLite, y con ese enfoque se puede evitar el problema de las dependencias transitivas
    La mayor parte de la superficie de ataque surge precisamente de esas dependencias indirectas

    • Hoy los package managers se volvieron un requisito para la adopción de un lenguaje. El problema es la falta de control de calidad y la estructura de incentivos
      Incluso OpenSSL está siendo reescrito por problemas de calidad de código, y en JS es difícil ampliar la librería estándar, así que abundan los polyfills
    • También hubo quien opinó que “la comunidad no va a aceptar una solución que requiera un poco más de esfuerzo”
      En cambio, se propuso endurecer los estándares de calidad en repositorios como npm y permitir el registro solo a mantenedores responsables
    • En desarrollo web la superficie de ataque es mucho más amplia. El enfoque de copiar manualmente dificulta seguir las actualizaciones, así que las notificaciones del package manager siguen siendo útiles
    • También se señaló que NPM es un ecosistema donde los ataques a la cadena de suministro son especialmente graves
    • Hubo una respuesta diciendo que Rust es más seguro que C y que la mayoría de los crates son de alta calidad, así que centrar la discusión en bibliotecas C sería exagerado
  • Salió la broma de que algunos empiezan el día saludando con un resignado: “¿Qué paquete de npm habrán comprometido hoy?

  • Para usuarios de Linux, se recomendó usar bwrap para aislar en sandbox toda la lógica de build de npm, pip, cargo, gradle, etc.
    bwrap ofrece un entorno aislado como Docker, pero sin necesidad de imágenes. Flatpak también se basa en esta tecnología
    En despliegues de servidor, lo importante es el hardening de contenedores, y la clave es tratar el entorno de CI/CD como una zona no confiable
    También sería buena idea usar el mismo sandbox para ejecutar IA

    • Pero también se señaló que este método solo sirve contra ataques vía postinstall. El código puede ejecutarse simplemente con hacer require dentro del programa
    • Incluso apareció alguien que creó un sandbox personal basado en Docker, amazing-sandbox
    • También recomendaron drop, una herramienta de nivel más alto que bwrap
    • Otro comentario dijo que firejail es un sandbox de seguridad más flexible
    • También se apuntó que el reenvío de sockets SSH permite acceso a la clave privada, así que no aporta ventajas de seguridad, y que es mejor usar claves protegidas con contraseña
  • Al ver una y otra vez problemas con dependencias, algunos se preocupan por si al ecosistema de Rust le pasará algo parecido algún día
    Aunque no es fácil inflar la biblioteca estándar, sí hace falta un sistema confiable de garantía de calidad para paquetes

    • Se propuso que los paquetes grandes, como Axios, requieran que varias personas con MFA aprueben la publicación
    • También se predijo que aparecerán servicios comerciales que ofrezcan dependencias verificadas
    • Otra propuesta fue copiar directamente a la base de código las pruebas de las dependencias y hacerlas pasar por un proceso propio de revisión de código
      Se comentó que gracias a la IA este trabajo extra ahora es más viable, y que en realidad así se debió haber hecho desde antes
  • Reglas clave para minimizar la exposición a ataques de cadena de suministro en NPM

    • Usar el modo zero-installs de Yarn
    • Desactivar los scripts de postinstall o revisarlos antes de ejecutarlos
    • Si durante el desarrollo se va a ejecutar código de terceros, hacerlo solo dentro de una VM o contenedor
    • Al agregar paquetes, tomar la popularidad como punto a favor y los commits recientes como señal negativa, y revisar personalmente el código y el historial de cambios
    • Verificar todo el árbol de dependencias y reevaluar la confianza cada vez que se incorpore un nuevo desarrollador
    • También hubo quien sostuvo que solo con el lockfile y la opción --frozen-lockfile ya hay suficiente protección
    • Y no faltó quien dijo que simplemente evita stacks problemáticos, una filosofía sencilla pero poderosa
  • Ante la pregunta “¿cómo se puede evitar por completo este tipo de ataques?”,
    alguien comentó que le gustaría pasarse a Qubes OS para separar por completo el administrador de contraseñas y el entorno de build

    • Un equipo dijo que realiza todo el trabajo relacionado con NPM solo dentro de contenedores de Apple, y que planea mover Python y Rust de la misma manera
      Lo compararon con el equipo de protección personal (PPE) de un laboratorio químico: en el entorno de desarrollo también hace falta aislamiento y protección
      pip ya empezó a bloquear instalaciones fuera de virtualenv, y esperan que en el futuro los package managers ofrezcan una opción para negarse a ejecutarse fuera de un sandbox
    • Otra persona dijo que directamente no instala Node.js/npm en el sistema. Hasta ahora nunca vio un caso en que fuera realmente necesario
  • pnpm y bun ahora ignoran por defecto los scripts de postinstall, pero npm todavía los ejecuta
    Conviene configurar ignore-scripts=true en ~/.npmrc
    npm sigue registrando 80 millones de descargas semanales

  • Se especuló que la filtración de credenciales de este incidente probablemente provino del caso anterior de LiteLLM
    A algunos les inquieta usar Python o Node.js, pero sienten que en realidad es un problema generalizado

    • Configurar un tiempo mínimo antes de aceptar nuevas versiones ayuda, pero aun así muchos dicen que siguen teniendo miedo de actualizar dependencias
    • También hubo quien sostuvo que la causa raíz no fue LiteLLM sino más bien el incidente de Trivy
    • Otra propuesta fue ejecutar todo dentro de contenedores temporales sin root para limitar el alcance de los daños