Volver al blog
21 de septiembre de 2026

Miasma al descubierto: cómo una sola cuenta de npm comprometida se convirtió en un ataque a la cadena de suministro multiecosistema

Viktor Bulanek
Founder & CTO, Penetrify
MSc IT Security · 20+ years in security · 4x Ex-CTO

En junio de 2026, varios investigadores descubrieron una nueva evolución de la familia de malware Mini Shai-Hulud, bautizada como Miasma. A diferencia del malware tradicional de npm, que apunta a un puñado de paquetes, esta campaña demostró cómo comprometer una sola pieza de la infraestructura de desarrollo puede propagarse rápidamente por varios ecosistemas: npm, GitHub Actions e incluso módulos de Go.

Los atacantes no fueron a por los usuarios finales. Fueron a por los publicadores, los pipelines de CI/CD y los mecanismos de confianza que permiten que el software viaje desde el portátil de un desarrollador hasta producción sin que nadie lo vuelva a comprobar por el camino. No existe ningún CVE para nada de esto, porque el código afectado nunca tuvo un defecto. El punto de entrada fue una credencial robada, no una vulnerabilidad.

Conviene señalar algo de entrada: el 12 de mayo de 2026, el grupo que originalmente estaba detrás de esta familia de gusanos, TeamPCP, publicó en GitHub el código fuente completo de Shai-Hulud/Mini Shai-Hulud bajo licencia MIT. Eso cambia la forma de leer esta historia. Las cuatro oleadas posteriores comparten el mismo conjunto de herramientas y las mismas técnicas, pero compartir herramientas ya no implica compartir operador. Una vez que el framework es público, cualquier actor con capacidad técnica puede ejecutar una campaña idéntica a las del propio TeamPCP. Lo que sigue debe leerse como un solapamiento de tácticas, técnicas y procedimientos (TTP), no como una atribución confirmada a un único grupo.

Paquetes afectados (npm y Go)

PaqueteVersión comprometida
hexo-deployer-wrangler1.0.4
hexo-shoka-swiper0.1.10
leo-auth4.0.6
leo-aws2.0.4
leo-cache1.0.2
leo-cdk-lib0.0.2
leo-cli3.0.3
leo-config1.1.1
leo-connector-elasticsearch2.0.6
leo-connector-mongo3.0.8
leo-connector-mysql3.0.3
leo-connector-oracle2.0.1
leo-connector-redshift3.0.6
leo-cron2.0.2
leo-logger1.0.8
leo-sdk6.0.19
leo-streams2.0.1
prism-silq1.0.1
rstreams-metrics2.0.2
rstreams-shard-util1.0.1
serverless-convention2.0.4
serverless-leo3.0.14
solo-nav1.0.1
github.com/verana-labs/verana-blockchain (Go)v0.10.1-dev.20

Socket continúa con la investigación; los nuevos hallazgos se van añadiendo a su página de seguimiento de Miasma.

Si su árbol de dependencias incluye alguno de estos paquetes en las versiones indicadas (o en oleadas comprometidas anteriores de la misma campaña, a principios de junio), trate el entorno como potencialmente comprometido y siga los pasos de remediación inmediata que se describen más abajo.

Cronología

24 de junio de 2026
   ↓
Cuenta de mantenedor de LeoPlatform comprometida
   ↓
23 versiones maliciosas de paquetes publicadas (con segundos de diferencia)
   ↓
La carga útil de binding.gyp se ejecuta durante la instalación
   ↓
Robo de credenciales de desarrolladores y de CI/CD
   ↓
Despliegue de un workflow de GitHub Actions ("Run Copilot")
   ↓
Exfiltración de los secretos robados a repositorios públicos de GitHub
   ↓
Módulo de Go (Verana Blockchain) comprometido mediante una técnica relacionada
   ↓
Investigadores de Socket, Wiz, Sonatype y otros identifican y divulgan la campaña

Es la cuarta oleada de la misma familia de campañas en menos de un mes: apareció por primera vez contra el espacio de nombres de Red Hat en npm el 1 de junio, después llegó una oleada de 57 paquetes el 3 de junio y, finalmente, esta oleada LeoPlatform/Go el 24 y 25 de junio. Como se ha señalado antes, TeamPCP liberó el código del framework del gusano el 12 de mayo, de modo que «misma familia de campañas» refleja TTP compartidas entre estas oleadas, no una prueba confirmada de que haya un único operador detrás de las cuatro.

Qué ocurrió realmente

  • A un mantenedor del ecosistema npm de LeoPlatform le robaron las credenciales de su cuenta o su token de npm, muy probablemente mediante un infostealer.
  • El atacante usó ese acceso robado para publicar versiones troyanizadas de paquetes que el mantenedor ya poseía legítimamente. No hizo falta «hackear» nada en el sentido tradicional, porque el atacante ya disponía de una identidad de publicación de confianza.
  • Cualquiera que instalara una de esas versiones ejecutaba una carga útil oculta que robaba sus propias credenciales.
  • Algunas de esas credenciales robadas pertenecían a entornos de GitHub Actions, lo que permitió al atacante plantar un workflow malicioso y robar secretos de CI/CD directamente desde el runner.
  • Por separado, un módulo de Go fue comprometido con una técnica relacionada pero distinta, lo que demuestra que la campaña no está ligada a ningún lenguaje ni gestor de paquetes en particular.
  • Todo esto se descubrió porque varios equipos independientes de investigación en seguridad estaban vigilando precisamente este patrón, conocido por oleadas anteriores de la misma campaña.

Análisis técnico

Fase 1: mantenedor comprometido

La cuenta de npm czirker, asociada a LeoPlatform, fue comprometida, muy probablemente a través de credenciales filtradas o de un token sustraído por un infostealer que ya se ejecutaba en la máquina del mantenedor.

Fase 2: publicación de paquetes maliciosos

Con el token robado, el atacante publicó 23 versiones maliciosas de paquetes de los ecosistemas LeoPlatform y RStreams en una ventana de seis segundos. Esa velocidad apunta a una herramienta de publicación automatizada mediante scripts, no a una persona subiendo archivos a mano.

El detalle más concreto de toda la campaña está en esta fase: el atacante no se limitó a publicar versiones maliciosas, sino que también redirigió el dist-tag latest de leo-sdk hacia la versión envenenada 6.0.19, mientras que la versión limpia 7.1.21 solo seguía disponible bajo el tag awsv3, mucho menos visible. En la práctica, esto significa que cualquier entorno que ejecutara npm install leo-sdk sin fijar explícitamente una versión semver era dirigido activamente hacia el gusano, aunque existiera una versión limpia en todo momento. La conclusión práctica es contundente: fijar la versión, y no el tag, fue lo que realmente marcó la diferencia entre una instalación limpia y un compromiso.

Fase 3: ejecución durante la instalación

Aquí está la parte ingeniosa. La mayoría de los paquetes maliciosos de npm se detectan porque los escáneres vigilan los campos de scripts preinstall y postinstall de package.json. Miasma los evita por completo y, en su lugar, añade un archivo binding.gyp, la configuración que Node.js utiliza para compilar módulos nativos durante la instalación. Como binding.gyp forma parte del proceso completamente normal de construcción de complementos nativos, ejecuta código durante npm install sin tocar jamás los campos que vigila la mayoría de las herramientas.

Esta técnica tenía dos mitigaciones reales, y merece la pena ser preciso con ambas. La primera ya existía: npm install --ignore-scripts sí suprime el hook implícito node-gyp rebuild que dispara un archivo binding.gyp, según la propia documentación de npm. No es una defensa completa por sí sola, ya que un paquete malicioso puede descargarse y desempaquetarse igualmente, y una reconstrucción posterior sin ese flag puede volver a activarlo, pero sí bloquea esta ruta de ejecución concreta. La segunda mitigación llegó tres semanas tarde: npm v12, publicado en julio de 2026, cierra este vector por defecto y trata el node-gyp rebuild implícito que dispara binding.gyp exactamente igual que cualquier otro script de instalación, bloqueado a menos que el paquete esté explícitamente en una lista de permitidos. La técnica «Phantom Gyp» de Miasma fue, en la práctica, una explotación directa del hueco exacto que npm v12 se diseñó para cerrar.

Fase 4: recolección de secretos

Una vez en ejecución, la carga útil recopila un amplio conjunto de credenciales, entre ellas:

  • Tokens de npm y PyPI
  • Personal Access Tokens de GitHub y tokens OIDC de GitHub
  • Credenciales de AWS, Azure y GCP
  • Claves SSH
  • Rutas de configuración y credenciales de Claude Code, Cursor y otros asistentes de programación con IA
  • Claves de API de OpenAI
  • Tokens de Slack y Twilio, archivos de autenticación de Docker, configuraciones de Kubernetes, datos de Vault e historial de la shell

Fase 5: abuso de GitHub

El malware deposita un workflow de GitHub Actions (visto con el nombre «Run Copilot») que extrae secretos directamente de la memoria del runner de CI durante la ejecución. Los datos robados se cifran y se envían a repositorios públicos de GitHub recién creados que actúan como buzones muertos; en el momento de escribir esto, más de 550 repositorios llevan el marcador de exfiltración de la campaña. En un incidente relacionado ocurrido el mismo día, un atacante hizo un force-push de un commit malicioso a una GitHub Action muy utilizada y redirigió las etiquetas de versión para que apuntaran a él, de modo que cualquier workflow que se ejecutara contra esas etiquetas a partir de ese momento ejecutaba la carga útil del atacante directamente dentro del runner de GitHub Actions.

Fase 6: propagación

Máquina del desarrollador comprometida
   ↓
Robo de las credenciales de GitHub del desarrollador
   ↓
Secretos del pipeline de CI/CD recolectados desde el runner
   ↓
Token de npm robado usado para publicar más paquetes maliciosos
   ↓
Más desarrolladores instalan los paquetes comprometidos
   ↓
(se repite)

Cada compromiso exitoso se convierte en la plataforma de lanzamiento del siguiente. Eso es lo que convierte esto en una campaña autopropagable y no en un incidente aislado.

Por qué este ataque es diferente

Ataques a la cadena de suministro clásicosMiasma
Un solo paqueteTodo el ecosistema
Robo de criptomonedasRobo masivo de credenciales
Un lenguajeVarios lenguajes (npm y ahora Go)
Un mantenedorLa infraestructura de desarrollo en su conjunto
Malware estáticoAutopropagable y en evolución entre oleadas

Los incidentes anteriores en la cadena de suministro de npm eran en gran medida golpes rápidos: comprometer un paquete, robar claves de monederos o tokens de sesión y seguir adelante. Miasma se comporta más bien como un gusano con modelo de negocio. Trata cada infección exitosa como combustible para la siguiente oleada y, de forma demostrable, sigue evolucionando después de más de tres semanas y cuatro oleadas distintas.

Por qué GitHub Actions se convirtió en objetivo

GitHub Actions ocupa un punto especialmente valioso en la cadena de suministro de software: alberga tokens OIDC utilizados para publicar de forma fiable y sin credenciales en proveedores de nube y registros, almacena secretos de CI de larga duración y, por diseño, ejecuta tareas de forma automatizada y sin supervisión. Comprometer un runner de CI no expone solo el portátil de un desarrollador: expone cada pipeline de despliegue, cada publicación en un registro y cada credencial de nube que toque ese workflow. El radio de impacto es mucho mayor que el del entorno local de cualquier desarrollador individual, y por eso precisamente la campaña construyó herramientas específicas para atacarlo.

Por qué importa la expansión a Go

La mayor parte de la cobertura de esta campaña se centró en npm, ya que es donde reside la mayoría de los paquetes. Pero el compromiso del módulo de Go es posiblemente el detalle más importante de toda la historia, y es el que la mayoría de los análisis pasan por alto.

La muestra de Go no utiliza binding.gyp. No depende en absoluto de la resolución de módulos ni de los mecanismos de compilación de Go. En su lugar, un desarrollador que simplemente clone el repositorio o lo abra en un IDE o asistente de programación con IA de confianza puede activar la carga útil únicamente a través de la configuración del proyecto.

Eso revela algo importante: a los atacantes no les interesa en el fondo JavaScript, ni npm, ni la mecánica de ningún gestor de paquetes concreto. Les interesan los desarrolladores, allí donde los desarrolladores depositan su confianza en herramientas automatizadas, ya sea un script de instalación, un paso de compilación o un IDE que abre una carpeta. El lenguaje no importa. Las credenciales de los desarrolladores sí.

La lección de fondo

El enfoque antiguo:

Escaneamos las dependencias
   ↓
Parcheamos los CVE conocidos
   ↓
Estamos a salvo

La realidad actual:

Un desarrollador es comprometido
   ↓
El CI/CD es comprometido
   ↓
Se publica un paquete legítimo con código malicioso
   ↓
Todos los consumidores aguas abajo lo instalan
   ↓
No hace falta ningún CVE en ningún paso

El escaneo de dependencias responde a la pregunta «¿tiene este código un fallo conocido?». Miasma plantea una pregunta completamente distinta que la mayoría de los programas de seguridad no están preparados para responder: «¿confío en las personas y los sistemas automatizados que tienen permiso para publicar código en mi entorno, ahora mismo, hoy?».

Oportunidades de detección

Más que preguntarse «cómo detenerlo», resulta más útil preguntarse dónde tuvo un defensor una oportunidad realista de darse cuenta de que algo iba mal. Aquí es donde la mayor parte de la cobertura sobre Miasma se queda corta, así que merece la pena analizarlo con más detalle.

Velocidad de publicación. Publicar 23 versiones de paquetes en una ventana de seis segundos no es algo que haga un humano. Cualquier organización que opere su propio mirror del registro de npm, o que vigile los eventos del registro público para los paquetes de los que depende, podría marcar automáticamente ráfagas de publicación como esta. Un incremento de versión aislado es normal. Dos docenas en cuestión de segundos son una señal por sí mismas, independientemente de lo que contenga el paquete.

Cambios de dist-tag sin que los usuarios pidieran un cambio de versión. El secuestro de leo-sdk funcionó precisamente porque latest pasó a apuntar a un número de versión inferior y de aspecto más antiguo (6.0.19), mientras que una versión limpia más reciente (7.1.21) quedaba bajo otro tag. Cualquier herramienta que compare los dist-tags de un paquete a lo largo del tiempo, y no solo su lista de versiones, habría detectado que latest apuntaba hacia atrás en la secuencia de versiones, algo que casi nunca es un comportamiento legítimo.

Artefactos de compilación que no coinciden con el código fuente. El paquete leo-sdk comprometido pasó de un index.js de 10 KB en la versión limpia a un index.js de 5 MB en la versión envenenada, con un archivo binding.gyp que no existe en ningún lugar del repositorio real del proyecto en GitHub. Comparar lo que se publica en el registro con lo que está confirmado en el control de versiones, paquete a paquete, habría sacado esto a la luz de inmediato. La mayoría de las organizaciones nunca realiza esta comparación.

Compilaciones nativas inesperadas en paquetes de JavaScript puro. leo-sdk es un SDK de JavaScript sin ninguna necesidad legítima de compilación nativa. Que aparezca un binding.gyp en un paquete que nunca ha incluido uno, o que no tiene ninguna razón plausible para necesitarlo, es por sí solo una fuerte señal de anomalía, mucho antes de que nadie inspeccione lo que hace realmente el archivo.

Nuevos archivos de workflow que aparecen sin una pull request asociada. Un archivo de workflow llamado «Run Copilot» que aparece en .github/workflows/ sin PR vinculada, sin revisión y sin un autor que nadie reconozca es exactamente el tipo de cambio para el que existen la protección de ramas y las revisiones obligatorias, siempre que se apliquen realmente a los archivos de workflow en particular.

Tráfico saliente hacia repositorios de GitHub recién creados. Como la exfiltración dependía de repositorios públicos recién creados que actuaban como buzones muertos, que runners de CI o máquinas de desarrolladores realicen llamadas salientes a la API para crear nuevos repositorios, o hagan push a repositorios que no existían una hora antes, es un patrón de comportamiento detectable y bastante inusual.

Uso de OIDC y tokens fuera de su patrón habitual. Que un token OIDC de GitHub o un token de publicación de npm se use desde un rango de IP, un workflow o una franja horaria desde los que nunca se había usado antes es una señal clásica de anomalía de identidad, y una que no requiere saber nada sobre esta campaña concreta para detectarla.

Ninguna de estas medidas requiere una base de datos de CVE ni conocimiento previo de Miasma en particular. Requieren observar de verdad cómo la confianza, el comportamiento de publicación y la identidad se mueven por su entorno a lo largo del tiempo, y no solo lo que figura en un archivo de manifiesto en un instante concreto.

Qué deberían hacer las organizaciones

De inmediato

  • Rotar las credenciales de cualquier entorno que haya instalado un paquete afectado, pero solo después de eliminar la versión maliciosa y confirmar que no queda persistencia, no antes.
  • Actualizar o eliminar las versiones afectadas de los paquetes en lockfiles, cachés, contenedores y runners de CI, no solo en el manifiesto de primer nivel.
  • Revocar y volver a emitir cualquier token de GitHub, configuración OIDC o token de npm que pueda haber quedado expuesto.
  • Fijar las GitHub Actions por hash de commit en lugar de por etiquetas de versión mutables.

A medio plazo

  • Introducir listas de paquetes permitidos para lo que se acepta en CI/CD y en las compilaciones de producción.
  • Exigir procedencia (provenance) y firma de paquetes allí donde estén disponibles.
  • Mantener un inventario de software (SBOM) real, no una instantánea puntual.
  • Añadir un periodo de espera antes de que las versiones recién publicadas de un paquete se consideren de confianza en una compilación.

A largo plazo

  • Validación continua de lo que realmente se ejecuta en su entorno, en lugar de auditorías periódicas.
  • Monitorización de repositorios y de CI/CD para detectar cambios inesperados en workflows o en el comportamiento de publicación.
  • Análisis continuo de rutas de ataque: no solo «¿es vulnerable este paquete?», sino «¿hasta dónde podría llegar alguien si este paquete fuera malicioso?».
  • Monitorización de la identidad de los desarrolladores, ya que en esta campaña la identidad fue la verdadera superficie de ataque.

Por qué esto importa a Penetrify

Miasma no fue un problema de npm. Fue un problema de identidad. Un problema de CI/CD. Un problema de confianza. Un problema de rutas de ataque.

Los atacantes no descubrieron una vulnerabilidad. Descubrieron un camino desde la estación de trabajo de un único desarrollador hasta miles de sistemas aguas abajo, y ese camino existía por completo dentro de relaciones que la mayoría de los programas de seguridad nunca monitorizan de verdad: qué personas pueden publicar código, qué tokens pueden desencadenar un despliegue, qué workflows se ejecutan sin supervisión con acceso real a producción.

npm v12 cierra el vector de ejecución concreto del que dependía Miasma. No hace nada respecto al problema de identidad que hay debajo. Como dijo Moshe Siman Tov Bustan, de OX Security, en la cobertura del lanzamiento de npm v12: una vez que un atacante controla las credenciales de un mantenedor legítimo, bloquear los scripts de instalación no sirve de nada, porque el código malicioso se distribuye como una versión firmada y de confianza.

Ese es todo el argumento que ha defendido este artículo, expresado de forma más sencilla de lo que nosotros podríamos. La seguridad moderna de la cadena de suministro de software tiene que ir más allá del escaneo de dependencias y avanzar hacia una comprensión continua de cómo evolucionan las relaciones de confianza entre repositorios, pipelines de compilación, identidades y entornos de producción, y no limitarse a qué CVE aparecen en un archivo de manifiesto.

Fuentes

Frequently Asked Questions

¿Qué tipos de vulnerabilidades detecta Penetrify?

Penetrify detecta todas las categorías de vulnerabilidades del OWASP Top 10, incluyendo inyección SQL, XSS, CSRF, IDOR, autenticación rota, configuraciones de seguridad incorrectas y exposición de datos sensibles. También prueba la seguridad de APIs, la gestión de sesiones y configuraciones incorrectas comunes en Supabase, Firebase y Bubble.

¿Cuánto tiempo dura un test de penetración con IA?

Un escaneo rápido se completa en 15–30 minutos. Un escaneo estándar dura 1–2 horas con mayor cobertura. Un escaneo profundo puede durar varias horas en aplicaciones complejas.

¿Qué incluye un informe de Penetrify?

Cada informe incluye un resumen ejecutivo, una puntuación general de seguridad, hallazgos clasificados por severidad (Crítico, Alto, Medio, Bajo), pasos de reproducción detallados y orientación concreta de remediación escrita para desarrolladores, no para responsables de cumplimiento.

Related articles

Testing de Cumplimiento Multi-Framework: Un Solo Compromiso, Múltiples Auditores
¿Sujeto a SOC 2, PCI DSS e HIPAA simultáneamente? Descubra cómo evitar pruebas redundantes y satisfacer a todos los auditores con un único programa.
¿Qué es un Pen Test? Una guía paso a paso sobre cómo funciona
¿Es su aplicación web realmente segura? La idea de una sola vulnerabilidad oculta que provoque una filtración de datos catastrófica es suficiente para que cualquier fundador no pueda dormir por la noche. Sabe que debe tomar medidas, pero el mundo de la ciberseguridad puede parecer un laberinto.
Análisis de Cumplimiento PCI DSS: La Guía 2026 para la Seguridad Automatizada
El 14 de marzo de 2025, un minorista de primer nivel descubrió que una simple regla de firewall mal configurada durante un *push* del viernes por la tarde invalidó tres meses de preparación para el cumplimiento normativo en menos de seis minutos. Seguramente ya sabrá que los escaneos trimestrales tradicionales de cumplimiento PCI DSS se sienten como revisar el velocímetro...

Explore more