sistema: OPERATIVO
← volver a todos los hacks
AGENTS CRITICAL NEW

DeepSeek Harness: el agente confinado que podía apagar su propio sandbox

Un agente de código confinado podía levantar su propio confinamiento con un solo comando de shell, porque el harness decidía la confianza a partir de una cabecera que suministra quien llama.

2026-09-16 // 8 min affects: deepseek-harness, coding-agents, llm-agents, deepseek

¿De qué se trata?

El 8 de septiembre de 2026, Nir Zadok y Moshe Siman Tov Bustan, de OX Research, publicaron su divulgación de un fallo en DeepSeek Harness (dsh), el harness de código abierto y local de DeepSeek para ejecutar agentes de programación en la máquina de un desarrollador. Un agente confinado podía desactivar su propio sandbox con un solo comando de shell, con la configuración de fábrica, sin exposición de red ni credenciales. VulnCheck, como CNA, publicó el registro ese mismo día con una puntuación de 9,4.

El harness se publicó en agosto de 2026 y superó las 215 000 estrellas en GitHub en pocas semanas. Ofrece una interfaz de navegador respaldada por una API HTTP local y ejecuta los comandos del agente dentro de un sandbox del sistema operativo — bubblewrap, Landlock o Seatbelt según la plataforma — precisamente para que un agente que manipula material no confiable no pueda salir de su espacio de trabajo.

Lo interesante no es que el sandbox tuviera un agujero. Es que el plano de control del sandbox estaba dentro del sandbox.

Cómo funciona

Tres decisiones de diseño corrientes, cada una defendible por separado, se compusieron en una fuga.

El sandbox confinaba archivos, no red. La invocación de bubblewrap pasaba --unshare-pid pero no --unshare-net, de modo que el espacio de nombres de red seguía compartido con el anfitrión. El perfil de Seatbelt era (allow default) con (deny file-write*) encima. El bucle local quedaba, por tanto, accesible desde dentro. La referencia de CLI del propio proyecto para la versión afectada indica con claridad que, en la configuración por defecto, las lecturas y el acceso a la red no están confinados.

La API local determinaba la confianza a partir de una cabecera del cliente. Una única función, isTrustedApiRequest, leía la cabecera Host y concedía acceso si el valor era una autoridad de bucle local o figuraba en una lista trustedHosts configurada. Nunca comparaba ese valor con la dirección real del par. Frente a la amenaza para la que se escribió — DNS rebinding, donde un navegador no puede mentir sobre Host — funcionaba. Frente a un cliente no sujeto a las reglas del navegador, no. Un comentario en el archivo lo dice: esa comprobación, en palabras de sus autores, “is not an auth layer” (no es una capa de autenticación).

Las llamadas de shell rutinarias no requerían aprobación. La aprobación solo se aplicaba cuando un comando solicitaba más acceso del que la sesión ya tenía. La fuga no solicitaba nada: cambiaba el ajuste de la sesión.

agente (dentro del sandbox)
   │  llamada a la herramienta bash — sin aprobación requerida

curl → http://127.0.0.1:<puerto>/…   Host: <autoridad de bucle local>
   │  isTrustedApiRequest() lee la cabecera, no el par

modo de sesión := danger-full-access,  aprobación := never


todo comando posterior se ejecuta sin confinamiento ni aviso

El harness, además, entrega al shell del agente la dirección de esa interfaz y el identificador de la sesión en curso, así que no hace falta ninguna fase de descubrimiento.

Dos detalles del registro de sesión documentado por OX merecen atención. En él no aparece ningún evento approval/request. Y el cambio de política se atribuye a source: {kind: 'user'}: el harness no podía distinguir el shell de su propio agente de la persona ante el teclado. OX ejecutó también un control emparejado: dos sesiones, mismo preajuste por defecto, misma escritura a una ruta fuera del espacio de trabajo. La que había hecho la llamada tuvo éxito; la otra fue bloqueada. El sandbox funcionaba hasta el momento en que se le pidió que dejara de hacerlo.

Existía una segunda vía allí donde el puerto fuera alcanzable desde fuera — un túnel, un proxy inverso, un reenvío SSH, un port forward del editor. Entonces un llamante remoto no autenticado podía dirigir al agente y descargar todas las conversaciones almacenadas, sin clave de API y sin llamada al modelo.

Por qué importa

La única condición previa para la fuga local era que el agente ejecutara un comando inducido por texto suministrado por el atacante. Esa es exactamente la clase de entrada que el sandbox existe para contener. Así que el control fue derrotado por aquello contra lo que fue construido — lo que lo convierte en un fallo de confinamiento y no de filtrado, y el confinamiento es la capa a la que uno recurre cuando el filtrado falla.

Un harness de agente de código merece ser atacado porque sostiene un shell que corre con la autoridad ambiental del desarrollador que lo lanzó: árboles de fuentes, comandos de compilación y prueba, y todo aquello que esa estación de trabajo alcance en claves SSH, credenciales de nube y sistemas internos. Hemos cubierto variantes cercanas: localhost como superficie de ataque de agentes, el DNS rebinding contra servidores MCP locales, la frontera de privilegio harness/modelo y la configuración de repositorio que ejecuta código del atacante fuera del sandbox. El patrón se repite porque el agente reside en el mismo host que aquello que lo gobierna.

Dos aspectos de la gestión merecen mención. Miembros de la comunidad describieron esta misma fuga en el foro público del proyecto los días 13 y 14 de agosto de 2026, once días antes del informe por la vía del proveedor. Y la compilación corregida 0.1.2-alpha.1 (27 de agosto) nunca se publicó en npm, que es adonde las propias instrucciones del proyecto envían a los usuarios; la primera versión corregida en npm fue 0.1.2-alpha.2, el 30 de agosto. Un parche que no llega a la ruta de instalación todavía no es un parche.

El archivo SAFETY.md del proyecto declara que el software no ha pasado una auditoría de seguridad y que el sandbox y los avisos de aprobación no garantizan el aislamiento. Ese aviso es honesto, y conviene leerlo como una especificación y no como una fórmula de rigor.

Defensas

Sitúe el plano de control del agente fuera de su alcance. Si la API capaz de cambiar el nivel de privilegio de una sesión es alcanzable desde dentro del sandbox, el sandbox es meramente indicativo. Aísle el espacio de nombres de red (--unshare-net en bubblewrap, un deny network* explícito en Seatbelt) o ligue la API de control a un socket que el perfil deniegue. Este es el arreglo que generaliza más allá de un producto.

Nunca derive confianza de una cabecera que controla el cliente. Host, Origin y X-Forwarded-For los suministra quien llama. “Solo local” significa comprobar la dirección del par o, mejor, exigir una credencial — que es lo que hace el parche: un token de un solo uso impreso al arrancar, canjeado por una cookie firmada que toda llamada debe portar.

Controle el cambio de privilegio, no solo el acto privilegiado. Una aprobación que se dispara cuando un comando pide más acceso, pero no cuando una llamada lo concede, deja toda la ruta de escalada sin vigilancia. Trate cualquier modificación del modo de sandbox, la política de aprobación o una lista de permitidos como la acción de mayor aprobación del sistema.

Haga distinguible al actor en su pista de auditoría. Un cambio de política registrado como procedente del “usuario” cuando procede del shell de un agente derrota por igual la detección y el análisis forense. Vincule un principal distinto a las llamadas de origen agéntico y alerte ante cualquier cambio de privilegio que lleve ese principal.

Audite lo que ya ejecuta. Actualice a una versión posterior al arreglo; compruebe qué versión del harness incorpora cualquier envoltorio de escritorio de terceros, ya que sus mantenedores fijan sus propias copias. Elimine túneles, proxies y reenvíos de puerto de editor que expongan la interfaz local de un agente. Y allí donde no pueda verificar el confinamiento, asuma que una sesión comprometida posee la autoridad completa del desarrollador y dimensione sus credenciales en consecuencia.

Observe lo que el parche no cambia: el sandbox sigue sin confinar lecturas ni acceso a red, y el shell del agente sigue recibiendo la dirección de la interfaz. La autenticación cerró la puerta que estaba abierta; no sacó la puerta de la habitación.

Estado

ElementoDetalle
ReferenciaCVE-2026-82533 (VulnCheck como CNA), CWE-807 — confianza en entradas no fiables para una decisión de seguridad
SeveridadCVSS 9,4
AfectadoDeepSeek Harness (dsh) 0.1.1-rc.2 y anteriores
Informes comunitariosReportes públicos en el foro del proyecto, 13 y 14 de agosto de 2026
DivulgaciónReportado a VulnCheck el 24 de agosto de 2026
Corrección0.1.2-alpha.1, 27 de agosto de 2026 (solo GitHub); primera versión corregida en npm 0.1.2-alpha.2, 30 de agosto de 2026
VerificaciónReprueba y confirmación de la remediación por OX Research, 30 de agosto de 2026
PublicaciónRegistro CVE y análisis de OX, 8 de septiembre de 2026
Brecha residualEl sandbox sigue sin confinar lecturas ni red; el shell del agente sigue recibiendo la dirección de la interfaz de control

Sources