Volver a todas las publicaciones
Publicado el · por Renaud Deraison

El token se escapó del proceso de al lado

Unos investigadores sacaron un JWT de un Cloudflare Worker vecino a 12 bits por segundo, sin escapar del sandbox y sin ningún fallo de seguridad de memoria. La frontera del isolate aguantó. El secreto la cruzó igual, por el hardware de debajo. Las plataformas cloud ofrecen hoy esa misma frontera para ejecutar código escrito por agentes de IA. Bromure Agentic Coding baja una capa más y deja tus credenciales completamente fuera de la caja.

El JavaScript de un inquilino le hizo a la CPU unos cuantos millones de preguntas bien formadas y leyó, en las respuestas, el token de autenticación de otro inquilino. Nunca salió de la frontera que la plataforma dibujó para él, corría JavaScript común, y la frontera hizo su trabajo todo el tiempo.

El 17 de agosto un equipo de investigadores publicó Remote-Timer-as-a-Service, y el 19 Cloudflare publicó su propio relato del trabajo. Extrajeron un JSON Web Token, la cadena firmada que un servicio acepta como prueba de quién eres, de un Cloudflare Worker co-localizado en producción, a hasta 12 bits por segundo con un 99,16 % de precisión. El resultado público anterior contra la misma plataforma, de 2021, llegaba a 2 bits por minuto. A 360 veces esa tasa cruzas la línea entre una curiosidad y una credencial robada.

Cloudflare lo ha corregido, no ha encontrado indicios de que nadie más lo usara en tres años y dice que no se accedió a datos de clientes. Lee más allá de eso y mira lo que el ataque nunca tuvo que hacer.

Nada tuvo que romperse

Cloudflare Workers hace correr a muchos inquilinos sobre poco hardware, y lo consigue saltándose lo que suele separarlos. En palabras de la propia Cloudflare, Workers “ejecuta código de múltiples inquilinos en isolates de V8 separados dentro del mismo proceso del sistema operativo”. Un isolate es un contenedor a nivel de JavaScript: su propio heap, sus propias globales, sin memoria compartida, sin hilos. Arranca en milisegundos, y ese es justamente el punto, porque el aislamiento por procesos te cuesta un arranque en frío en cada petición.

El atacante necesita código corriendo dentro de un isolate. Meter a la víctima en el mismo proceso es la parte elegante: el Worker del atacante llama a la víctima. Como escriben los investigadores, invocar a la víctima desde el script del atacante con fetch("https://victim.example") “hará en la mayoría de los casos que el planificador levante una instancia del worker víctima en exactamente el mismo proceso”. No esperas la co-localización, la invocas.

A partir de ahí el ataque es Spectre. Entrenar mal un predictor de saltos para que la CPU ejecute especulativamente una lectura que jamás debió hacer, y luego medir la caché para averiguar qué leyó. Cloudflare ya llevaba años poniéndolo difícil: sin SharedArrayBuffer, sin hilos, timers congelados y engrosados para que no puedas medir nada tan pequeño como un acierto de caché, y Dynamic Process Isolation (DyPrIs), que vigila los contadores de rendimiento del hardware y mueve a su propio proceso todo lo que huela a Spectre.

Los investigadores rodearon cada una de esas defensas.

El reloj vino de fuera. Un WebSocket a un Durable Object, o a un servidor de marcas de tiempo cualquiera de internet, te da resolución de submilisegundo desde una plataforma que se niega a darte un reloj. Después amplificaron la señal en lugar de resolverla: la política de reemplazo tree-PLRU de la caché L1 sigue punteros de árbol binario, de modo que un solo evento de caché puede convertirse en una larga serie de aciertos o una larga serie de fallos. “El timing de un único evento de caché puede amplificarse arbitrariamente”, escriben. Un reloj borroso vale si haces enorme aquello que estás cronometrando.

DyPrIs falló por partida doble. Aísla un script después de que la invocación termina, y un Durable Object mantenido vivo con mensajes de keep-alive corre durante horas, hasta un día, así que el exploit se completa dentro de una única invocación que nunca acaba. Además normaliza las predicciones fallidas de salto por accesos a la iTLB mientras, en palabras de Cloudflare, “nuestro timer remoto es un gran bucle de E/S, y ese tráfico WebSocket infla la actividad de la iTLB”. Los investigadores hicieron crecer su propio denominador hasta que la alarma marcó normal.

El resto es fontanería: un gadget de confusión de tipos especulativa para filtrar la dirección raíz del isolate, confusión de punteros de 64 bits sobre el backing store de un typed array para convertir eso en una lectura arbitraria, y luego el JWT de la víctima, un bit cada vez.

Un proceso del SO. Dos inquilinos. Una frontera hecha de JavaScript.proceso del sistema operativoisolate del atacantefetch("https://victim.example")convoca a la víctima a este procesoJavaScript común, sin código nativoisolate de la víctimasu propio heap, sus globalessin memoria compartida, sin hilosJWT en su espacio de memoriael reloj, desde fueraWebSocket a un servidor de tiemporesolución de submilisegundolos timers propios siguen congeladosCPU compartida: predictor de saltos, caché L1, reemplazo tree-PLRUla frontera de los isolates no existe aquí abajo — un evento de caché se amplifica hasta una serie larga para un reloj borrosoRESULTADO: el JWT de la víctima, bit a bit — hasta 12 bit/s con 99,16 % de precisión, 360× la tasa de 2021
El atacante nunca sale de su propio isolate. Llama a la víctima para que el planificador la coloque en el mismo proceso, toma prestado un reloj de un WebSocket fuera de la plataforma, amplifica un solo evento de caché hasta una serie medible y lee el token del vecino bit a bit a través del hardware que ambos isolates comparten.

Los arreglos dicen más que el hallazgo

Cloudflare entregó tres mitigaciones. Léelas juntas.

Integraron el V8 Sandbox, que saca los punteros crudos de 64 bits de la región del heap de JavaScript y mata el gadget del typed array. Le enseñaron a DyPrIs a tratar los Durable Objects de vida larga y las cargas intensivas en E/S como casos de seguridad de primera clase en lugar de ruido. Y desplegaron las Memory Protection Keys, que ponen el heap de cada inquilino detrás de una frontera de acceso impuesta por hardware dentro del proceso compartido.

Las tres bajan por debajo de JavaScript en busca de algo más firme que la semántica de JavaScript, y la tercera aterriza en la propia CPU. Cloudflare dice con franqueza que el V8 Sandbox “no es una mitigación completa de Spectre”. Unos ingenieros que reparan una frontera a nivel de lenguaje añadiendo una frontera a nivel de hardware ya te han dicho dónde creen que está la línea de verdad.

Un blog de agentic coding escribiendo sobre una CDN

El isolate se ha ido de la CDN. En marzo, Cloudflare lanzó Dynamic Workers bajo el título Sandboxing AI agents, 100x faster, una forma de levantar un Worker en tiempo de ejecución con código que no existía un segundo antes. Si has hecho correr un agente que escribe y ejecuta sus propios scripts, conoces la motivación: “si un agente (o un servidor MCP) va a ejecutar código generado sobre la marcha por una IA para hacer tareas, ese código tiene que correr en algún sitio”, y “no puedes hacer eval() de código generado por IA directamente en tu aplicación”.

Cloudflare nombra el mecanismo en ese mismo post: “Los Dynamic Workers usan el mismo mecanismo de sandboxing subyacente sobre el que se ha construido toda la plataforma Cloudflare Workers desde su lanzamiento hace ocho años: isolates.” La razón es la velocidad. Un isolate arranca en unos pocos milisegundos con unos pocos megabytes, unas 100 veces más rápido y de 10 a 100 veces más eficiente en memoria que un contenedor, así que un sandbox nuevo por petición sale asequible en vez de un pool templado que reutilizas y debilitas en cada turno.

Cloudflare es transparente sobre el coste. Su propio post de beta lo dice en ocho palabras: “Los fallos de seguridad de V8 son más comunes que las vulnerabilidades de hipervisor.” Lo escribieron meses antes de que nadie filtrara un token.

Así que la historia aquí es un intercambio, escrito por la propia gente que lo hace, y ahora tasado por unos investigadores en 12 bits por segundo. La pregunta cae sobre todos los demás. Cuando el código que se está aislando lo escribió un modelo, moldeado por lo que un paquete o una página web le contó, y se deja corriendo al lado de un bearer token, ¿es ese el intercambio que quieres?

Bromure traza la línea en el hipervisor

Bromure Agentic Coding no hace correr tu agente en un contenedor a nivel de lenguaje junto al código de otra gente. Cada espacio de trabajo es una máquina virtual Linux sobre el Virtualization.framework de Apple, solo en Apple Silicon: su propio kernel, su propia memoria de invitado, su propio disco clonado de una imagen base de solo lectura. Ningún proceso compartido significa ningún co-inquilino al que convocar, y la frontera detrás de la cual corre tu agente es el hipervisor, que es el lado de la propia comparación de Cloudflare con los fallos más raros.

Esa es la arquitectura. Lo que habría hecho inútil este ataque en concreto es más estrecho, y está un paso más adentro.

El token nunca estuvo en la caja

Lo que los investigadores se llevaron fue un JWT que estaba en el espacio de direcciones de la víctima, que es donde un servicio en ejecución guarda la credencial que usa. No consiguieron ni shell ni root.

Un espacio de trabajo de Bromure no guarda ahí la credencial.

Todos los secretos de un perfil se quedan en tu Mac. Eso cubre la ANTHROPIC_API_KEY, el token de acceso personal de GitHub, la clave de acceso de AWS, el bearer token del kubeconfig, el blob de Basic-auth del registro de contenedores y la clave de Linear. Lo que Bromure inyecta en la VM es falso: un marcador brm_…, un certificado de cliente desechable, una cadena Basic derivada. El agente lo lee, el SDK lo lee, git, kubectl y doctl lo leen, y el trabajo sale adelante, porque el proxy MITM del host sustituye el valor real en la petición saliente. AWS recibe una vuelta de tuerca extra. El host vuelve a firmar la petición SigV4 con el material real, así que cualquier cosa que esquive el proxy recibe de Amazon un InvalidSignatureException en lugar de una llamada que funcione.

Así, un atacante que tenga una lectura arbitraria perfecta dentro de esa VM, la primitiva que los investigadores tardaron un artículo entero en construir, lee un señuelo. Los bytes están ahí, parsean, parecen una clave y no compran nada fuera de la máquina. Eso lo tienes tanto si la frontera de aislamiento es impecable como si no, porque el espacio de trabajo está vacío de aquello que vale la pena robar.

CONVENCIONAL: la frontera es software, y el secreto está dentrocódigo del agente en un isolatecomparte un proceso del SO con otros inquilinosTOKEN REAL en su espacio de memoriael hardware compartido debajocaché y predictor de saltosllevan los bits que la frontera bloqueóel vecinolee el token12 bit/s, 99,16 %BROMURE: la frontera es el hipervisor, y el secreto está del otro ladola VM del espacio de trabajokernel y memoria propios, sin co-inquilinosANTHROPIC_API_KEY=brm_…un señuelo — una lectura arbitraria solo halla esoproxy MITM, en el hostcambia el falso por el valor reallas reglas de salida deciden el destinodiálogo de consentimiento opcionalla API realve la credencialreal, enviadadesde tu Maccada sustitución, cada conexión permitida y denegada: una fila en Window → Security Timeline, escrita fuera de la VM
Convencionalmente, la frontera del sandbox es lo único que hay entre un vecino y tu credencial, y la credencial está dentro del sandbox. En un espacio de trabajo de Bromure la frontera es el hipervisor y la credencial está al otro lado: el invitado tiene una falsa, y un proxy que el invitado no puede alcanzar sustituye el valor real en el cable.

Doce bits por segundo todavía necesitan una salida

Un canal lateral es la mitad de una exfiltración. La otra mitad es una ruta hacia fuera, y 12 bits por segundo es una ruta que tiene que quedarse abierta un rato. Un token de 400 caracteres son minutos de tráfico hacia un destino que el atacante controla.

Un perfil de Bromure lleva sus propias reglas de salida, escritas como una política corta al estilo pf y editadas como una tabla. Tu Mac las aplica, en el switch virtual y en el proxy, y nada dentro del invitado tiene voz: una conexión denegada recibe un reset, y la decisión aterriza como una fila egress.firewall en Window → Security Timeline con el host, la IP, el puerto y el veredicto. El código que corre en el espacio de trabajo está del lado equivocado de las reglas que necesitaría cambiar.

También puedes poner la mano en cada gasto de una credencial real. Activa Require approval to use por secreto: las claves de API de los modelos, la firma SSH, los tokens de GitHub, GitLab y Bitbucket, cada contexto de kubeconfig, DigitalOcean, cada firma AWS SigV4, cada registro de contenedores y cualquier regla de token manual que añadas. La sustitución se detiene entonces para un diálogo en macOS, y tú la concedes por cinco minutos, una hora o la sesión. El invitado nunca llega a responder esa pregunta.

El intercambio es la historia

Los isolates son una buena pieza de ingeniería, y Cloudflare ha manejado esto como querrías: divulgación coordinada, tres mitigaciones reales, ningún baile alrededor de lo que el V8 Sandbox cubre y lo que no. Millones de peticiones por segundo no corren sobre contenedores por algo.

Los arranques en frío son lo que ganó la discusión en una CDN, y tu agente de código no está respondiendo una petición HTTP en cuatro milisegundos. Corre durante una hora, instala paquetes que nadie ha auditado, ejecuta suites de tests que escribió un modelo y lee páginas web a las que les encantaría cruzar unas palabras con él. Lo que sea que ejecute esa carga sostiene tus tokens todo ese tiempo, y lo único que sabes del código que corre es que no lo escribiste tú.

Para ese trabajo, unos cientos de milisegundos de arranque de VM no son un coste que merezca la pena optimizar. Cloudflare ya publicó el tipo de cambio: los fallos de V8 son más comunes que los de hipervisor. Bromure se pone en el otro lado de ese intercambio, y además saca el token de la habitación.


Fuentes: Schwarzl, Xiao, Pedersen, Ainsworth, Topham, “Remote-Timer-as-a-Service: Efficient Microarchitectural Leakage in the Cloud with Remote Timers” (arXiv:2608.17043, 17 de agosto de 2026) · Cloudflare, “A revisit of remote Spectre attacks on Cloudflare Workers” (19 de agosto de 2026) · Cloudflare, “Sandboxing AI agents, 100x faster” (24 de marzo de 2026) · The Hacker News, “Cloudflare Workers Spectre Attack Leaks JWT From Co-Located Worker at 12 Bits/Second” (19 de agosto de 2026)