El servidor Kanban de Cline: un secuestro WebSocket de origen cruzado que lleva a la ejecución de código
Una divulgación de mayo de 2026 muestra que el servidor WebSocket local del Kanban de Cline no valida ningún origen: cualquier sitio que visite el desarrollador puede leer el espacio de trabajo e inyectar comandos en un agente activo.
¿Qué es esto?
El 8 de mayo de 2026, un aviso de seguridad de GitHub reveló una falla de secuestro de WebSocket de origen cruzado en el tablero Kanban que se incluye con el agente de código Cline. El paquete npm kanban, iniciado por la CLI cline, arranca un servidor WebSocket local en 127.0.0.1:3484 y acepta toda conexión entrante sin verificar la cabecera Origin ni exigir ningún token. El NVD publicó la entrada el 1 de junio de 2026 y la calificó como crítica (CVSS 9.6); el enriquecimiento ADP de CISA, el 3 de junio, la marcó como explotable mediante PoC, con impacto técnico total. En el momento de la publicación no había ninguna versión corregida: todas las versiones hasta Cline 2.13.0 inclusive están afectadas.
La técnica es antigua, el objetivo es nuevo. El secuestro de WebSocket entre sitios (CSWSH) figura desde hace años en el corpus de OWASP. Lo que cambió es qué hay detrás del socket: un agente de código autónomo con una terminal activa y acceso completo al sistema de archivos, alcanzable desde cualquier pestaña del navegador.
Cómo funciona
Los navegadores aplican la política del mismo origen a fetch y XMLHttpRequest, pero las solicitudes de actualización (upgrade) de WebSocket no están sujetas a CORS. Una página en https://un-sitio.example puede abrir ws://127.0.0.1:3484/... y el navegador completará el handshake. Lo único que debería detenerlo es que el servidor valide la cabecera Origin durante el upgrade, algo que el servidor Kanban de Cline no hacía.
Tres puntos de acceso quedaban expuestos sin validación de origen ni autenticación. Un socket de estado del runtime transmite una instantánea completa del espacio de trabajo en cuanto un cliente se conecta: rutas del sistema de archivos, rama de git, títulos y descripciones de tareas y mensajes en vivo del agente. Un socket de E/S de terminal escribe bytes crudos directamente en el pseudoterminal del agente. Un socket de control puede detener las tareas activas. Conceptualmente, el manejador de upgrade vulnerable se veía así:
server.on("upgrade", (request, socket, head) => {
// Se verifica la ruta, pero la cabecera Origin nunca se valida,
// de modo que una página de origen cruzado completa el handshake.
runtimeStateHub.handleUpgrade(request, socket, head, { ... });
});
A partir de ahí, la cadena es: conectarse desde una página maliciosa y leer la instantánea transmitida; vigilar un evento task_sessions_updated que marca un agente en ejecución; y luego enviar texto al socket de terminal terminándolo con un retorno de carro. El agente trata el texto inyectado como entrada del usuario, y el \r lo confirma exactamente como si el desarrollador hubiera pulsado Intro, convirtiendo la inyección de texto en ejecución del comando [REDACTED shell command]. No se requiere ninguna interacción más allá de tener el Kanban abierto y visitar la página equivocada.
Ejemplo de prompt
El fragmento siguiente muestra por qué funciona el secuestro: el servidor local completa el handshake de WebSocket sin comprobar la cabecera Origin, así que una página en cualquier pestaña puede alcanzarlo. La carga está redactada — esto es para defensores.
# Cross-Site WebSocket Hijack on a local agent server (illustrative, defensive)
# A page the developer visits opens a socket the browser never blocks:
ws = connect("ws://127.0.0.1:3484/[terminal-endpoint]")
# Injected text + a carriage return would submit as agent input:
ws.send("[hidden instruction]\r") # [payload redacted]
# Root cause: the server accepts the upgrade without checking Origin.
# Defense: validate the Origin header on every WebSocket upgrade,
# require a per-session token, and bind to a random loopback port.
Por qué importa
Es un ejemplo concreto de un patrón que se repite a medida que los agentes llegan al escritorio: un servicio local potente, ligado a la interfaz de bucle (loopback), tratado como de confianza porque «solo localhost puede alcanzarlo». El loopback no es una frontera de seguridad frente a un navegador que el usuario ya está ejecutando. Cualquier marco publicitario, dependencia comprometida o enlace malicioso puede alojar el JavaScript. El solo impacto sobre la confidencialidad —rutas del repositorio, nombres de ramas no publicadas, prompts de tareas y las conversaciones del propio agente— ya constituye una fuga grave para desarrolladores que trabajan con código privado, y el paso de inyección en la terminal lo eleva a ejecución remota de código, tanto en macOS como en Linux y Windows.
Defensas
Las recomendaciones del aviso se generalizan a cualquier servidor de agente local:
- Valide la cabecera
Originen cada upgrade de WebSocket. Rechace todo lo que no sea el origen de la propia interfaz del agente. Es la corrección que cierra la clase completa. - Exija un token de sesión imposible de adivinar. Genere un secreto aleatorio al arrancar el servidor, entréguelo a la interfaz legítima al cargar la página y solicítelo como parámetro de conexión. Las páginas de origen cruzado no pueden adivinarlo.
- Autentique los sockets sensibles de forma independiente. Los canales de E/S de terminal y de control de tareas deben verificar a quien llama, no asumir que localhost equivale a confianza.
- Vincule el servicio a un puerto alto aleatorio o a un socket de dominio unix cuando sea posible, y nunca transmita secretos en una instantánea no solicitada al conectar.
- Como usuario, mientras no disponga de una versión corregida: evite ejecutar el tablero Kanban mientras navega por sitios no confiables, y trate cualquier interfaz de agente en localhost como accesible desde Internet.
Estado
| Elemento | Valor |
|---|---|
| Referencia | CVE-2026-44211 / GHSA-5c57-rqjx-35g2 |
| Clase | CSWSH (CWE-1385) + ausencia de autenticación (CWE-306) |
| Afectado | Cline <= 2.13.0 (paquete npm kanban) |
| CVSS 3.1 | 9.6 crítico (AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H) |
| Divulgación | Aviso GitHub 2026-05-08; NVD 2026-06-01 |
| Parche | Ninguno disponible en la fecha de divulgación |
| Explotación | PoC público (SSVC de CISA: impacto técnico total) |
Fechas clave: 5 de mayo de 2026 — reserva del CVE. 8 de mayo de 2026 — publicación del aviso de GitHub. 1 de junio de 2026 — entrada en el NVD. 3 de junio de 2026 — enriquecimiento ADP de CISA.