Resumen
curl mantiene un conjunto de conexiones abiertas para reutilizarlas entre peticiones al mismo host. Con la autenticación HTTP Negotiate (SPNEGO/Kerberos vía GSS-API), la conexión queda autenticada como un todo, así que antes de reaprovecharla libcurl tiene que comprobar que la nueva petición pide exactamente la misma identidad de autenticación.
Esa comprobación no tenía en cuenta el nombre del servicio (el service principal), que se fija con CURLOPT_SERVICE_NAME en la biblioteca o --service-name en la línea de comandos. Resultado: si una primera petición se autenticaba contra el servicio svcA y una segunda pedía svcB en el mismo host, libcurl 8.20.0 reutilizaba la conexión ya autenticada, no enviaba ninguna cabecera Authorization nueva y no hacía un handshake Negotiate para svcB. El servidor atendía la petición a svcB con el contexto de autenticación que se había creado para svcA.
El investigador lo presentó como una variante de un arreglo incompleto de CVE-2026-5545, que ya había corregido otros casos de reutilización indebida de conexiones Negotiate, pero no el cambio de nombre de servicio. El reporte se publicó como CVE-2026-8458. HackerOne lo clasifica como Authentication Bypass by Primary Weakness con severidad baja.
Según el análisis del reporte, el nombre de servicio sí interviene al crear el contexto de autenticación, en lib/http_negotiate.c (con STRING_SERVICE_NAME / STRING_PROXY_SERVICE_NAME, que se pasan a la ruta de SPNEGO), pero la lógica de reutilización de conexiones autenticadas de lib/url.c no parecía tratarlo como parte de la identidad de la conexión.
Versión probada:
curl 8.20.0 (x86_64-pc-linux-gnu) libcurl/8.20.0 OpenSSL/3.6.1 zlib/1.3.1 mit-krb5/1.22.1
Release-Date: 2026-04-29
Protocols: http https ipfs ipns ws wss
Features: alt-svc AsynchDNS Debug GSS-API HSTS HTTPS-proxy IPv6 Kerberos Largefile libz SPNEGO SSL threadsafe TLS-SRP UnixSockets
Pasos de reproducción
La prueba se hizo en Linux x86_64 con un curl 8.20.0 compilado desde el tarball oficial con GSS-API activado. Para no depender de un Kerberos real, el investigador usó una biblioteca que intercepta las llamadas GSS (LD_PRELOAD) y un servidor de prueba con keep-alive en Python. Ambos iban como adjuntos del reporte (poc_gss_service_reuse.c y poc_negotiate_service_server.py) y su código no forma parte del texto divulgado.
- Compilar curl 8.20.0 con GSS-API / SPNEGO:
cd /tmp
curl -fsSLo /tmp/curl-8.20.0.tar.xz https://curl.se/download/curl-8.20.0.tar.xz
tar -xf /tmp/curl-8.20.0.tar.xz
cd /tmp/curl-8.20.0
./configure \
--prefix=/tmp/curl-8.20.0-install \
--enable-debug \
--with-openssl \
--with-gssapi \
--disable-ldap \
--disable-ldaps \
--without-libidn2 \
--without-libpsl \
--without-brotli \
--without-zstd \
--without-libssh2 \
--without-nghttp2 \
--disable-ftp \
--disable-file \
--disable-tftp \
--disable-rtsp \
--disable-dict \
--disable-telnet \
--disable-pop3 \
--disable-imap \
--disable-smb \
--disable-smtp \
--disable-gopher \
--disable-mqtt \
--disable-manual
make -j2
- Compilar el interceptor GSS y arrancar el servidor de prueba:
gcc -shared -fPIC -o /tmp/libpoc_gss_service_reuse.so /tmp/poc_gss_service_reuse.c -ldl
python3 /tmp/poc_negotiate_service_server.py
- Lanzar en un solo proceso dos peticiones encadenadas con
--next: la primera con serviciosvcAy la segunda consvcB, al mismo host:
LD_LIBRARY_PATH=/tmp/curl-8.20.0/lib/.libs \
LD_PRELOAD=/tmp/libpoc_gss_service_reuse.so \
POC_GSS_CREDS=NTLM_Alice \
/tmp/curl-8.20.0/src/curl -sv --noproxy '*' \
--negotiate --service-name svcA http://127.0.0.1:18080/svcA \
--next \
--negotiate --service-name svcB http://127.0.0.1:18080/svcB
- Revisar la salida. La primera petición hace su handshake Negotiate normal para
svcA. En la segunda, curl anuncia que reutiliza la conexión, no mandaAuthorizationy el servidor responde como sisvcBheredase la autenticación desvcA:
* Reusing existing http: connection with host 127.0.0.1
> GET /svcB HTTP/1.1
> Host: 127.0.0.1:18080
> User-Agent: curl/8.20.0
> Accept: */*
...
SVC_B_REUSED_AUTH_FROM_A
- Como control, pedir solo
svcBen un proceso nuevo:
LD_LIBRARY_PATH=/tmp/curl-8.20.0/lib/.libs \
LD_PRELOAD=/tmp/libpoc_gss_service_reuse.so \
POC_GSS_CREDS=NTLM_Alice \
/tmp/curl-8.20.0/src/curl -sv --noproxy '*' \
--negotiate --service-name svcB http://127.0.0.1:18080/svcB
- Aquí sí hay un handshake Negotiate propio para
svcB, se envíaAuthorization: Negotiate ...y la respuesta es otra:
> GET /svcB HTTP/1.1
> Authorization: Negotiate ...
...
SVC_B_FRESH_AUTH
Impacto
Es una confusión del destino de la autenticación. Los nombres de servicio sirven para separar contra qué servicio se autentica el cliente; con este fallo, una petición pensada para el servicio B viaja por una conexión ya autenticada para el servicio A del mismo host, sin nuevo handshake ni cabecera Authorization para B. En la prueba, la petición a /svcB se atendió bajo el contexto establecido para svcA, mientras que una petición limpia a /svcB exigía su propio intercambio Negotiate y obtenía un resultado distinto.
Para que ocurra, una misma aplicación (o una misma invocación de curl) tiene que hacer peticiones al mismo host con distintos nombres de servicio y reutilizar la conexión entre ellas. La severidad que figura en HackerOne es baja.
Remediación
El reporte figura como resuelto, pero el texto divulgado no detalla el parche ni la versión que lo corrige. Lo que se desprende del análisis es que el nombre de servicio (y el de proxy) debe formar parte de los criterios con los que libcurl decide si una conexión autenticada con Negotiate se puede reutilizar.
Qué aprender de este caso
- Cuando una conexión queda autenticada entera (Negotiate, NTLM), revisa la función que decide reutilizarla (aquí, la búsqueda de conexiones de
lib/url.c) y comprueba que compara todos los parámetros que intervienen al crear el contexto de autenticación, no solo host, puerto y usuario. - Tras un CVE de reutilización de conexiones, busca qué otras opciones alimentan la autenticación (
CURLOPT_SERVICE_NAME,CURLOPT_PROXY_SERVICE_NAME…) y prueba a cambiarlas una a una entre peticiones encadenadas: es justo el hueco que dejó el arreglo de CVE-2026-5545. - Para reproducir fallos de Kerberos sin montar un KDC, un interceptor de las funciones GSS cargado con
LD_PRELOADy un servidor keep-alive que registre qué cabeceras llegan por cada conexión bastan para demostrar la reutilización. - Si tu aplicación usa libcurl con varios principales de servicio contra el mismo host, no compartas el pool de conexiones entre ellos (o fuerza una conexión nueva) hasta tener una versión corregida.