Reports
MEDIA[AUTENTICACIÓN]#3897588

MariaDB: una cuenta anónima podía cortar conexiones ajenas con KILL solo con presentar el nombre de la víctima

En MariaDB, la autorización de KILL comparaba el nombre de usuario que envía el cliente, no la cuenta autenticada. Una sesión anónima podía así terminar conexiones y consultas de otro usuario.

Resumen
Resumen en castellano de un reporte público, no una traducción literal. El código y los comandos se mantienen como en el original.

Resumen

MariaDB guarda dos identidades en el contexto de seguridad de cada conexión: user, que es el nombre de usuario tal y como lo envía el cliente, y priv_user, que es la cuenta de la tabla de privilegios con la que realmente se autenticó. Normalmente coinciden, pero no cuando el servidor recurre a una cuenta anónima (''@host): en ese caso user conserva el nombre que puso el cliente y priv_user queda vacío.

El fallo está en la comprobación de permisos de KILL. Sin el privilegio de administrar conexiones de otros, un usuario solo puede matar sus propias conexiones, y para decidir si una conexión es "suya" kill_one_thread() llama a Security_context::user_matches(). Esa función compara el campo user, es decir, el nombre que el cliente ha dicho tener, en lugar de priv_user. Resultado: alguien que entra por una cuenta anónima presentándose con el nombre de otro usuario puede terminar las conexiones de ese usuario.

Ubicaciones del código señaladas por el investigador:

sql/sql_parse.cc:9187-9188
sql/sql_class.cc:5091-5095
sql/sql_acl.cc:16259-16283

Para que sea explotable hacen falta dos condiciones:

  • Que exista una cuenta anónima que encaje con la dirección de origen del atacante.
  • Que la víctima use un nombre de usuario que el atacante pueda presentar desde una dirección donde la cuenta real de la víctima no encaja (así el servidor cae en la cuenta anónima en vez de pedir la contraseña de la víctima).

Pasos de reproducción

  1. Crear una cuenta anónima para 127.0.0.1 y una cuenta de víctima limitada a localhost (datos de laboratorio):
CREATE USER ''@'127.0.0.1';
CREATE USER 'killvictim'@'localhost'
  IDENTIFIED BY 'victim-test-only';
FLUSH PRIVILEGES;
  1. Abrir una sesión como killvictim por el socket UNIX (es decir, como localhost) y dejarla ocupada:
SELECT SLEEP(20);
  1. Desde una sesión de administración, apuntar el ID de conexión de esa sesión de la víctima.
  1. Conectarse por TCP (llega como 127.0.0.1) usando el nombre killvictim y sin contraseña. Como killvictim@localhost no encaja con esa dirección, el servidor autentica contra la cuenta anónima ''@'127.0.0.1'. Desde ahí, comprobar la identidad y lanzar el KILL:
SELECT USER(), CURRENT_USER();
KILL CONNECTION <victim_connection_id>;
  1. Resultado observado:
USER()          = [email protected]
CURRENT_USER()  = @127.0.0.1
KILL result     = success
victim result   = connection terminated

USER() muestra el nombre presentado, pero CURRENT_USER() confirma que la cuenta autenticada es la anónima. Aun así el KILL se acepta y el cliente de la víctima, que estaba en el SLEEP, falla porque su conexión se ha cerrado.

Lo esperado era que la sesión anónima recibiese ER_KILL_DENIED_ERROR, ya que no tiene privilegio para administrar conexiones ajenas ni es la dueña autenticada de la sesión de la víctima.

Impacto

Una cuenta anónima puede terminar conexiones o consultas en curso de otros usuarios solo con presentar su nombre de usuario. Es una denegación de servicio entre usuarios: permite interrumpir trabajo en marcha en la base de datos de cualquier cuenta cuyo nombre conozca el atacante, siempre que se den las condiciones descritas.

Remediación

El reporte figura como resuelto, pero no detalla el parche aplicado. La propuesta del investigador era:

  • Basar las comprobaciones de propiedad de la conexión en priv_user (y el contexto de host/cuenta autenticados), no en el nombre enviado por el cliente.
  • Añadir una comprobación explícita que deniegue por defecto cuando la identidad autenticada sea anónima, antes de permitir un KILL sobre conexiones del "mismo" usuario.