A critical remote code execution vulnerability chain in MariaDB Server enabled authenticated low-privilege database users to seize complete operating system control as the mariadbd process user over standard TCP port 3306. Documented in coordinated public disclosure via HackerOne (Report #3856148) by security researcher rdjgr, the exploit elegantly chains two independent memory safety flaws: an out-of-bounds heap disclosure oracle in the OpenGIS ST_Area function to defeat Address Space Layout Randomization (ASLR), and a heap use-after-free in the Oracle-compatibility SYS_REFCURSOR cursor-array engine to hijack the instruction pointer.
| Specification Parameter | Vulnerability Detail & Telemetry | Technical Risk & Exploit Impact |
|---|---|---|
| Target Software | MariaDB Server 13.0.2 HEAD (Commit 84c246ca) / Release 13.0.1-rc |
Core relational database engine deployed across enterprise and cloud database platforms. |
| Exploit Primitives | CWE-416 (Use After Free) & CWE-125 (Out-of-Bounds Read) | Chained memory disclosure oracle + virtual method table (vtable) hijack. |
| Required Privileges | Minimal low-privilege account (SELECT, INSERT, UPDATE, CREATE TEMPORARY TABLES, EXECUTE on one schema) |
No FILE, no SUPER, no ADMIN, and no CREATE ROUTINE permissions required. |
| Exploitation Impact | Critical (CVSS 8.5) — CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
Arbitrary host OS command execution under mariadbd service account. |
| Reporter Attribution | Security Researcher rdjgr |
Demonstrated under AddressSanitizer and against official MariaDB production container images. |
The Minimal-Privilege Threat Model
In multi-tenant software-as-a-service (SaaS) and managed cloud environments, database operators routinely grant isolated tenant accounts access to a single designated schema. Cloud providers rely on the assumption that a tenant confined to basic data manipulation language (DML) operations (SELECT, INSERT, UPDATE, DELETE, and temporary table creation) cannot breach database process isolation, read host configuration files, or compromise co-tenant data.
This exploit shattered that security boundary entirely. Operating purely through standard SQL queries over a standard connection, the exploit requires no special administrative privileges, no access to external files, and no dangerous native UDF creation rights.
Bug 1: Out-of-Bounds Heap Read in ST_Area (ASLR/PIE Defeat Oracle)
Modern Linux servers enforce Address Space Layout Randomization (ASLR) and compile daemons with Position Independent Executable (PIE) flags. To construct a reliable control-flow hijack, an adversary must first locate the base memory address of the mariadbd binary and the process heap.
The researcher discovered an information disclosure primitive inside the OpenGIS spatial engine (sql/spatial.cc). When evaluating Gis_multi_polygon::area (invoked via the standard SQL function ST_Area()), the engine parses a binary MULTIPOLYGON geometry blob. A missing bounds check allowed a crafted geometry payload to declare more polygon rings than actually existed in the buffer:
// sql/spatial.cc: Gis_multi_polygon::area bounds omission:
bool Gis_multi_polygon::area(double *ar, const char **data, uint length) const {
uint32 num_polygons = uint4korr(*data);
*data += 4;
// Missing check: does (*data + num_polygons * sizeof(Polygon)) exceed length?
for (uint i = 0; i < num_polygons; i++) {
// Reads memory past the allocated geometry buffer into IEEE 754 floating point!
}
}
By supplying a truncated geometry stream, the parser reads contiguous heap bytes directly beyond the geometry allocation, treating arbitrary heap pointers as coordinates and folding them into the double-precision float returned by ST_Area(). By issuing automated SQL queries that extract the IEEE 754 mantissa and exponent bits, the attacker derived an in-process memory disclosure oracle, leaking both heap addresses and the mariadbd code base to calculate ROP gadgets and bypass ASLR with 100% reliability.
Bug 2: Use-After-Free in the SYS_REFCURSOR Cursor Array
With memory addresses disclosed, the second stage of the chain targets MariaDB's PL/SQL Oracle compatibility mode (sql_mode=ORACLE). In this mode, stored procedures can declare dynamic cursor references using SYS_REFCURSOR.
Inside the server thread context, open cursors are managed by thd->statement_cursors (an instance of sp_cursor_array defined in sql/sp_cursor.h:220). This structure inherits from Dynamic_array<sp_cursor_array_element> with prealloc=0, storing elements inline in an expandable heap buffer. When the array grows beyond its current capacity (which increments in steps of 16), mysys/array.c:alloc_dynamic() invokes my_realloc(), allocating a new memory region and freeing the old buffer.
The flaw resides in object reference synchronization: when a cursor is opened via Materialized_cursor, the server assigns a raw pointer (select_result *result) pointing directly into the internal buffer of the cursor array. When subsequent cursor allocations trigger array reallocation, the backing buffer is relocated and freed, but the existing Materialized_cursor retains its raw pointer into the stale, deallocated buffer:
INITIAL ALLOCATION:
Dynamic_array Buffer: [ Slot 0 | Slot 1 | ... | Slot 15 ]
Cursor Instance retains: raw pointer -> Slot 0
ARRAY EXPANSION (Slot 16 allocated):
my_realloc() moves buffer: [ Slot 0 ... Slot 31 ] (New Heap Address)
Old Buffer FREED back to glibc arena!
Cursor Instance STILL retains: raw pointer -> Old Freed Buffer! (DANGLING POINTER)
Chaining the Exploits: From Heap Grooming to Shell Execution
When the attacker subsequently executes a FETCH against the cursor, MariaDB dereferences the dangling pointer to invoke a virtual method on the select_result object. By preceding the FETCH with prepared statement allocations and temporary table creations, the attacker grooms the glibc heap, reclaiming the deallocated chunk and placing a fake C++ virtual method table (vtable) at the exact address.
Because Bug 1 already leaked the base address of mariadbd and libc, the fake vtable points its method dispatch pointer directly to a ROP chain or system() call, executing arbitrary shell commands under the mariadbd daemon's Linux user context:
-- Abbreviated Exploit Chain Conceptual Flow:
SET sql_mode=ORACLE;
-- Phase 1: Leak code & heap addresses via ST_Area OOB read
SELECT ST_Area(ST_PolyFromWKB(0x0106000000...)) INTO @leaked_float;
-- Math extraction yields mariadbd_base and heap_base
-- Phase 2: Allocate 16 cursors to fill initial sp_cursor_array capacity
CALL setup_cursor_slots();
-- Phase 3: Trigger reallocation while holding raw cursor handle
CALL expand_cursor_slots(); -- my_realloc frees original buffer
-- Phase 4: Heap spray via temporary table to occupy freed slot with fake vtable
CREATE TEMPORARY TABLE groom (payload VARBINARY(4096));
INSERT INTO groom VALUES (CONCAT(@fake_vtable, @rop_gadgets, 'touch /tmp/pwned_mariadb; id > /tmp/rce_out'));
-- Phase 5: Trigger virtual method dereference
FETCH stale_cursor INTO @sink; -- HIJACKS INSTRUCTION POINTER!
The researcher confirmed the end-to-end exploit against the official Docker release mariadb:13.0.1-rc (glibc 2.43), verifying arbitrary file creation in /tmp/pwned_mariadb without restarting or crashing the server.
Memory Corruption Primitives Breakdown
| Exploit Stage | Vulnerable Routine | Memory Corruption Mechanism | Security Consequence |
|---|---|---|---|
| Stage 1: Memory Leak | sql/spatial.cc: Gis_multi_polygon::area |
Out-of-bounds heap read past truncated geometry coordinates. | Defeats ASLR and PIE by leaking mariadbd text base and heap pointers. |
| Stage 2: Use-After-Free | sql/sp_cursor.cc: sp_cursor_array::append |
Buffer relocation via my_realloc() leaving dangling select_result pointers. |
Creates a controlled dangling pointer to a freed heap chunk. |
| Stage 3: Heap Reclaim | sql/sql_tmp_table.cc & prepared statements |
Deterministic heap grooming matching the freed sp_cursor chunk size. |
Replaces freed object with attacker-controlled vtable pointers. |
| Stage 4: Execution | FETCH statement execution |
Virtual method call dispatch through corrupted vtable. | Instruction pointer hijacked; executes arbitrary shell commands as mariadbd. |
Remediation & Upstream Patch Architecture
MariaDB maintainers resolved both vulnerabilities across upstream branches:
- Geometry Bounds Validation:
sql/spatial.ccnow strictly verifies that the coordinate stream length matches the declared polygon count before processing vertices. - Cursor Pointer Modernization:
sp_cursor_arraywas refactored to store cursor elements via stable pointer references or non-relocating vector containers, ensuring reallocation does not invalidate active cursor handle pointers.
Database administrators must upgrade to MariaDB 13.0.2 or deploy vendor backports immediately. In multi-tenant environments, run database instances inside unprivileged, rootless container namespaces with SELinux or AppArmor profiles blocking outbound TCP sockets and shell execution.



