REPRO-2026-00330: Verified Reproduction
REPRO-2026-00330: MariaDB 13.0.1-rc RCE chain: F-09 GRANT PROXY priv-esc MDEV-40470 + /proc/self/maps ASLR leak + F-05 SYS REFCURSOR heap UAF → JOP to system as uid 999 mysql , pure SQL from a low-priv account
REPRO-2026-00330 is verified against mariadb/server · github. Affected versions: F-09 affects every released MariaDB version (confirmed 13.0.1 through 10.6.27). F-05 unfixed at HEAD as of 2026-08-03. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00330.
What Is REPRO-2026-00330?
REPRO-2026-00330 is a critical-severity RCE vulnerability affecting mariadb/server F-09 affects every released MariaDB version (confirmed 13.0.1 through 10.6.27). F-05 unfixed at HEAD as of 2026-08-03.. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00330).
REPRO-2026-00330 Severity
REPRO-2026-00330 is rated critical severity.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
Affected mariadb/server Versions
mariadb/server · github versions F-09 affects every released MariaDB version (confirmed 13.0.1 through 10.6.27). F-05 unfixed at HEAD as of 2026-08-03. are affected.
How to Reproduce REPRO-2026-00330
pruva-verify REPRO-2026-00330 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00330/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for REPRO-2026-00330
- reached the target end-to-end
- full exploit chain demonstrated
- crash observed
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
pure SQL statements from a USAGE-only account over TCP/3306: GRANT PROXY ... IDENTIFIED VIA '', LOAD DATA INFILE '/proc/self/maps', 128MiB user variable with baked JOP layout, CALL uaf5()
- mariadb client
- TCP/3306
- GRANT PROXY priv-esc (MDEV-40470)
- maps ASLR leak
- F-05 SYS_REFCURSOR UAF (sp_cursor_array::get_cursor_by_ref)
- Materialized_cursor::open() virtual dispatch
- JOP D2
- D1
- system()
How the agent worked
Root Cause and Exploit Chain for REPRO-2026-00330
A USAGE-only MariaDB account with TCP reachability to port 3306 achieves remote
code execution as uid=999(mysql) on the stock, unmodified MariaDB
13.0.1-rc Docker image (mariadb@sha256:ef34af04bda12e6c85395328af78d562176c34fb29ae52063a4eb0d68fa7b3e9)
using nothing but SQL statements. The chain combines: (F-09) a GRANT PROXY ... IDENTIFIED VIA '' privilege escalation that hijacks the root account with an
empty password (MDEV-40470); a server-side LOAD DATA INFILE '/proc/self/maps'
read that discloses the live PIE and libc base addresses (ASLR defeat); a
128 MiB user-variable buffer whose address is discovered by diffing
/proc/self/maps from SQL; and (F-05) a SYS_REFCURSOR use-after-free in
sp_cursor_array::get_cursor_by_ref() whose freed 1792-byte array chunk is
reclaimed by an exact-fit heap spray, redirecting a virtual dispatch into a
two-gadget JOP chain (D2 → D1) that calls libc system() with an
attacker-chosen command string.
- Package/component:
mariadb/server— server core (sql/sql_acl.ccGRANT PROXY handling;sql/sp_cursor.{cc,h}cursor array). - Affected versions: reproduced on 13.0.1-MariaDB-ubu2604 (pinned image).
Per the advisory, F-09's fix (commit
dbd60d0ad8d) exists only on dev branches and is absent from every released version 13.0.1 → 10.6.27; F-05 is unfixed upstream (no commits tosql/sp_cursor.{cc,h}since the 13.0.1 tag). - Risk level: Critical. Any authenticated low-privilege database user gains
full OS command execution as the
mysqlservice account (uid 999), i.e. complete database-server compromise, remotely, over the normal SQL protocol.
Impact Parity
- Disclosed/claimed maximum impact: code execution as uid 999 (mysql) from a USAGE-only account via pure SQL over TCP/3306.
- Reproduced impact from this run: identical — attacker-chosen shell
commands executed as
uid=999(mysql)on the stock pinned image in 6/6 fresh processes (5 scripted + 1 manual calibration run), each with fresh ASLR. - Parity:
full.
Root Cause
F-09 — GRANT PROXY privilege escalation (MDEV-40470).
GRANT PROXY ON CURRENT_USER() TO 'root'@'%' IDENTIFIED VIA '';passes an empty authentication clause.LEX_USER::has_auth()returns false, so the privilege check incheck_alter_user()is skipped, whilereplace_user_table()still applies the (empty) password — replacing root's credentials with an empty password. One statement, any authenticated user. Fix: commitdbd60d0ad8d(dev branches only; no released version has it). Runtime proof: after the statement, login asrootwith--skip-passwordsucceeds (CURRENT_USER()=root@%), and the pre-escalation negative control showsERROR 1045onLOAD DATA INFILEandERROR 1227onSET GLOBAL.ASLR defeat. With the FILE privilege obtained in step 1 and
secure_file_privunset (stock),LOAD DATA INFILE '/proc/self/maps'loads mariadbd's own memory map into a table; plain SELECTs return the PIE base (firstr--p 00000000mapping of/usr/sbin/mariadbd) and the libc base. Fresh values every process (observed 0x63abb3250000, 0x57b3cea2c000, 0x55b611d36000, 0x76955fffe000-area, 0x731cf3fff000-area).F-05 — SYS_REFCURSOR use-after-free (unfixed 0day).
sp_cursor_array::get_cursor_by_ref()returns an interior pointer into aDynamic_array. When a cursor'sopen()executes SQL that opens more cursors (grow5()opens 16 cursors, thenOPEN p FOR SELECT spray128()), the array grows,my_reallocfrees the old 1792-byte storage (16×112 B), and the caller's cached pointer dangles. A heap spray of 128 session user variables of exactly 1784 bytes (SET @e3sNNN=@e3pad, glibc exact-fit for the 1792-byte chunk) reclaims the freed storage with attacker bytes, placing a controlled pointerVat offset 0x20 (theresultmember ofsp_cursor).Materialized_cursor::open()then dispatches virtually:mov rax,[result]; call [rax+0x20].JOP to
system(). The fake vtableVlives in a 128 MiB user-variable buffer whose address is learned via the maps diff (glibc gives such large allocations a dedicated mmap; the slot is reused across free+realloc, so the self-referential pointer baked by SQL stays valid; buffer data begins at region+0x30 — verified this run via gdb on the live process). Layout:V+0x20=D2,V+0xa0=system,V+0xa8=V+0x140(cmd ptr),V+0x100=D1,V+0x140="sh -c '<cmd>'". Dispatch:call [rax+0x20]→ D2 (call *0x100(%rax), PIE+0x80da77) → D1 (mov rdi,[rax+0xa8]; call [rax+0xa0], PIE+0xe3075b) →system()(libc+0x5c560). All three gadget offsets were re-verified withobjdumpagainst the binaries extracted from the pinned image in this run (logs/repro/gadget_check.log).
Reproduction Steps
bundle/repro/reproduction_steps.sh(run twice consecutively — both runs exited 0).The script: installs
mariadb-client/binutilsif missing; pulls the pinned image; verifies the D2/D1/system gadget offsets in the extracted binaries; then performs two independent attempts, each against a freshly created container (fresh datadir, root passwordlabpass, USAGE +appdb.*-onlylowprivaccount, fresh ASLR). Each attempt runsbundle/repro/exploit_mdev40470.py, a pure-SQL driver (mariadb client over TCP/3306) that executes the whole chain and finally reads the marker fileid > /tmp/mdev40470_pwned_attemptNthe payload wrote inside the container.Expected evidence: per attempt —
F-09 done - root login with EMPTY password, leaked PIE/libc bases, stable 128 MiB slot, session death onCALL uaf5(), and marker contentuid=999(mysql) gid=999(mysql) groups=999(mysql); script exits 0 only when both attempts confirm.Note on the exploit driver: it is the published PoC (github.com/dinosn/mariadb-13-rce-lab @ 6ac868e1) with one robustness fix in the
/proc/self/mapsregion discovery — on this kernel the fresh 128 MiB mmap is sometimes VMA-merged with an adjacent pre-existing anonymous region (observed merged sizes 0x8022000 and 0x10002000), which the stock exact-size filter missed. The patched logic tracks all anonymous rw-p regions and handles both new-region and grew-region cases. Gadget offsets, sizes, DATA_OFF (0x30) and the whole chain are unchanged.
Evidence
bundle/logs/reproduction_steps.log— full scripted run (exit 0, 2/2).bundle/logs/repro/version_check.log—13.0.1-MariaDB-ubu2604.bundle/logs/repro/gadget_check.log— objdump verification of D2/D1/system.bundle/logs/repro/vuln_attempt_1.log,vuln_attempt_2.log— per-attempt chain transcripts (distinct ASLR bases).bundle/logs/repro/marker_attempt_1.log,marker_attempt_2.log—uid=999(mysql) gid=999(mysql) groups=999(mysql).bundle/logs/repro/vuln_attempt_token.log+marker_attempt_token.log— additional run writing the unique tokenMDEV40470_CHAIN_EXEC_1785762660.bundle/logs/repro/negative_control_lowpriv.log+bundle/repro/negative_control_1.json— identical procedure without F-09:ERROR 1045(FILE),ERROR 1227(SUPER), no marker, no crash; the escalation is the linchpin.bundle/repro/runtime_manifest.json— tcp_peer entrypoint, service/health/ target-path flags, pinned-image target identity.- Environment: Docker 27.5.1 daemon, host kernel 6.8, container glibc 2.43;
ASLR enabled (
/proc/sys/kernel/randomize_va_spacedefault); PIE binary.
Recommendations / Next Steps
- F-09 (MDEV-40470): backport commit
dbd60d0ad8dto all maintained release branches; reject emptyIDENTIFIED VIA ''authentication clauses in GRANT PROXY / treat them as authenticated changes requiringcheck_alter_user()privileges. - F-05: fix
sp_cursor_array::get_cursor_by_ref()callers to re-validate the cursor reference afteropen()(or pin the array storage / use index-based lookup) so growth during a nested open cannot dangle the cached pointer. - Defense in depth: set
secure_file_privto a dedicated directory in the stock image; considerlocal_infile=0and restricting FILE privilege. - Until patched releases exist, any authenticated account must be treated as equivalent to full OS code execution as the mysql user.
Additional Notes
- Idempotency:
reproduction_steps.shwas run twice consecutively; both runs passed (4/4 scripted attempts + 1 manual + 1 token run = 6/6 total). Each attempt recreates the container from the pinned image, so no state carries over. - No fixed negative control exists: the F-09 fix is not in any released version and F-05 is unfixed upstream, so no released image can serve as a fixed build. The in-band negative control (same procedure without the F-09 escalation) demonstrates the privilege gate; the gadget-offset verification binds the result to the exact pinned binaries.
- Kernel-dependent VMA merging was the only environmental deviation from
the published PoC (its exact-size maps filter). The patched discovery was
verified against gdb ground truth (marker bytes
ABCDfound at region+0x30, confirmingDATA_OFF=0x30; chunk header0x8000ff2IS_MMAPPED at region+8). - The server process crashes after
system()returns (mariadbd is PID 1, so the container exits); marker files persist in the container layer and are read afterdocker start. This post-exploit housekeeping is the only non-SQL step and is not part of the exploitation.
REPRO-2026-00330 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
python3 -c "
import json
d=json.load(open('bundle/learning/exploit_knowledge_preflight.json'))
for r in d['results']:
rec=r['record']
print(rec['id'], '|', rec.get('state'), '|', rec.get('record_type') or rec['body'].get('record_type'), '|', rec.get('title'))
"EXPLOIT-KNOWLEDGE PREFLIGHT REVIEW REQUIRED: `run_shell` is blocked until this agent calls `review_exploit_knowledge_preflight` with snapshot_sha256 `dc98b2e663eaa7e653978c790c2f557b7c2584b2bd260f1bc21100b764b28ca0` and exactly one disposition for each candidate ID [0533adf6-0e1f-44ca-afb8-6cb2faafab11, 1491a4cf-2d16-4a81-98dd-3d114a69bb9c, 1755d479-acfb-4e6b-9199-5abf23eb5fbc, 5dab43ae-009f-4c25-a4fd-7a7b9a874b38, 6de687bb-a6a7-4522-b134-121b577fdb0c, 7c845cfe-5dab-41d2-9f50-d71160b7cf40, 981e5...
Unknown error
Artifacts and Evidence for REPRO-2026-00330
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix REPRO-2026-00330
FAQ: REPRO-2026-00330
Is REPRO-2026-00330 exploitable?
How severe is REPRO-2026-00330?
What type of vulnerability is REPRO-2026-00330?
Which versions of mariadb/server are affected by REPRO-2026-00330?
How can I reproduce REPRO-2026-00330?
Is the REPRO-2026-00330 reproduction verified?
References for REPRO-2026-00330
Authoritative sources for REPRO-2026-00330 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.