Una sesión, cien repositorios
El informe AI Risk and Resilience 2026 de Mandiant describe a un atacante que tomó el control de una sesión activa de asistente de código en un proveedor de SaaS, logró que por ella se instalara un paquete de PyPI envenenado, robó tokens OAuth de GitHub y propagó el gusano Shai-Hulud por unos 100 repositorios internos. El informe no dice cómo se tomó la sesión. En un workspace de Bromure Agentic Coding no hace falta que lo diga: el token de esa sesión es un señuelo `ghp_`, el intento de sacarlo pausa la VM, y los push que convierten una máquina en cien repositorios se detienen en el proxy del anfitrión.
Alguien tomó el control de una sesión de código que estaba corriendo, autenticada y de confianza, en la propia máquina de un desarrollador. Todo lo que vino después, hasta cien repositorios, salió de lo que esa máquina tenía guardado en ese momento.
Un desarrollador de una empresa de software como servicio le preguntó a su
asistente de código qué paquete usar para un problema corriente. El asistente
nombró uno. El desarrollador dijo que sí, que es justo para lo que sirve tener
un asistente, y pip install hizo el resto.
Para entonces un atacante ya tenía la sesión, y fue él quien escribió esa recomendación.
Mandiant describe el caso en su AI Risk and Resilience Report 2026, publicado este mes por la rama de respuesta a incidentes de Google Cloud; The Hacker News lo contó el 16 de septiembre. El atacante secuestró una sesión activa de asistente de código en el puesto de trabajo de un desarrollador. Cinco pasos más lo llevaron hasta unos cien repositorios de código internos y al espacio de nombres de paquetes de la propia empresa, donde un segundo empleado se descargó la versión envenenada.
La cadena, salto a salto
Mandiant se guarda justo la parte que más querría saber. El caso de estudio público no dice cuándo ocurrió la intrusión, y no dice cómo consiguió el atacante el control de una sesión en curso. Cookies de navegador robadas, una extensión maliciosa, una CLI comprometida en una máquina compartida: el informe no elige ninguna. Quien lo lea buscando el parche se irá sin él.
Los pasos 2 a 6 no contienen ningún exploit. Una instalación de paquete
funciona como funcionan las instalaciones de paquetes, y el código que corre
como el desarrollador lee los tokens que están en el disco del desarrollador.
Un git push que lleva un token válido empuja, y una publicación que lleva
permisos de publicación válidos publica. Un paso fue una intrusión; los otros
cinco fueron gasto.
El multiplicador son las credenciales, y no para de crecer
Shai-Hulud es el gusano autorreplicante de la cadena de suministro que lleva
comiéndose cuentas de mantenedores desde 2025, y aparece como carga útil en una
historia como esta porque el paso 5 es exactamente para lo que sus autores lo
construyeron. GitGuardian desmontó una variante reciente en agosto y
la encontró barriendo 469 ubicaciones de credenciales,
frente a 189 en compilaciones anteriores: entornos de desarrollo,
herramientas de CI/CD, configuración de la nube, configuraciones de
herramientas de IA, archivos de configuración de gestores de paquetes,
historial del shell, archivos .env, ajustes del IDE, cachés de CLI. El gusano
lee un archivador en lugar de salir a cazar un fallo.
El análisis de GitGuardian resume la categoría en una frase:
“Los atacantes han dejado de intentar romper las relaciones de confianza y han empezado a usar las credenciales que ya hacen que esas relaciones funcionen.”
Un puesto de trabajo se convirtió en cien repositorios porque tenía guardada autorización para cien repositorios. Ese número salió de un inventario, y usted decide lo que entra en el inventario.
La sesión solo tenía señuelos
Bromure Agentic Coding ejecuta el agente dentro de una máquina virtual Linux en su Mac, y esa máquina no guarda ningún secreto real. No hay que activar nada para conseguirlo. Es la forma en que un workspace guarda una credencial, sin más.
Su token real de GitHub se queda cifrado en el anfitrión. La VM recibe un
falso que conserva la estructura: ghp_ seguido de 36 caracteres, 40 en
total, de modo que las comprobaciones de prefijo y longitud del propio gh
pasan sin queja. Bromure lo exporta como GH_TOKEN y lo escribe en
~/.git-credentials y en la configuración de gh, y es la única credencial de
GitHub que hay en esa máquina. Un proxy en el anfitrión sustituye el valor real
en el cable después de que la petición haya salido de la VM, y solo cuando el
destino coincide con el anfitrión para el que se acuñó la credencial. Cada
falso es determinista, derivado del valor real más una sal de 32 bytes propia
de la instalación mediante HKDF-SHA256, así que un cliente que tome la huella
de su propia clave no ve rotación aparente entre sesiones.
Ejecute el paso 4 contra eso. El ladrón de información llega, barre las 469
ubicaciones, encuentra la variable de entorno, encuentra ~/.git-credentials,
encuentra la configuración de gh y envía lo que encontró. Cada lectura tiene
éxito, y cada archivo está donde el gusano lo esperaba. El atacante acaba con
una cadena de cuarenta caracteres que no autentica en ninguna parte, salvo
volviendo a pasar por un Mac muy concreto.
Los pasos 5 y 6 necesitan los dos que el paso 4 haya producido un token que funcione, y el paso 4 produjo un señuelo.
El robo es también la alarma
Un token falso tiene un único destino legítimo. Ninguna petición inocente lleva
un señuelo ghp_ acuñado para github.com a otro anfitrión, así que el proxy
examina cada petición saliente, cabeceras y cuerpo, en busca de cualquier falso
que salga de su propio ámbito. Ejecuta un autómata de Aho-Corasick, lo bastante
barato como para apuntarlo a todo el tráfico.
Cuando el proxy encuentra uno, hace algo más que registrarlo:
- rechaza la petición con HTTP 451, y ni un byte de ella llega al destino;
- Bromure pausa la VM en el acto;
- una alerta le ofrece Apagar, Guardar para investigación (exportar antes el disco, la carpeta personal y las carpetas compartidas para el análisis forense) o Continuar por su cuenta y riesgo;
- la Security Timeline gana una fila roja de Credential brokering;
- Bromure marca el workspace como comprometido, de modo que el siguiente arranque borra la imagen de disco y la carpeta personal persistente. Sus tokens, claves SSH y ajustes del workspace sobreviven a ese borrado.
Usted no activa nada de esto; el detector está siempre encendido. El intento de exfiltración del paso 4 es lo que detiene la sesión, así que el incidente termina con una VM pausada y una entrada en la línea de tiempo, en lugar de cien repositorios y un caso de estudio.
Cien repositorios son cien push
Supongamos que quiere el cinturón además de los tirantes. El paso 5 es un gusano escribiendo en repositorios, y el anfitrión clasifica las escrituras.
Las protecciones de un workspace llevan una política de
escritura por servicio. Para GitHub, esa política lee git sobre HTTPS igual que
la API REST: un git push llega como git-receive-pack y cuenta como
escritura, mientras que un git fetch llega como git-upload-pack y cuenta
como lectura. Solo lectura rechaza los push. Preguntar antes de
escribir, el valor por defecto de los workspaces nuevos, retiene cada uno
para un diálogo del lado del anfitrión que nombra la operación. Los fetch pasan
con ambas, así que el agente sigue trabajando.
El proxy toma esa decisión en el anfitrión, fuera de la VM. Un agente cuya sesión pertenece a otra persona no puede desactivar la política ni rodearla, porque la política no se ejecuta en el lado del agente de la línea. Un gusano que quiere cien repositorios recoge cien rechazos, o le pregunta cien veces.
Y el paquete todavía tenía que llegar
El paso 3 es donde Mandiant da consejos concretos: validar las dependencias de terceros recomendadas por la IA contra sumas de verificación criptográficas y listas de permitidos, y encaminar el tráfico de dependencias por repositorios que usted controle.
En un workspace, cada descarga de paquete pasa ya por el anfitrión, puesto que
el proxy es la única ruta de la VM hacia la red. Bromure intercepta PyPI en
pypi.org y files.pythonhosted.org y aplica allí la política del anfitrión.
El pip.conf de dentro de la VM puede apretar lo que el proxy sirvió; no puede
aflojarlo. Tres capas hablan por los paquetes de Python:
- la barrera de edad, activada por defecto con un mínimo de dos días, que rechaza las versiones más jóvenes que el corte, partiendo de que una publicación recién salida es la que tiene más probabilidades de haber sido secuestrada hace nada. Los índices PEP 503 no llevan marcas de tiempo, así que Bromure consulta la hora de publicación bajo demanda;
- la comprobación OSV contra
api.osv.dev, gratuita y sin clave, que bloquea cualquier versión que lleve un aviso de seguridad con la gravedad que usted elija o superior; - el filtro de paquetes comprometidos de socket.dev, que salta con malware, malware conocido, typosquatting y scripts de instalación maliciosos.
Un bloqueo vuelve como un HTTP 451 cuyo cuerpo empieza por
Bromure Supply-Chain Security blocked this request: seguido del motivo, y pip
lo imprime tal cual. Usted ve por qué falló la instalación, y el agente
también, que muchas veces puede fijar por su cuenta una versión más antigua.
Esa versión más antigua es justo hacia la que la barrera de edad lo estaba
dirigiendo.
Los tres controles de Mandiant, y dónde viven
El informe cierra el caso de estudio con tres recomendaciones. Cada una nombra un sitio donde un workspace de Bromure ya pone la frontera.
Validar las dependencias recomendadas por la IA
Mandiant pide sumas de verificación y listas de permitidos para todo lo que sugiera el asistente. Su workspace aplica su política a la respuesta del registro antes de que la VM la vea. El proxy quita del listado de versiones las demasiado recientes, de modo que desde el punto de vista del agente todavía no existen, y rechaza una versión demasiado reciente fijada a mano citando en el error la edad real del paquete. Las claves de los servicios de reputación se quedan en el anfitrión, fuera de la máquina que ejecuta la instalación.
Mantener lejos las claves en bruto y los tokens de larga vida
Esa es la frontera del cable escrita como consejo. Ningún archivo, variable de entorno o proceso dentro de la VM guarda una clave de API, un token OAuth, un secreto de AWS o una clave privada SSH reales. Bromure mantiene los tokens OAuth de la suscripción cifrados en el anfitrión y los renueva allí unos cinco minutos antes de que caduquen, de modo que el invitado no lleva ningún token de refresco. El canal entre ambos corre en un solo sentido: el anfitrión escribe falsos hacia dentro, y la VM no tiene ninguna llamada con la que pedir de vuelta un token real.
Encaminar el tráfico de dependencias por algo que usted controle
El proxy es la única ruta de la VM hacia fuera. Cada petición HTTPS viaja por un socket virtual hasta el anfitrión, que termina el TLS, inspecciona la petición y la reemite por la propia pila TLS de macOS. Ese único camino es lo que hace que los otros dos controles sean exigibles en vez de meros consejos. Ninguna descarga de paquete puede saltarse las comprobaciones, y ninguna credencial puede salir sin sustituir.
Y reducir el radio de explosión a propósito
Cien repositorios cayeron porque una máquina llevaba autorización para cien repositorios. Corte los workspaces uno por proyecto, o uno por frontera de credenciales. Cada uno corresponde a una sola VM, no hay dos que compartan una, y cada uno lleva las credenciales que el trabajo de ese workspace necesita. El inventario de una máquina cualquiera se fija en un panel de ajustes.
La parte que no se puede parchear
Mandiant enmarca la categoría en una frase que describe la forma más que el incidente: “una fuente de datos envenenada, una dependencia de modelo o un gancho de extensión pueden convertir a un agente de confianza en un canal para el reconocimiento interno, el movimiento lateral o la fuga autónoma de un entorno aislado.”
Tres puntos de entrada distintos, una sola frase, y vale para los tres. El punto de entrada determina cómo llega un atacante; el contenido de la máquina de un desarrollador determina hasta dónde llega. Este atacante entró por una sesión secuestrada. El siguiente entrará por otra cosa, y el informe no nombra ninguna puerta que usted pueda cerrar.
Construya para el paso posterior a la entrada. Dé por hecho que la sesión ya pertenece a otra persona, que es justo lo que el caso de estudio le obliga a suponer al no explicar nunca el paso 1, y pregúntese luego hasta dónde llega esa sesión.
Una versión de este incidente termina en el paso 3, con un 451 en el terminal y una línea en el Security Log. Otra termina en el paso 4, con una VM pausada y una fila roja en la línea de tiempo que deja constancia de que una credencial intentó salir. Ninguna de las dos llega al paso 5, y en el paso 5 estaban los cien repositorios.
Ponga el agente en una máquina que solo guarde señuelos. Instale Bromure Agentic Coding, y deje que alguien tome la sesión.