Resumen
El fallo está en la lógica con la que libcurl decide si una conexión ya abierta del pool puede aprovecharse para una petición nueva que usa autenticación Negotiate (SPNEGO). El investigador lo reprodujo en Windows con SSPI en la versión 8.21.0; con la 8.20.0 no se reproduce.
Cuando una petición se autentica con Negotiate sin credenciales explícitas, SSPI usa las del usuario de Windows que ejecuta el proceso (credenciales "ambientales"). Esas conexiones pueden quedar con conn->creds == NULL. La función url_match_auth_nego() solo compara credenciales y origen si conn->creds tiene valor, así que con esas conexiones la comprobación directamente no se hace:
if(m->want_nego_http) {
if(conn->creds &&
(!Curl_creds_same(conn->creds, m->data->state.creds) ||
!Curl_peer_equal(conn->creds_origin, m->data->state.origin)))
return FALSE;
}
El resultado es que una petición posterior que sí trae credenciales propias puede acabar enviándose por la conexión ya autenticada, y el servidor la procesa como si viniera del primer usuario. HackerOne lo clasifica como "Authentication Bypass by Primary Weakness", con identificador CVE-2026-19931 y severidad media.
El reporte se envió el 7 de agosto de 2026 y se hizo público el 3 de septiembre de 2026.
Pasos de reproducción
El investigador adjuntó dos ficheros: poc.c, un reproductor mínimo con libcurl, y server.cs, un servidor Negotiate local para Windows. El procedimiento es:
- Arrancar el servidor Negotiate local de Windows (
server.cs). - Ejecutar el PoC como un usuario de Windows A (en su prueba,
Administrator). - La petición A usa
CURLAUTH_NEGOTIATEsin credenciales explícitas. - La petición B usa otro easy handle dentro del mismo
CURLMy pasa credenciales explícitas deuserB. - Con curl 8.21.0, la petición B reutiliza la conexión que abrió A.
Resultado observado, comparando ambas versiones:
8.20.0:
A default -> Administrator
B explicit userB -> userB
8.21.0:
A default -> Administrator
B explicit userB -> Administrator
Si se desactiva la reutilización de conexiones, B se autentica correctamente como userB.
Impacto
Una petición configurada para un usuario puede ejecutarse con la identidad de una conexión del pool autenticada previamente por otro. En la prueba, la petición que debía hacerse como userB llegó al servidor como Administrator.
Remediación
El investigador propuso que la comparación de credenciales y origen no dependa de que conn->creds tenga valor, sino de que la conexión haya entrado ya en el estado de autenticación Negotiate:
if(m->want_nego_http) {
- if(conn->creds &&
+ if((conn->http_negotiate_state != GSS_AUTHNONE) &&
(!Curl_creds_same(conn->creds, m->data->state.creds) ||
!Curl_peer_equal(conn->creds_origin, m->data->state.origin)))
return FALSE;
}
El reporte figura como resuelto y con CVE asignado, pero no detalla qué cambio concreto aplicó finalmente el proyecto curl ni en qué versión.