Resumen
El fallo está en el backend OpenLDAP de libcurl, concretamente en la función oldap_state_sasl_resp() de lib/openldap.c. Esa función decide si la autenticación SASL ha terminado mirando solo si el progreso es distinto de "en curso". El problema es que "distinto de en curso" agrupa dos situaciones muy diferentes:
SASL_DONE: la autenticación se ha completado correctamente.SASL_IDLE: no queda ningún mecanismo de autenticación que probar.
Un servidor LDAP malicioso puede forzar el segundo caso: responde con un desafío SASL corrupto, libcurl cancela el mecanismo (GSSAPI), vuelve a intentarlo sin mecanismos restantes y Curl_sasl_start() devuelve CURLE_OK con progress == SASL_IDLE. Como la comprobación no distingue ese estado, la conexión pasa a OLDAP_STOP (lista para usarse) como si el login hubiese funcionado.
En la práctica esto anula la autenticación mutua de Kerberos: un atacante sin el keytab del servicio puede hacerse pasar por el servidor LDAP y devolver al cliente las respuestas que quiera.
Datos del caso:
- CVE: CVE-2026-13608
- Debilidad según HackerOne: Authentication Bypass by Primary Weakness
- Severidad: baja
- Enviado el 24-jun-2026 y divulgado el 3-sep-2026; estado final: resuelto.
- Probado sobre el commit
68720b4837284335b2d63cb358f8f6ce65f5bc55(master a 24-jun-2026) en Ubuntu 24.04 x86_64 (Linux 6.8.0), con MIT Kerberos 1.20.1 y OpenLDAP 2.6.10. Según el investigador, el código vulnerable existía desde que se añadió el soporte SASL para OpenLDAP y seguía sin cambios en la versión publicada entonces.
Versión usada en la prueba:
curl 8.21.0-DEV (Linux) libcurl/8.21.0-DEV OpenSSL/3.0.13 zlib/1.3 mit-krb5/1.20.1 OpenLDAP/2.6.10
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns ldap ldaps pop3 pop3s rtsp smtp smtps telnet tftp ws wss
Features: alt-svc AsynchDNS GSS-API HSTS HTTPS-proxy IPv6 Kerberos Largefile libz SPNEGO SSL threadsafe TLS-SRP UnixSockets
Dónde está el error
En lib/openldap.c (línea 786 en el commit probado):
result = Curl_sasl_continue(&li->sasl, data, code, &progress);
if(!result && progress != SASL_INPROGRESS) // ← does not distinguish SASL_DONE from SASL_IDLE
oldap_state(data, li, OLDAP_STOP);
Cuando Curl_sasl_continue() entra en el caso SASL_CANCEL (lib/curl_sasl.c, líneas 773-780), elimina el mecanismo que ha fallado y vuelve a llamar a Curl_sasl_start(). Si ya no quedan mecanismos, esta devuelve CURLE_OK y fija *progress = SASL_IDLE (línea 519). De ahí se llega a la transición a OLDAP_STOP sin haberse autenticado.
Pasos de reproducción
Requisitos
- curl/libcurl compilado con el backend OpenLDAP (
-DUSE_OPENLDAP=ON) y con GSSAPI (-DCURL_USE_GSSAPI=ON). - Un entorno Kerberos funcionando (KDC con su realm y un TGT obtenido con
kinit). - Python 3 para el servidor LDAP falso.
1. Compilar curl y preparar un KDC de pruebas
Las contraseñas que aparecen son valores de laboratorio del propio reporte. Los [REDACTADO] sustituyen a los principales de Kerberos del laboratorio, con forma de dirección de correo: el del usuario de prueba testuser y el del servicio ldap/localhost, ambos en el realm TEST.LOCAL.
# Build curl with OpenLDAP + GSSAPI
cmake -B build -DCURL_DISABLE_LDAP=OFF -DUSE_OPENLDAP=ON \
-DCURL_USE_GSSAPI=ON -DCURL_USE_OPENSSL=ON -DCURL_USE_LIBPSL=OFF \
-DCURL_USE_LIBIDN2=OFF -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j$(nproc)
# Set up minimal KDC (example for MIT Kerberos)
sudo kdb5_util create -s -P testpass123 # realm: TEST.LOCAL
sudo kadmin.local -q "addprinc -pw testpass [REDACTADO]"
sudo kadmin.local -q "addprinc -randkey [REDACTADO]"
sudo kadmin.local -q "ktadd -k /tmp/ldap.keytab [REDACTADO]"
sudo krb5kdc
# Obtain TGT
echo testpass | kinit [REDACTADO]
2. Levantar el servidor LDAP del atacante
El investigador adjuntó un script, mock_gssapi_server.py, que simula al atacante. Importante: este servidor no tiene el keytab del servicio, así que no puede completar Kerberos legítimamente.
python3 mock_gssapi_server.py 3893
Su comportamiento, paso a paso:
- Anuncia únicamente
GSSAPIensupportedSASLMechanisms. - Recibe el BindRequest SASL del cliente, que lleva un AP-REQ de Kerberos que no puede descifrar.
- Contesta con
LDAP_SASL_BIND_IN_PROGRESS(resultCode 14) y unserverSaslCredsbasura (\x60\x10\xDE\xAD...). Ese token GSSAPI inválido hace fallargss_init_sec_context(), libcurl lo traduce enCURLE_BAD_CONTENT_ENCODINGy entra en el estadoSASL_CANCEL. - Cuando llega el BindRequest de cancelación, lo acepta con
LDAP_SUCCESS(resultCode 0).
3. Conectar el cliente al servidor falso
# Ensure valid TGT exists
klist
# Connect to malicious server with GSSAPI auth
./build/src/curl --user "[REDACTADO]:testpass" \
--login-options "AUTH=GSSAPI" \
"ldap://localhost:3893/dc=example,dc=com?*?sub" \
--connect-timeout 5 --max-time 10
4. Comprobar el resultado
Lo correcto sería que curl abortase con error de autenticación (código de salida 67, CURLE_LOGIN_DENIED). En su lugar, da la conexión por autenticada, ejecuta la consulta y muestra como válidos los datos inventados por el servidor:
DN: cn=admin,dc=example,dc=com
cn: admin
uid: attacker-injected
userPassword: {CLEAR}pwned
sshPublicKey: ssh-ed25519 AAAA_ATTACKER_KEY attacker@evil
description: INJECTED via CWE-306 Kerberos bypass
Exit code: 0
Impacto
Con un único desafío SASL malformado, un servidor LDAP malicioso (o un atacante en posición de intermediario) se salta la autenticación SASL del backend OpenLDAP de libcurl. Con GSSAPI/Kerberos, el cliente deja de verificar que el servidor posee un keytab válido, así que el atacante puede:
- Capturar las consultas del cliente: ve el DN base de búsqueda, el filtro y los atributos solicitados.
- Inyectar resultados LDAP arbitrarios: el cliente acepta como fiables entradas de directorio fabricadas, por ejemplo claves SSH públicas, pertenencia a grupos o credenciales falsas.
Para que el ataque funcione tienen que darse todas estas condiciones:
- libcurl compilado con el backend OpenLDAP (
USE_OPENLDAP) y soporte Kerberos. - La aplicación configura autenticación SASL (por ejemplo
CURLOPT_LOGIN_OPTIONS "AUTH=GSSAPI"). - El atacante es intermediario o controla la dirección del servidor LDAP (envenenamiento DNS, ARP spoofing, etc.).
- La conexión no usa LDAPS/TLS, o el cliente no valida bien el certificado del servidor; con TLS correcto el ataque de intermediario ya queda bloqueado por otra vía.
El riesgo es mayor en entornos que confían en Kerberos para verificar la identidad del servidor en conexiones LDAP sin TLS.
Remediación
El reporte figura como resuelto y recibió el CVE-2026-13608. El investigador propuso aceptar solo la finalización explícita de SASL y tratar la ausencia de mecanismos como login denegado:
result = Curl_sasl_continue(&li->sasl, data, code, &progress);
if(!result && progress == SASL_DONE) // ← only accept explicit completion
oldap_state(data, li, OLDAP_STOP);
else if(!result && progress == SASL_IDLE)
result = CURLE_LOGIN_DENIED;
El reporte no detalla qué corrección aplicó finalmente el proyecto curl ni en qué versión.
El hallazgo inicial se hizo con IA y, según el reporte, las afirmaciones técnicas fueron revisadas por expertos humanos; se envió en nombre del equipo Autonomous Code Security (ACS) de Microsoft.