Resumen
libcurl guarda las conexiones en un pool para reutilizarlas entre transferencias. En FTP, una conexión de control puede pasar a TLS con AUTH TLS (STARTTLS) y, en ese momento, presentar un certificado de cliente. El fallo, registrado como CVE-2026-86411, es que una transferencia posterior configurada con CURLUSESSL_NONE y sin certificado podía recibir esa misma conexión del pool. Al reutilizarla, curl no hacía ni un nuevo handshake TLS ni un nuevo login FTP, de modo que el servidor seguía atribuyendo las órdenes a la identidad del certificado del primer handle.
Según el investigador, es una regresión introducida por el commit 4ff212f8ed328fb95261e11add8f4d2f0616895a ("url: connection reuse fixes for starttls") y está muy relacionada con CVE-2016-5420, un fallo antiguo de reutilización de identidad de certificado de cliente entre conexiones. Afecta a curl 8.21.0 y a versiones de desarrollo previas a 8.22.0.
Por qué ocurría
Tres piezas del código de emparejamiento de conexiones encajaban mal:
- FTP declara
PROTOPT_SSL_REUSE(lib/protocol.c), así queurl_match_ssl_use()(lib/url.c:712-730) acepta una conexión ya subida a TLS para una transferencia posterior enftp://plano. - Con
CURLUSESSL_NONE, el emparejamiento fijamay_tls = falseyrequire_tls = false(lib/url.c:1187-1188).url_match_ssl_config()(lib/url.c:883-895) solo llama aCurl_ssl_conn_config_match()si el protocolo tienePROTOPT_SSLo simay_tlses verdadero. Como no se cumple ninguna de las dos, la configuración del certificado de cliente nunca se comparaba. - El commit citado cambió
Curl_ftp_conns_match()(lib/ftp.c:4472-4492): antes exigía queuse_sslfuera idéntico (!=) y pasó a rechazar solo cuando el valor nuevo era mayor que el de la conexión del pool (>). Eso abrió la puerta a la combinaciónCURLUSESSL_CONTROL→CURLUSESSL_NONE.
Pasos de reproducción
Preparación:
- Compilar curl 8.21.0 (u otra revisión afectada) con soporte de FTP, TLS explícito y certificados de cliente.
- Crear una CA de pruebas, un certificado de servidor FTP, un certificado de cliente privilegiado y otro sin privilegios.
- Levantar un servidor FTP que acepte
AUTH TLS, pida certificado de cliente y registre, por cada conexión de control, un identificador estable y la identidad del certificado presentado. - Configurar un recurso u orden FTP que solo funcione si la conexión de control presenta el certificado privilegiado.
- Usar el mismo usuario y contraseña FTP en todas las pruebas, para que la única diferencia de autorización venga del certificado TLS.
Prueba principal:
- Crear un multi handle de libcurl, que aporta el pool de conexiones compartido.
- Crear un primer easy handle hacia el recurso protegido, con
CURLOPT_USE_SSL=CURLUSESSL_CONTROL, el certificado y la clave privilegiados, y añadirlo al multi handle. - Ejecutar la transferencia y comprobar que funciona, que el servidor registra la identidad privilegiada y que la conexión de control queda disponible para reutilizar.
- Quitar ese easy handle, conservando el multi handle y su pool.
- Crear un segundo easy handle al mismo destino FTP, con el mismo usuario y contraseña, pero con
CURLOPT_USE_SSL=CURLUSESSL_NONEy sin certificado. - Ejecutar la segunda transferencia y anotar
CURLINFO_NUM_CONNECTS, la respuesta FTP, el identificador de conexión del servidor y la identidad de certificado asociada. - Resultado vulnerable: la segunda transferencia reutiliza la conexión de control ya subida a TLS, no abre ninguna nueva y accede al recurso protegido con la identidad privilegiada.
Controles:
- Repetir la segunda transferencia con
CURLOPT_FRESH_CONNECTactivado. - Comprobar que se abre una conexión nueva sin certificado y que el servidor deniega el acceso.
- Repetir con TLS explícito y el certificado sin privilegios en el segundo handle.
- Comprobar que curl crea una conexión con la identidad sin privilegios y que el servidor deniega el acceso.
- Control positivo: usar el mismo certificado privilegiado en ambos handles y confirmar que la reutilización y la autorización son las esperadas.
- Para validar un arreglo, exigir que la configuración de identidad de cliente sea compatible siempre que la conexión del pool ya use TLS, aunque la transferencia nueva pida
CURLUSESSL_NONE. - Repetir la secuencia original (privilegiado y luego sin certificado) y verificar que el segundo handle ya no hereda la conexión autenticada del primero.
Lo esperado es que una transferencia sin certificado nunca reutilice una conexión de control autenticada con el certificado de otro handle: debe abrir la suya y recibir solo su propia autorización.
Impacto
Un handle sin certificado de cliente heredaba en silencio la sesión TLS y la identidad de certificado de otro handle que había usado el mismo pool. Si el servidor FTP concede permisos según ese certificado, la segunda transferencia podía leer, subir, modificar o borrar recursos a los que no tendría acceso con su propia configuración: un salto de autorización entre handles, no solo un cifrado más fuerte del pedido.
Hacen falta varias condiciones a la vez, por eso el investigador lo valoró como de severidad baja:
- Los dos handles comparten pool y apuntan al mismo servidor FTP con el mismo
USER/PASS. - El primero usa
CURLUSESSL_CONTROLcon un certificado de cliente privilegiado. - El segundo usa
CURLUSESSL_NONEsin certificado. - El servidor FTP decide la autorización a partir del certificado de cliente.
Remediación
El reporte figura como resuelto y con CVE asignado. La corrección propuesta por el investigador es comparar la configuración del certificado de cliente cuando la conexión del pool ya tiene TLS (por STARTTLS), aunque el handle nuevo no pida TLS (may_tls = false). Como mínimo, url_match_ssl_config() debería tener en cuenta si la conexión existente ya está cifrada, y no solo si el handle nuevo quiere cifrado.
Qué aprender de este caso
- En cualquier pool de conexiones, la comprobación de compatibilidad debe mirar el estado real de la conexión guardada (TLS activo, certificado presentado, usuario autenticado), no solo lo que pide la petición nueva.
- Cambiar una comparación de igualdad (
!=) por una de orden (>) en código de emparejamiento abre combinaciones antes imposibles; ante cambios así en protocolos con STARTTLS conviene probar explícitamente la dirección "de más seguro a menos seguro". - Si un servidor autoriza por certificado de cliente sobre una conexión de larga duración, cualquier cliente que reutilice conexiones hereda esa identidad: es un sitio donde buscar fallos parecidos en otras librerías con pooling para FTP, SMTP u otros protocolos con STARTTLS.
- Cuando un fallo se parece tanto a uno antiguo (aquí CVE-2016-5420), merece la pena convertir la secuencia "con certificado y luego sin él" en un test permanente para que una refactorización no la vuelva a abrir.