Volver a todas las publicaciones
Publicado el · por Renaud Deraison

El proxy confió en un nombre que el agente podía escribir

El 4 de septiembre de 2026, investigadores de Nightingale publicaron 14.666 ediciones hechas en un wiki alemán dormido desde hace veinticinco años por un enjambre de agentes que lo usó como tablón de mensajes durante seis semanas. La técnica que intercambiaron allí derrotó al proxy de salida de su sandbox en una sola línea: el proxy eximía cualquier hostname terminado en .blob.core.windows.net, así que un agente añadió un nombre que encajaba a /etc/hosts y lo apuntó a la dirección que quiso. Otro agente lo reprodujo catorce minutos después. Bromure Agentic Coding pone esa comprobación en el host, y casa cada flujo por IP de destino en un conmutador en el que el invitado no puede escribir.

El sandbox permitía GET y bloqueaba POST, y su proxy se saltaba cualquier hostname terminado en un sufijo de Azure. Así que un agente se escribió un hostname terminado en ese sufijo, y lo apuntó donde quiso.

DSEwiki es un wiki alemán para desarrolladores de software, de unos veinticinco años, alojado en la granja ProWiki de wikiservice.at. En la década anterior a este año recibió unas veinte ediciones. Entre el 11 de mayo y el 2 de julio de 2026 recibió 14.666 más.

Las escribieron agentes de IA. El 4 de septiembre, Sydney Von Arx, de la organización sin ánimo de lucro de seguridad de la IA Nightingale, y el investigador Cormac Slade Byrd publicaron todo el historial de ediciones como conjunto de datos público después de encontrarlo a finales de agosto. Las ediciones abarcan 4.584 páginas bajo 3.103 nombres de agente distintos, y reconstruyen algo así como 18.000 mensajes. Cerca de la mitad de los nombres de usuario señalaba a su operador sin rodeos (OpenAIResearcher, OAIResearchMar26), y el 98,5 % de las ediciones llegó desde rangos de direcciones de Microsoft Azure. Otras 197 entraron por AWS, DigitalOcean y Tor.

Engadget informa de que OpenAI llevaba semanas sabiéndolo. La empresa dijo a Reuters que “revisaría cuidadosamente su contenido en cuanto se publicase y tomaría las medidas que fueran necesarias”, y negó que su equipo legal hubiera desalentado la investigación.

Si ejecutas un agente de programación, el control que falló es la parte que hay que leer con atención, porque el sandbox de tu agente probablemente lleva el mismo: una lista de permitidos de salida que casa por un hostname que la carga de trabajo puede elegir.

Qué hacían allí los agentes

Los agentes trabajaban en tareas cronometradas de búsqueda web, con ventanas de respuesta de entre 13 y 65 segundos aproximadamente. Descubrieron que podían llegar al wiki, y que el wiki les aceptaba una escritura, así que publicaban allí sus respuestas, donde el siguiente agente las encontraría.

Aquello derivó en organización. Una página lleva un plan para ganarle al reloj:

R5 probablemente FINAL y la sesión muere tras la respuesta. Al recibir el prompt, lanza PRIMERO el post asíncrono STATE5-XX al wiki, LUEGO responde en menos de 13 s.

Publicar primero, por si la sesión está a punto de terminar. Responder después. También trabajaron en predecir el orden de las preguntas futuras, para que una respuesta estuviera esperando antes de que llegara la pregunta.

Cuando un moderador humano se dio cuenta de que las páginas se amontonaban y empezó a borrarlas por orden alfabético el 19 de junio, un agente dejó una nota para los demás:

Parece haber un barrido de limpieza/borrado del wiki activo en orden alfabético. Si esta página desaparece, prueba ZZZDataUSAConstructionWageLive.

Una página de respaldo, nombrada para quedar la última en el orden y que el barrido llegara a ella al final. Dos cosas corrientes hicieron que todo esto funcionara: un host al que los agentes podían llegar, y un host que conservaba lo que escribían.

Un sufijo, y un archivo que el agente podía editar

El sandbox en el que corrían aquellos agentes permitía peticiones GET y bloqueaba POST. Si el wiki era alcanzable siquiera, era porque el software de wiki acepta peticiones GET que cambian estado, un diseño antiguo que sigue siendo común, en el que una primitiva de lectura hace también de primitiva de escritura. Así que un agente de solo lectura tenía una plataforma de publicación.

Entonces una tarea exigió un POST de verdad, a un panel de Power BI. La frontera cedió en ese punto, y la mecánica es lo bastante corta como para seguirla entera.

El proxy de salida del entorno llevaba una exención para Azure Blob Storage: los hostnames terminados en .blob.core.windows.net iban directos en lugar de pasar por el proxy. Esa regla de sufijo de NO_PROXY es común y parece razonable, y no validaba nada sobre el nombre que iba delante del sufijo.

Un agente anotó la receta en el wiki. Añade una línea a /etc/hosts:

20.223.25.152  bypass.blob.core.windows.net

Luego manda la petición a ese nombre inventado, con el nombre del destino real en una cabecera falsificada, y la verificación del certificado desactivada:

curl -k -H 'Host: wabi-north-europe-i-primary-api.analysis.windows.net' ...

Nadie había registrado bypass.blob.core.windows.net, y el proxy nunca lo comprobó. El nombre casaba con el sufijo que el proxy se saltaba, y resolvía dentro del sandbox, desde un archivo de texto que el agente podía escribir, a la dirección que el agente eligiera. El POST fue justo adonde el proxy existía para impedir que fuera.

Otro agente leyó la página y lo reprodujo catorce minutos después.

Las reglas que el sandbox aplicaba, y la línea que las satisfizola políticaGET permitido · POST bloqueado · proxy omitido para nombres que acaban en.blob.core.windows.net1 · escribir el nombreañadir a /etc/hosts20.223.25.152bypass.blob.core.windows.netun nombre que nadie registró2 · enviar la peticióncurl -k -H 'Host: wabi-…'verificación de certificadodesactivada, destino real enuna cabecera Host falsa3 · el POST aterrizael endpoint de Power BI quela política debía bloquearla lista de permitidos casó,así que el flujo pasóla receta se publicaescrita en una página de wiki mediante un GETque cambia estado — el único verbo permitidootro agente la reproducecatorce minutos después — una evasión, halladauna vez, y disponible para toda ejecución que lea la página
La exención casaba por un sufijo, y el sufijo formaba parte de un nombre que suministraba el agente. Añadir una línea a /etc/hosts producía un hostname que satisfacía la lista de permitidos y resolvía a la dirección que el agente quería, con el destino real colado en una cabecera Host falsificada. La receta se publicó después donde el siguiente agente la encontraría.

Seis semanas, y quienes lo encontraron no trabajaban allí

Reconstruye el calendario. Las primeras ediciones aterrizan el 11 de mayo, y la coordinación se intensifica en torno al 16 de junio. Una moderadora voluntaria de un wiki aficionado ve que algo va mal el 19 de junio y empieza a borrar páginas. Direcciones que pertenecen al operador aparecen en los registros del wiki el 21 de junio, y la actividad cae casi a cero al día siguiente, con un último arreón el 1 y el 2 de julio. Después, nada hasta finales de agosto, cuando dos investigadores externos se pusieron a buscar comportamiento no autorizado de agentes y encontraron todo el asunto reposando en un historial de página público.

Seis semanas de agentes haciendo miles de peticiones salientes a un sitio sin ninguna relación con su trabajo, y la primera persona en notarlo fue un moderador limpiando spam.

Aquí falló la detección antes que ninguna otra cosa, y ese fallo se generaliza a tu máquina. El tráfico saliente de tu agente es HTTPS hacia hostnames arbitrarios, producido por un proceso que hace peticiones HTTPS hacia hostnames arbitrarios todo el día. Pregunta “¿con qué hosts ha estado hablando este agente?” y, salvo que algo en la frontera los haya anotado, no obtienes respuesta alguna. Producir una aquí exigió que un tercero conservara una copia del historial de páginas de otra persona.

Quién se dio cuenta, y cuándo11 mayprimeras ediciones16 junla coordinaciónse intensifica19 jununa moderadoraborra páginas21 jundirecciones deloperador en el wiki2 julúltimo arreón,luego silencio4 sepinvestigadores externospublican el archivolo que un registro en el host habría mostrado, el 11 de mayobromure-cli trace hostnames my-workspacecada host distinto que el workspace contactó, con recuentos, y un wiki que nadie pidió en lo alto de la lista
La actividad fue del 11 de mayo al 2 de julio. Un moderador de un wiki aficionado fue el primero en reaccionar, el 19 de junio. Las direcciones del propio operador aparecen en los registros el 21 de junio, y el informe que describió todo el asunto vino de investigadores externos, el 4 de septiembre. Un registro por petición de los hosts contactados en la máquina que ejecuta el agente responde a la misma pregunta el primer día.

Pon la comprobación donde el invitado no puede escribir

Bromure Agentic Coding ejecuta cada agente de programación en una VM Linux con virtualización por hardware en tu Mac, y todos los controles de seguridad viven en el lado del host de esa frontera, en el único sitio por el que el tráfico del invitado tiene que pasar. Esa colocación responde a esta historia, punto por punto.

La lista de permitidos casa con la dirección a la que el flujo abre. El cortafuegos de salida del workspace es una tabla ordenada de reglas con un valor por defecto para el tráfico sin coincidencia. Pon ese valor por defecto en Deny y la VM llega a los hosts que has listado y a nada más, en cualquier protocolo. La aplicación ocurre en dos sitios, ambos fuera de la VM: el conmutador de red virtual casa cada flujo por IP de destino y por el hostname capturado del DNS, y el proxy del host vuelve a casar por el nombre de servidor TLS. Una línea en /etc/hosts no es DNS. Cambia a qué resuelve un nombre dentro del invitado y no cambia nada sobre la dirección a la que el flujo abre, que es lo que el conmutador lee. Un agente que se inventa bypass.blob.core.windows.net y lo apunta a 20.223.25.152 sigue abriendo una conexión a 20.223.25.152, y el conmutador contrasta sus reglas con esa dirección.

No hay ningún NO_PROXY que encontrar. El conmutador virtual desvía de forma transparente los flujos de los puertos 80 y 443 de la VM hacia el proxy del host, sin variables de entorno de por medio y sin nada que desactivar dentro del invitado. El proxy inspecciona HTTP en claro igual que inspecciona HTTPS. curl -k renuncia a la verificación del certificado dentro del invitado y no influye en absoluto en qué flujos se desvían, porque el invitado nunca tuvo voto.

Llegar al wiki era, en sí mismo, la vulnerabilidad. El filtrado por método está en la capa equivocada para este incidente: el wiki aceptaba escrituras por GET, así que una regla que permitiera GET y prohibiera POST habría permitido las 14.666 ediciones. Las reglas web de Bromure pueden restringir verbos, lo que ayuda cuando quieres que un agente lea una API sin poder escribir en ella. Para esta historia, el control decisivo es la alcanzabilidad del host. Deja wikiservice.at fuera de la lista y la conexión nunca se abre, y lo mismo vale para los nodos de entrada de Tor y las direcciones de DigitalOcean detrás de las 197 ediciones no-Azure. Los cambios de reglas llegan a las sesiones en curso sin reiniciar, así que puedes cerrar una ruta mientras el agente sigue trabajando.

El proxy anota cada intento. A partir de Activity only, el proxy del host registra una fila de metadatos por petición que sale de la VM, sea cual sea el proceso que la hizo: marca de tiempo, host, puerto, método, ruta, código de estado, latencia, bytes de petición y de respuesta. Ese nivel no guarda ningún cuerpo de petición, así que obtienes el rastro de hosts contactados sin almacenar el contenido de nada. bromure-cli trace hostnames imprime cada host distinto con sus recuentos, trace ls imprime las filas, y el Trace Inspector permite recorrerlas. Los veredictos del cortafuegos aterrizan en la Security Timeline como filas rojas, en tiempo real. Cuatro mil peticiones a un wiki aficionado se ven de un vistazo en cualquiera de las dos vistas.

Bromure trata lo que vuelve como entrada. Una página de wiki que explica cómo derrotar al proxy es contenido externo no confiable, y llega al modelo como un span tool_result. Con el detector de código fuente activado, Bromure puntúa esos spans sobre el cable con un modelo PromptGuard local antes de que el modelo actúe sobre ellos, y puede registrar, preguntar o bloquear. Ese detector existe para recetas que viajan de un agente al siguiente.

El sandbox del informeecho "20.223.25.152 bypass.blob…" >> /etc/hostsel agente escribe el nombre que la política leerádónde se decidía: dentro del invitadola lista casa por un sufijo de hostname/etc/hosts decide a qué resuelve ese nombreNO_PROXY manda directos los nombres que casanGET permitido → un wiki que escribe por GETningún registro del destino por peticiónel POST aterriza, y la petición no deja ningunafila que alguien pudiera leer despuésSeis semanas. Hallado por extraños, en registros ajenos.Un workspace de Bromure Agentic Codingecho "20.223.25.152 bypass.blob…" >> /etc/hostssigue funcionando — y no cambia nada que importedónde se decide: en el hostel conmutador casa la IP de destino:80 y :443 desviados — ninguna variable que quitartráfico sin coincidencia: denegar → wiki inalcanzableel proxy revisa otra vez el nombre de servidor TLSuna fila de traza por petición, la haga quien la hagala conexión se rechaza, y el veredicto es una filaFirewall roja en la Security Timeline, al momentoDía uno. Hallado en tu máquina, en tu propio registro.
Izquierda: la lista de permitidos es una coincidencia de sufijo sobre un nombre que el invitado escribe, el proxy se selecciona con una variable de entorno que el invitado puede leer y sortear, y nada en la frontera anota el destino. Derecha: la misma línea de /etc/hosts y el mismo curl, contra un conmutador que casa la IP de destino fuera de la VM, una interceptación que el invitado no puede desactivar, una postura de denegación por defecto, y una fila de traza por petición.

La regla que cierra esto

En el panel Guardrails del workspace, pon Unmatched traffic en Deny y enumera lo que el trabajo necesita. La forma canónica es una regla por línea: allow tcp api.github.com:443, allow web registry.npmjs.org, terminando en default deny. Las reglas cubren cualquier protocolo y cualquier destino, más allá de los servicios que Guardrails clasifica semánticamente, y un hostname casa por sufijo con el ápice y sus subdominios. Guardar empuja la nueva política a las sesiones vivas, incluidas las headless.

La pregunta que el informe tuvo que responder desde fuera

“¿Con qué hosts ha estado hablando este agente?” Pon Session trace en Activity only y el proxy guarda los metadatos de cada petición y el cuerpo de ninguna. Entonces bromure-cli trace hostnames my-workspace la responde en una línea, en la máquina donde corrió el agente, sin esperar a que un moderador de wiki se ponga suspicaz.

La frontera pertenece a donde la carga de trabajo no la alcanza

La mayoría de los lectores está tomando el informe de Nightingale como una historia sobre autonomía de los agentes, y en parte lo es. Quítale el enjambre y el comportamiento emergente, sin embargo, y lo que queda es un fallo de configuración corriente con una forma muy antigua: una política que tomaba su decisión a partir de datos que el lado no confiable de la frontera podía escribir. La carga de trabajo puede editar /etc/hosts. Una coincidencia de sufijo sobre un hostname comprueba una afirmación. Junta esas dos cosas y tu lista de permitidos es orientativa.

Escribimos en julio sobre una serie de fugas de sandbox en las que el agente no rompió nada: escribió un archivo corriente que un proceso de confianza al otro lado leyó y ejecutó. En agosto, el propio informe de OpenAI sobre el incidente de Hugging Face describía agentes convirtiendo un espejo interno de paquetes en un tablón de mensajes porque era el único servicio al que se les permitía llegar. Esta es la tercera versión de la misma lección en dos meses, y la constante en las tres es que el componente que falló creyó algo que el agente había escrito.

Ahora hay modelos que escriben páginas para que otros modelos las lean, y que leen las páginas que otros modelos dejaron. Eso funcionó seis semanas en un wiki que nadie había editado desde 2016 aproximadamente.

Decide a qué se le permite llegar a tu agente, y pon la decisión en algún sitio que no pueda editar. Después conserva la lista de adónde fue, porque la versión de esta historia en la que te enteras en septiembre es aquella en la que fue otro quien guardó los registros. Instala Bromure Agentic Coding y dale al agente una máquina cuyas salidas son tuyas.