Resumen
El cliente oficial en C de MariaDB (mariadb-connector-c) tenía un error de lógica en run_plugin_auth(), dentro de plugins/auth/my_auth.c. Esa función atiende las peticiones de cambio de plugin de autenticación que envía el servidor (paquete 0xFE, AuthSwitchRequest). Antes de aceptar un plugin que no aplica hash a la contraseña, el cliente debía comprobar si la verificación del certificado TLS había fallado. Pero la comprobación (línea 875) solo miraba uno de los dos bits de error:
if ((mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_TRUST) &&
!password_and_hashing(mysql, auth_plugin))
Los valores posibles de tls_verify_status son:
MARIADB_TLS_VERIFY_TRUST= 1 (falla la cadena de confianza de la CA)MARIADB_TLS_VERIFY_HOST= 2 (el hostname no coincide)MARIADB_TLS_VERIFY_AUTO= 3 (fallan las dos cosas)
Si solo falla el hostname, el estado vale 2, y 2 & 1 da 0: la guarda no se activa. El servidor puede entonces pedir mysql_clear_password y el cliente le manda la contraseña en claro. Hay una comprobación general posterior (línea 894) que acaba cortando la conexión porque tls_verify_status no es cero, pero llega tarde: el plugin ya se ha ejecutado y la contraseña ya ha salido por la red.
La comprobación de la autenticación inicial (línea 484) usa otra condición, tls_verify_status > MARIADB_TLS_VERIFY_AUTO, que es más permisiva a propósito: deja pasar plugins con hash en el primer intercambio aunque la verificación no sea completa. El camino del auth-switch, en cambio, debería ser más estricto: si a mitad de la autenticación el servidor pide un plugin sin hash, cualquier fallo de verificación tendría que bloquearlo.
Afectaba a todas las versiones hasta la rama 3.4 actual en el momento del reporte (comprobado en el commit a4a04c52, 82 commits después de la etiqueta v3.4.9; la v3.4.9 también es vulnerable), en cualquier plataforma, con la compilación por defecto con TLS, en la que mysql_clear_password viene registrado como plugin dinámico.
El hallazgo es de Aisle Research, que lo comunicó a Red Hat por el proceso de embargo; el reporte en HackerOne lo envió mschorm el 28 de julio de 2026 y se divulgó el 4 de noviembre de 2026. Tiene asignado el CVE-2026-107819.
Pasos de reproducción
- Montar un servidor de prueba compatible con MariaDB/MySQL con un certificado TLS emitido por una CA de confianza, pero para otro nombre (por ejemplo, el servidor está en
db.example.comy el certificado es paraother.example.com). - Configurar un cliente con Connector/C y la verificación del hostname activada (
MYSQL_OPT_SSL_VERIFY_SERVER_CERT=1). - Conectar. El handshake TLS termina bien porque la CA es de confianza, pero
ma_verification_callback()(enopenssl.c) dejatls_verify_status = MARIADB_TLS_VERIFY_HOST(valor 2). - En la comprobación inicial de la línea 484,
2 > 3es falso, así que se sigue adelante.password_and_hashing()devuelve verdadero para el plugin por defecto (por ejemplo,caching_sha2_password) y empieza el intercambio con hash. - El servidor del atacante responde con un
0xFEAuthSwitchRequest que pidemysql_clear_password. - En la guarda de la línea 875,
(2 & 1) == 0, así que no bloquea nada. clear_password_auth_client()(enmariadb_cleartext.c) envía la contraseña completa en claro convio->write_packet().- El atacante lee la contraseña en el paquete de respuesta al auth-switch.
- Solo después de que el plugin termine, la comprobación de la línea 894 rechaza la conexión y el cliente recibe el error.
Impacto
Un atacante en posición de intermediario que tenga cualquier certificado válido de una CA de confianza, aunque sea para otro dominio, puede quedarse con la contraseña de la base de datos en claro. Con ella puede autenticarse después contra el servidor legítimo y acceder con los permisos de ese usuario.
Condiciones necesarias:
- Estar en medio entre cliente y servidor.
- Disponer de un certificado válido de una CA de confianza (basta con que sea para otro hostname).
- Que el cliente tenga disponible
mysql_clear_password(lo tiene por defecto).
Remediación
La corrección propuesta cambia la máscara de bits por una comprobación de distinto de cero, de modo que cualquier fallo de verificación (confianza, hostname o ambos) impida usar un plugin sin hash en el auth-switch. La comprobación de la autenticación inicial no se toca. El reporte figura como resuelto.
diff --git a/plugins/auth/my_auth.c b/plugins/auth/my_auth.c
index 8ee14b66..17007b8b 100644
--- a/plugins/auth/my_auth.c
+++ b/plugins/auth/my_auth.c
@@ -872,12 +872,12 @@ retry:
auth_plugin= &dummy_fallback_client_plugin;
/* can we use this plugin with this tls server cert ? */
- if ((mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_TRUST) &&
+ if (mysql->net.tls_verify_status &&
!password_and_hashing(mysql, auth_plugin))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
- "Certificate verification failure: The certificate is NOT trusted.");
+ "Certificate verification failure: peer certificate verification failed.");
return 1;
}
goto retry;
Qué aprender de este caso
- Cuando un estado de error se guarda como banderas de bits, revisa cada
estado & BANDERAque decide algo de seguridad: si la pregunta real es "¿ha fallado algo?", la condición correcta esestado != 0, no comprobar un bit concreto. - En clientes de bases de datos y protocolos con renegociación de autenticación, mira qué pasa cuando el servidor pide cambiar de método a mitad del proceso: ese segundo camino suele tener comprobaciones distintas (y a veces más flojas) que el primero.
- Una comprobación que corta la conexión "al final" no protege si el secreto ya se ha enviado antes; las decisiones sobre si se puede mandar una credencial tienen que tomarse antes de ejecutar el plugin que la envía.
- Si una aplicación no necesita
mysql_clear_password, conviene no tenerlo disponible en el cliente, para que un servidor malicioso no pueda pedirlo.