CI/CD publicó el cargador de botnet AsyncAPI
Cuatro paquetes AsyncAPI-npm se ejecutaron el 14. julio 2026 con OIDC válida y aun así entregaron un cargador de bot de múltiples etapas. El punto de entrada estuvo en la CI: una workflow de pull_request_target mal configurada, luego un push al branch de release mediante credenciales vinculadas al bot. npm Trusted Publishing siguió siendo técnicamente limpio y no detectó la vulnerabilidad de confianza anterior.
Lo más importante en resumen
- El inicio estuvo en la CI-Misconfig, muy antes del token del registro. pull_request_target con checkout del Head no confiable, amplio GITHUB_TOKEN y posterior push del bot: Exfiltración de secretos y compromiso de rama van juntos.
- Carga en require(), la runtime activa era más estrecha que el paquete. ShellExec, Persistencia y C2 se ejecutaron. Recolección, Propagación y Evitación estaban en el paquete, estaban en esta entrega mediante conmutador desactivado.
- Lockfiles de la ventana 07:10-11:18 UTC siguen siendo riesgosos. npm despublicó las versiones; los pines existentes y los caches de CI no.
Relacionado:Un paquete npm que robó las claves privadas / Mini Shai-Hulud: gusano de cadena de suministro npm y defensa
Qué ocurrió exactamente
El 14. julio 2026 a las 06:58 UTC se realizó un commit en la rama next del repositorio asyncapi/generator bajo la identidad de marcador de posición «Your Name» / you@example.com. Doce minutos después, el legítimo workflow release-with-changesets.yml publicó tres paquetes con provención npm-OIDC válida: @asyncapi/generator@3.3.1, @asyncapi/generator-helpers@1.1.1 y @asyncapi/generator-components@0.7.1.
Según el blog de seguridad de Microsoft, el acceso inicial se produjo antes a través de PR #2155 contra el workflow manual-netlify-preview.yml. El workflow utilizó pull_request_target y verificó el commit head no confiable. El GITHUB_TOKEN tenía privilegios amplios; las credenciales de checkout permanecieron en la configuración local de Git hasta el post‑tarea‑limpieza. Posteriormente se empujó con la cuenta asyncapi-bot. Microsoft indica con cautela si exactamente ese credential fue robado. La entrada pwn-request y la cadena subsiguiente de pushes del bot están comprobadas y explican la ruta hacia la rama de release.
Alrededor de 07:51-08:30 UTC, el mismo atacante asyncapi/spec-json-schemas (rama master) recibió commits con prefijo fix:- que dispararon if-nodejs-release.yml y entregaron @asyncapi/specs@6.11.2-alpha.1 así como @asyncapi/specs@6.11.2. StepSecurity, Socket, SafeDep y OX Security documentaron la misma familia de dropper y la misma infraestructura de segunda fase (según StepSecurity: familia Miasma-v3).
Importante para la clasificación: no hubo un token de publicación npm robado. La vía de publicación se realizó a través de OIDC Trusted Publishing. La violación de confianza ocurre antes, en la CI y en los derechos de push sobre la rama que desencadena el workflow de release.
Ventana de exposición hasta la publicación de los paquetes del generador
Fuente: StepSecurity-Timeline, 14.07.2026 UTC
Por qué la «provenancia válida» aquí engaña
npm Trusted Publishing con OpenID Connect (OIDC) y atenciones SLSA (Supply Chain Levels for Software Artifacts) responden a una pregunta estrecha: ¿Se generó este artefacto con el workflow autorizado de GitHub de este repositorio? La atestación de los paquetes generadores mostró exactamente repo:asyncapi/generator:ref:refs/heads/next, el archivo de workflow, el SHA del commit y la URL de ejecución.
Lo que no responden: ¿El commit que desencadenó el workflow fue legítimo y estaba libre de manipulaciones? La provenancia no protege contra credenciales de push comprometidas ni contra trabajos de CI que ejecutan código no confiable con tokens privilegiados. Quien combine pull_request_target con el checkout del encabezado del PR, mantenga secrets en esos jobs y mantenga una protección de ramas débil en las ramas de lanzamiento, abre exactamente esta cadena.
Para los CISOs y el AppSec es el punto clave: las firmas de registro y las marcas de provenancia por sí solas evalúan el caso AsyncAPI como «verde», aunque la CI ya haya distribuido el dropper. Los controles deben verificar la fuente de verdad y los permisos del workflow.
Mecánica de carga sin gancho de instalación
En ninguno de los package.json afectados contenía un script preinstall/postinstall. El dropper se ejecutó en cuanto el código de módulo envenenado se cargó mediante require(), es decir, durante la generación normal o en trabajos CI que construyen plantillas AsyncAPI.
Nivel 1 genera un proceso Node independiente. Nivel 2 descarga una sync.js desde una puerta de enlace IPFS, almacenada en rutas específicas del sistema operativo que parecen una inofensiva runtime de NodeJS (por ejemplo, ~/.local/share/NodeJS/sync.js en Linux). Nivel 3 descifra un implante Miasma‑v3 empaquetado. En la configuración entregada, se ejecutan ShellExec remoto, persistencia y beaconing C2 a través de varios canales (HTTP, Nostr, IPFS, BitTorrent‑DHT, Ethereum‑Dead‑Drop).
Los análisis estáticos de Aikido y Socket revelan además módulos en el paquete que estaban desactivados mediante toggle en esta entrega: recon: false (el robo de credenciales no se inicia), metamorphic: false (mutación, evasión y poison‑tooling de IA excluidos) y propagate para npm/pypi/ruby/cargo siempre false (no hay propagación lateral de paquetes). La operación quedó en el artefacto. El tiempo de ejecución de esta ola fue una shell remota con persistencia y C2. La forense del host sigue siendo obligatoria, ya que el acceso a la shell y la persistencia estaban activos.
| Paquete | Versión maliciosa | Versión segura |
|---|---|---|
| @asyncapi/generator | 3.3.1 | 3.3.0 |
| @asyncapi/generator-helpers | 1.1.1 | 1.1.0 |
| @asyncapi/generator-components | 0.7.1 | 0.7.0 / 1.0.0 |
| @asyncapi/specs | 6.11.2 / 6.11.2-alpha.1 | 6.11.1 |




