Research/phpmyadmin-issuperuser
0-dayNo CVEMediumPublic

phpMyAdmin 5.2.3, isSuperUser catalog SELECT

phpMyAdmin 5.2.3 isSuperUser() is SELECT 1 FROM mysql.user LIMIT 1, not SUPER or SYSTEM_USER. AllowUserDropDatabase defaults false. An account with SELECT on mysql.user plus DROP, and no SUPER, gets Drop through PMA. Labbed. No CVE yet.

Name
phpMyAdmin 5.2.3, isSuperUser catalog SELECT
Type
0-day analysis
CVE
n/a
CVE Risk
medium
Disclosure Status
public
Vendor
phpMyAdmin
Affected
phpMyAdmin 5.2.3 tag RELEASE_5_2_3 (962857e). Authenticated cookie login. AllowUserDropDatabase default false. Not a new MySQL privilege.
Published
12 Oct 2026
Updated
12 Oct 2026
Tags
0-day, phpmyadmin, privilege-leftover, cwe-863, authenticated

SUPER was a catalog SELECT

I am @abraxas_null. The proof of concept is on GitHub: abraxas/phpmyadmin-issuperuser (loopback client). The lab stack is lab/: Dockerfile, docker-compose.yml, config.user.inc.php, init.sql, run.sh, poc.py. Authorized lab only. It talks to loopback.

This is phpMyAdmin 5.2.3 (962857e4f63d42e38f11ff4d63f5e722018add76). No CVE yet. CWE-863. 6.3 Medium (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L). Remote privilege leftover. Authenticated cookie login. A phpMyAdmin policy bypass, not a new MySQL privilege.

$cfg['AllowUserDropDatabase'] defaults to false. A normal user should not see Drop database and should not run DROP DATABASE from the SQL box. phpMyAdmin still treats SELECT 1 FROM mysql.user LIMIT 1 as superuser and skips that policy. Typical backup/monitor accounts can read mysql.user. Give them DROP on a schema they already own and PMA draws the control.

Why phpMyAdmin

I had already published the CustomServer WebAuthn leftover on this pin. Privilege oracles that talk like SUPER should look like SUPER. phpMyAdmin caches a boolean named is_superuser for the rest of the session. If that boolean is a catalog SELECT, every screen gated on it is a leftover of the name.

I only wanted leftovers that survived 5.2.2. Inflating this to RCE or real MySQL SUPER is how you miss the actual bug. The bar here is PMA policy, not GRANT ALL.

Pinning RELEASE_5_2_3

Latest 5.2.x GA. Tag RELEASE_5_2_3. Commit 962857e4f63d42e38f11ff4d63f5e722018add76. Official image phpmyadmin:5.2.3 plus a one-line config.user.inc.php that keeps AllowUserDropDatabase false. That is the default. The lab just makes it explicit.

The skip list is half the hunt

  • PMASA-2025-1/2/3. Already in 5.2.2.
  • Real MySQL SUPER / SYSTEM_USER is a different grant. This leftover does not mint it.
  • Older PMA privilege bugs were often missing checks on the SQL box. This one has a check. The check asks the wrong question.

Closed on this pin: FILE, GRANT OPTION, shutdown, ALL PRIVILEGES, OS RCE. An account with DROP but no SELECT on mysql.user still cannot drop through PMA. That account is the negative control, not the bug.

Other 5.2.3 leftovers from the same review sit in their own packs. This post is the SUPER oracle.

Surfaces that returned nothing

I looked at isCreateUser, isGrantUser, and the replication / binlog menus first. Several of those gates also consult isSuperUser(). I did not exercise them in this lab. Drop database is the policy that ships off by default and has a clean sibling: an account with DROP only. If that sibling is refused and the catalog-SELECT account is not, the leftover is the oracle, not MySQL.

SHOW GRANTS for selectuser stayed USAGE, table-level DROP, and SELECT on mysql.user. No SUPER. phpMyAdmin still drew Drop.

The leftover

DatabaseInterface.php isSuperUser():

PHP
$result = $this->tryQuery('SELECT 1 FROM mysql.user LIMIT 1');
$isSuperUser = false;
if ($result) {
    $isSuperUser = (bool) $result->numRows();
}
SessionCache::set('is_superuser', $isSuperUser);

Gates that take this flag around AllowUserDropDatabase (default false):

  • Controllers/Server/DatabasesController.php is_drop_allowed
  • Controllers/Server/Databases/DestroyController.php (AJAX destroy)
  • Controllers/Sql/SqlController.php / Sql::hasNoRightsToDropDatabase
  • Controllers/Import/ImportController.php
  • Controllers/Database/OperationsController.php

MySQL still has to permit DROP DATABASE. PMA is what was supposed to refuse it.

Wrong turns in the order they happened

I almost claimed real MySQL SUPER. SHOW GRANTS is the negative. The leftover is the PMA flag.

I treated a DROP-only account as the bug. That account is how you know the policy still works when the oracle is false.

I considered inflating this to RCE because Drop is destructive. It is availability of a schema the account can already DROP in mysql CLI if PMA would get out of the way. The leftover is PMA getting out of the way.

Theatre: a reverse shell.

The lab

phpmyadmin:5.2.3 + mysql:8.0 on loopback. Host bind 127.0.0.1:19270. Compose project phpmyadmin-issuperuser. $cfg['AllowUserDropDatabase']=false.

selectuser has SELECT on mysql.user plus DROP on dropme / dropme_sql, no SUPER. plaindrop has DROP on nodropme only. Databases page draws Drop for selectuser. POST /server/databases/destroy dropped dropme. POST /sql with DROP DATABASE dropme_sql ran too. plaindrop still got the Error JSON from destroy and "DROP DATABASE statements are disabled" from /sql.

Plain text
python3 phpmyadmin-issuperuser-Abraxas-Labs.py
# or: cd lab && ./run.sh

Last line:

Plain text
SUCCESS phpmyadmin-issuperuser drop-ui=yes destroy=ok sql-drop=ok grants-no-super=yes mysql.user-select=yes neg-drop-ui=no neg-destroy=denied PMA-ISSUPERUSER-WITNESS

What an attacker can do

A signed-in MySQL account that can read mysql.user (typical backup/monitor SELECT) plus DROP, without SUPER, is treated as a phpMyAdmin superuser.

Privilege Method / sink Recon / integrity / availability Lab-proven or inferred
Bypass $cfg['AllowUserDropDatabase']=false and see Drop on the databases page GET index.php?route=/server/databases Recon of the Drop UI Lab-proven. bulkActionDropButton / selected_dbs[] present for selectuser; absent for plaindrop
Drop a database through the PMA destroy handler POST index.php?route=/server/databases/destroy selected_dbs[]=dropme ajax_request=true Integrity / availability of that database Lab-proven. JSON success:true. dropme gone
Run DROP DATABASE from the SQL box POST index.php?route=/sql sql_query=DROP DATABASE \dropme_sql`` Integrity / availability of that database Lab-proven. Database gone. Negative account is refused
Unlock other PMA screens gated on isSuperUser() Privileges, replication, status, user groups, engines, binlog, variables Recon / integrity of those screens Inferred from call sites. Not exercised
Read mysql.user MySQL SELECT that PMA uses as the SUPER oracle Recon of the catalog PMA mistakes for SUPER Lab-proven. That grant is what the leftover mistakes for SUPER

Closed on this lab: real MySQL SUPER, FILE, GRANT OPTION, shutdown, ALL PRIVILEGES, RCE. An account with DROP but no SELECT on mysql.user still cannot drop through PMA. This is not a new MySQL privilege.

The fix

Test real SUPER / SYSTEM_USER via INFORMATION_SCHEMA or SHOW GRANTS. Keep a separate "can read mysql.user" flag for UI that actually needs that catalog. Do not cache a catalog SELECT as superuser.

Contact

Abraxas Labs - abraxaslabs.tech - github.com/abraxas - @abraxas_null - abraxas.null@proton.me

phpMyAdmin 5.2.3, isSuperUser catalog SELECT · Abraxas Labs