El paquete sí era de Red Hat
Entre finales de mayo y el 1 de junio de 2026, un gusano llamado Miasma metió código robacredenciales en 32 paquetes bajo el ámbito npm @redhat-cloud-services — el propio namespace de Red Hat, ~117.000 descargas semanales, firmado por el pipeline de publicación real de Red Hat. No había ningún typosquat que detectar ni ningún mantenedor desconocido que señalar. La señal de confianza era el nombre del proveedor en el ámbito, y el nombre del proveedor es exactamente aquello con lo que el atacante se coló. Aquí está por qué «prefiere editores reputados» dejó de ser una defensa, y qué cambia cuando el agente que ejecuta la instalación vive en una VM Bromure por perfil.
Los ataques de cadena de suministro que documentamos en mayo tenían
una pista. Una URL de Git donde debería ir un rango de versiones. Un
runtime de Bun apareciendo de la nada. Una dependencia opcional que
fallaba a propósito. Miasma no tiene ninguna pista. Los paquetes eran
@redhat-cloud-services/sources-client y treinta y uno de sus
hermanos — genuinamente de Red Hat, bajo el propio ámbito npm de Red
Hat, publicados por el propio pipeline de Red Hat con una firma
válida. Nada estaba falsificado. El atacante no necesitaba falsificar
nada. El nombre en el ámbito era todo el exploit.
Un agente de codificación que resuelve @redhat-cloud-services/vulnerabilities-client
no duda, y tú tampoco lo harías. Es una dependencia de primera mano de
uno de los mayores proveedores empresariales de la industria. No hay
mantenedor que examinar, porque el mantenedor es Red Hat. No hay
ningún nombre que escudriñar en busca de una letra transpuesta, porque
el nombre está escrito exactamente como debería estar. Cada heurística
que un desarrollador cuidadoso o un agente cuidadoso aplica antes de
ejecutar npm install da verde. Así que la instalación corre, y un
hook preinstall se dispara, y el hook es un blob de 4,2 megabytes de
JavaScript ofuscado que empieza a leer el sistema de archivos en busca
de llaves.
Todo el incidente es una demostración de un hecho incómodo: editor reputado no es un control de seguridad. Parece serlo. La mayoría de los consejos sobre cadena de suministro de los últimos tres años se apoyan en él. Y el 29 de mayo no compró absolutamente nada.
Qué hizo Miasma.
La campaña — la cadena Miasma: The Spreading Blight aparece por
primera vez en un commit fechado el 29 de mayo de 2026, según
OX Security —
fue detectada por Aikido y OX Security y
posteriormente analizada por Socket, JFrog, Wiz, ReversingLabs,
Microsoft y otros. BleepingComputer
y The Hacker News
la cubrieron ambos el 1 de junio.
La forma, reducida a su mecánica:
- El punto de entrada fue una cuenta de GitHub comprometida de un
empleado de Red Hat, usada para empujar commits maliciosos a los
repositorios fuente de
@redhat-cloud-services. - Un workflow de GitHub Actions llevaba un script
_index.jsque se autenticaba en el endpoint de publicación confiable de npm usando un token OIDC — el mismo mecanismo sin llaves que npm ahora recomienda por encima de los tokens de publicación de larga duración. Desde el lado de npm, la CI de Red Hat publicó los paquetes de Red Hat. La firma era real. - Los paquetes publicados llevaban un hook
"preinstall": "node index.js"y una carga útil ofuscada de aproximadamente 4,2 MB. - Al instalarse, la carga útil barría en busca de secretos de GitHub
Actions, credenciales de AWS, credenciales de Google Cloud,
service principals de Azure, tokens de HashiCorp Vault, tokens de
cuenta de servicio de Kubernetes, tokens de publicación de npm y
PyPI, llaves SSH, credenciales de Docker, llaves GPG y archivos
.env— luego cifraba y exfiltraba todo lo que encontrara. - Se autopropagaba usando el acceso robado para hacer commits a través
de la API de GitHub, leyendo archivos
action.ymlpor GraphQL y escribiendo nuevos workflows de vuelta mediante mutaciones, de modo que los cambios aparecían, en las propias palabras de Red Hat en el log de commits, verified y signed.
En total, fueron afectados 32 paquetes en 96 versiones, paquetes
con aproximadamente 117.000 descargas semanales, y la campaña más
amplia tocó 309 repositorios de GitHub. La evaluación de Socket fue
que esto es «efectivamente una campaña Mini Shai-Hulud: usa las mismas
tácticas centrales de ejecución en tiempo de instalación, cosecha de
credenciales, focalización de CI/CD, exfiltración cifrada y propagación
potencial hacia abajo.» La declaración de Red Hat fue que «los
paquetes están estrictamente limitados al desarrollo interno, y el
código malicioso nunca se publicó para consumo de clientes» — lo cual
es cierto, y también no es mucho consuelo para los desarrolladores y
runners de CI que tiraron de @redhat-cloud-services/* como
dependencia transitiva en la ventana antes de que los paquetes fueran
retirados.
La defensa que todo el mundo recomienda es la que se rompió.
Escribimos sobre un gusano similar
hace tres semanas — el compromiso
de TanStack, donde la pista delatora era un script prepare colgando
de una URL de Git fijada y un runtime de Bun que apareció de la nada.
La lección honesta de aquel post fue: no confíes en el lockfile, no
confíes en la firma. Miasma es la siguiente vuelta del mismo tornillo,
y vale la pena ser preciso sobre qué es diferente, porque la
diferencia es la cuestión entera.
El conjunto estándar de higiene de cadena de suministro tiene tres
peldaños. Fija tus versiones. Verifica la procedencia. Prefiere
editores reputados. Miasma atraviesa los tres de frente. Fijar no hace
nada, porque las versiones maliciosas son las versiones publicadas.
La procedencia no hace nada, porque la procedencia es válida — el flujo
de publicación confiable OIDC genuinamente era la CI de Red Hat, y
hacia abajo el gusano acuñó commits de workflow que el propio GitHub
marcó como verified y signed. Y el tercer peldaño, prefiere editores
reputados, no solo es derrotado aquí — es la superficie de ataque. La
reputación del ámbito @redhat-cloud-services es la razón por la que
los paquetes se tiran sin una segunda mirada. Cuanto más confiable es
el namespace, más útil le resulta a quienquiera que se apodere de él.
No hay ninguna versión de «lee el paquete con más cuidado» que atrape esto. El paquete está bien. El paquete es de Red Hat. El problema es que la ejecución de código en tiempo de instalación con las credenciales ambientales del desarrollador es el contrato, y un nombre confiable no hace nada por cambiar lo que ese código puede tocar una vez que corre.
La misma instalación dentro de Bromure Agentic Coding.
Bromure Agentic Coding ejecuta tu agente de
codificación dentro de una VM Linux por perfil — su propio kernel,
su propio sistema de archivos, su propia pila de red, sobre el
framework de Virtualización de Apple. Un perfil es un ámbito de trabajo
coherente: este cliente, este producto interno, esta biblioteca
open-source. El agente hace sus npm install ahí dentro, y el host —
tu keychain real, tus credenciales cloud reales, tus llaves SSH
reales — está al otro lado de una frontera reforzada por hardware que
el hook preinstall no puede cruzar.
Las credenciales no viven en el perfil. Viven en el host, detrás de un
intermediario de credenciales.
Cuando el agente necesita empujar un commit o publicar un paquete, no
lee un token del sistema de archivos del invitado — no hay ninguno que
leer. Le pide al intermediario, por un socket de dominio Unix, que
use una credencial en su nombre. El intermediario guarda la clave
privada real de la GitHub App, acuña un token de instalación de corta
duración con ámbito al repo en el que el agente ya estaba trabajando,
y — para un perfil configurado para requerirlo — presenta un aviso de
autorización que el desarrollador responde en el host antes de que la
petición salga. El token se acuña y se adjunta a la petición saliente
enteramente del lado host; nunca entra en la memoria ni en el disco del
invitado. El principio, que es tan viejo como ssh-agent:
intermedia el uso de la credencial, nunca su valor.
Así que recorramos el barrido de Miasma a través de esa frontera.
node index.js lee ~/.aws/credentials y encuentra un stub o nada.
Lee ~/.npmrc y no encuentra ningún token de publicación. Lee el
entorno en busca de $GITHUB_TOKEN y no encuentra nada que robar — el
token de instalación se acuña y se gasta en el host, nunca se escribe
en el invitado, y el intermediario solo acuña uno cuando el
desarrollador responde el aviso de autorización. El keyring GPG, la
clave privada SSH, el token de Vault, el kubeconfig: del lado host,
intermediados si están expuestos siquiera, ausentes si no. La carga
útil de 4,2 MB corre hasta completarse exactamente como fue diseñada.
Solo que exfiltra un invitado que nunca tuvo las llaves del
desarrollador.
El push que el proxy se niega a reenviar.
Robar la llave es solo la mitad de Miasma. La otra mitad es la propagación: usó el acceso robado a GitHub para hacer commit de workflows envenenados de vuelta a través de la API, y eso es lo que convirtió una mala instalación en 309 repositorios. El intermediario maneja el robo. Los Guardrails, añadidos en Bromure Agentic Coding 2.0, manejan el mal uso.
Los Guardrails son un motor de políticas del lado host que vive dentro
del mismo proxy MITM por el que ya fluye el tráfico intermediado, de
modo que un agente comprometido en el invitado no puede rodearlos. Cada
petición se clasifica por lo que realmente le hace al recurso, y cada
recurso — GitHub, AWS, Kubernetes, registros de Docker, DigitalOcean,
GitLab, Bitbucket, bases de datos alojadas — puede ponerse en off,
bloquear destructivos o solo lectura. Pon el guardrail de GitHub de
un perfil en modo solo lectura y un git push — el git-receive-pack
que el gusano necesita para escribir su workflow de vuelta — devuelve
un 403 duro, mientras que git fetch sigue funcionando. Un DELETE
contra la API de Kubernetes, un borrado de manifiesto en un registro,
una llamada Terminate* a EC2: el mismo trato. El agente solo ve un
fallo normal de la API. El paso de propagación de Miasma es una
escritura que el proxy declina reenviar, haya llegado o no el agente
cerca de una credencial real.
¿Y qué hay de la persistencia?
Aquí es donde el modelo por perfil es honesto sobre un costo. Miasma es un gusano; toda su ambición es regresar. En una fantasía de disco desechable, eso lo descartas — el disco se va después de la tarea. Un perfil Bromure es de larga duración, así que una carga útil que se escribe a sí misma en un script de arranque dentro del perfil puede sobrevivir hasta la siguiente sesión del agente en ese perfil. No vamos a pretender lo contrario.
Lo que esa persistencia hereda, sin embargo, es un invitado sin llaves del host y un intermediario que solo habla en tokens de corta duración y ámbito limitado. El gusano se despierta en el mismo sandbox en el que murió. Puede leer los mismos stubs. Puede pedirle al intermediario que use una credencial para el único repo para el que este perfil está autorizado — y el desarrollador todavía tiene que responder el aviso de autorización para que eso vaya a algún lado, con los Guardrails libres de rechazar el push de plano. No puede alcanzar el keychain del host, los otros perfiles, ni las credenciales cloud del desarrollador, porque esas nunca estuvieron dentro de la frontera para empezar. La persistencia compra presencia continuada en una caja sin nada dentro.
Y cada parte de eso — el preinstall disparándose, node index.js
cargando un blob de varios megabytes, el archivo escrito en una ruta de
arranque, el intento de salida — aterriza en el trazo de sesión a
nivel de hipervisor. Cuando
Aikido publica los indicadores a la mañana siguiente, la pregunta
«¿este perfil llegó a ejecutar Miasma alguna vez?» es un grep, no una
intervención de respuesta a incidentes.
Donde esto no te salva.
El ámbito del intermediario es todo el juego.
Si un perfil está aprovisionado hoy para publicar en tu ámbito npm, y ese perfil instala Miasma hoy, el intermediario lo dejará publicar. La intermediación funciona porque la concesión es estrecha y de corta duración. Un perfil que no necesita publicar no debería poder hacerlo. Dale ámbito a propósito.
No revisa el diff.
Miasma se propagó haciendo commit de workflows que aparecían verified y signed. Un guardrail de GitHub en solo lectura bloquea el push de plano — pero un perfil que legítimamente necesita empujar corre en modo bloquear-destructivos, y los Guardrails clasifican por método, no por lo que hay en el diff. Ni el aislamiento ni un guardrail a nivel de método impiden que a un agente lo convenzan de hacer commit de un workflow envenenado que tiene permitido empujar. Lee el diff. El trazo te dice qué diffs leer.
El portapapeles se comparte por defecto.
Bromure viene con el intercambio de portapapeles entre host e invitado activado, porque pegar un stack trace en un chat es algo que los humanos hacen todo el día. Para un perfil sensible, aísla el portapapeles. El control existe; simplemente no es el defecto.
El trazo es un log de auditoría, no un IDS.
El trazo de sesión registra el preinstall, el blob, la salida. No decide, por sí solo, que el destino es hostil. Captura lo suficiente para que, una vez que alguien nombre el indicador, tu respuesta esté a dos segundos de distancia.
El siguiente ámbito ya es de confianza.
La lección del gusano de TanStack fue que el lockfile y la firma no son
defensas. Miasma añade el incómodo corolario: tampoco lo es el editor.
El ámbito @redhat-cloud-services no hizo nada malo por ser de
confianza — ser de confianza es el propósito entero de un namespace de
proveedor, y es exactamente lo que lo hizo digno de atacar. La próxima
campaña se colará en un ámbito en el que confías igual de mucho, firmado
con igual validez, por un pipeline que genuinamente era del proveedor
hasta el momento mismo en que dejó de serlo.
No puedes arreglar eso confiando con más cuidado. Lo arreglas
disponiendo las cosas de modo que «qué ámbito publicó esto» deje de ser
la pregunta de la que depende tu keychain. Bromure Agentic
Coding es la configuración donde el agente hace sus
instalaciones dentro de una VM por perfil, las credenciales reales se
quedan en el host detrás de un intermediario, y lo peor que un hook
preinstall puede hacer es exfiltrar una caja que nunca tuvo tus
llaves. Es gratis, open-source, y se entrega hoy.