
Ataques
CVE-2025-67635: DoS no autenticado en el endpoint full-duplex de Jenkins CLI

Analista de seguridad
Actualizado
12 dic 2025
5 min
TL;DR:
El endpoint del plain CLI de Jenkins (
/cli?remoting=false) empareja dos peticiones POST (download/upload) a través de un UUID deSessioncompartido y es accesible sin permisos Overall/Read.Se encadenan dos bugs: Un
HashMapno sincronizado enCLIActionpierde una mitad de la sesión (race condition), y los loops de espera del protocolo CLI (ServerSideImpl.run,FullDuplexHttpService.upload) no tienen timeout.Un atacante puede finalizar sus llamadas HTTP en milisegundos mientras dejan threads de Jetty durante 15 segundos (ventana de la race condition) o indefinidamente (protocolo abandonado), causando DoS asimétrico en todo el controller.
Afecta a core ≤ 2.540 y LTS ≤ 2.528.2 (CWE-362/CWE-404, CVSS: 7,5). Se solucionó en 2.541 y 2.528.3 usando
ConcurrentHashMap, agregando timeouts en el handshake y cerrando streams en caso de error.¿Cómo mitigarlo ahora mismo? Actualiza, o bloquea el acceso al endpoint del plain CLI desde redes no confiables y captura thread dumps para confirmar que no haya threads atascados en
CLIAction$ServerSideImpl.runoFullDuplexHttpService.upload/download.
¿Qué hace el endpoint?
El CLI sin Remoting construye un canal de full-duplex a partir de dos POSTs de HTTP:
Lado download:
Side: download, abre/cli?remoting=false, el servidor escribe un byte y espera la mitad de uploadLado de carga:
Side: upload, mismo UUID deSession, provee el input stream
hudson.cli.CLIAction conecta esto con jenkins.util.FullDuplexHttpService, almacenando las sesiones activas en un registro común entre peticiones.
Causa raíz #1: Registro de sesiones sin sincronizar (race condition)
CLIAction mantiene el mapa de sesiones en un HashMap simple compartido por todos los threads de petición:
FullDuplexHttpService.Response.generateResponse llama a services.put(uuid, service) para el lado download y a services.get(uuid) para el lado upload. Como HashMap no es thread-safe, puts/gets concurrentes bajo carga pueden
devolver
nullpara un lado download válido;perder entradas durante un resize;
dejar threads de download dentro de
FullDuplexHttpService.downloadesperando hasta 15 segundos por un upload que ya llegó.
El resultado: Cada par afectado por la race condition bloquea un thread del servlet durante todo el timeout mientras los sockets del atacante se cierran de inmediato, causando un DoS asimétrico y violando el requisito de seguridad “Hacer que los flujos lógicos críticos sean seguros”.
Causa raíz #2: Faltan timeouts en el protocolo (bloqueo determinístico)
Incluso cuando las dos mitades se emparejan correctamente, el handshake del protocolo puede entrar en deadlock porque ningún lado tiene timeout:
Si el cliente corta la conexión antes de enviar frames del CLI, el thread de download se bloquea en ServerSideImpl.run() y el de upload en upload() sin timeout, consumiendo dos threads de Jetty por intento hasta agotar el controller.
Notas de explotación
Ambos vectores solo requieren conectividad de red a /cli:
Escenario A (race condition): Dispara pares download/upload superpuestos con ligero jitter para que el lado upload ocasionalmente vea
null. Los threads se acumulan enFullDuplexHttpService.downloadpor ~15 segundos cada uno.Escenario B (abandono): Abre ambas mitades, déjalas que se emparejen, luego cierra sin enviar frames del CLI. Los threads quedan en
CLIAction$ServerSideImpl.runy enFullDuplexHttpService.uploadindefinidamente, sin necesidad de ventana de timing.
A continuación se presentan las mismas PoCs compartidas con el equipo de seguridad de Jenkins:
PoC: racecond_a.py (race en HashMap, dos downloads por UUID)
PoC: racecond_b.py (abandono de protocolo, bloqueo determinístico)
Evidencia
Thread dump (Escenario B, controller de prueba Jenkins 2.516.2): Atascado exactamente en los call sites sin timeout:
Evidencia en video: Reproducción del crash end-to-end contra un controller recién instalado:
En un build vulnerable, verás docenas de threads de solicitud esperando en esos call sites, y las llamadas CLI regulares comenzarán a dar timeout.
Impacto
DoS no autenticado: No se requiere Overall/Read para acceder a
/cli?remoting=falseBajo costo para el atacante: Los sockets se cierran de inmediato, el servidor retiene el trabajo (15 segundos por intento de race, infinitamente por abandono)
Degradación del controller completo: Los threads del servlet y los stream de E/S se acumulan, otros endpoints empiezan a dar timeout
Detalles del parche
Commit: efa1816
CLIActionahora almacena las sesiones en unConcurrentHashMap, eliminando las pérdidas delHashMapque potenciaban el DoS asimétrico.ServerSideImpl.runyFullDuplexHttpService.uploadadoptaron waits acotados porCONNECTION_TIMEOUTcon wake-ups de 1 segundo y logs de DEBUG, de modo que los handshakes abandonados se deshacen en lugar de estacionar threads.PlainCLIProtocolahora siempre llama aside.handleClose()en un bloquefinally, asegurando que ambas mitades se desmonten incluso ante errores de lectura o excepciones de runtime.Se agregó cobertura de regresión en
Security3630Test(JUnit 5): Reduce el timeout del CLI para test, ejercita la race condition con invocaciones CLI concurrentes y verifica que los threads se liberen después de streams truncados.Efecto neto: El emparejamiento download/upload ahora falla rápido y libera threads de Jetty en vez de bloquearse indefinidamente esperando contrapartes faltantes.
Los cambios restauran la conformidad del path CLI full-duplex con el requisito “Hacer que los flujos lógicos críticos sean seguros”.
Referencias de CVE y advisory
A SECURITY-3630 se le asignó CVE-2025-67635 (CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H). Consulta el registro del CNA en cve.org y la entrada de NIST en NVD para metadatos canónicos.
Write-up oficial de Jenkins: Jenkins Security Advisory 2025-12-10: SECURITY-3630.
Mitigación y hardening
Si no puedes actualizar inmediatamente, haz lo siguiente:
Deshabilita o aplica firewall al endpoint de plain CLI; utiliza preferentemente el WebSocket CLI con autenticación adecuada.
Reduce los límites de threads de Jetty solo como último recurso (no elimina el bug).
Monitorea thread dumps buscando estados de wait en
CLIAction$ServerSideImpl.runyFullDuplexHttpService.upload/download.
Indicadores de compromiso
Mensajes repetidos de
IOException: No download side found for <uuid>en los logs.Thread dumps que muestran muchos
TIMED_WAITINGenFullDuplexHttpService.downloadoWAITINGenCLIAction$ServerSideImpl.run/FullDuplexHttpService.upload.Picos en peticiones a
/cli?remoting=falsesin encabezados de autenticación.
Parchea de inmediato. Este es un path de DoS servido en bandeja, alcanzado por red en deployments de Jenkins con configuración por defecto.
Get started with Fluid Attacks' PTaaS right now
Suscríbete a nuestro boletín
Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.
Otros posts


















