
phpMyAdmin 5.2.3 - High - Remote - 2FA - config/set clears two-factor
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · phpmyadmin-config-set-2fa
Class: Privilege leftover / missing step-up Reach: Remote
phpMyAdmin 5.2.3 (RELEASE_5_2_3 / 962857e) - phpMyAdmin
POST index.php?route=/config/set writes any allowlisted preference, including 2fa. UserPreferences::persistOption does no validation. Settings -> Two-factor still calls TwoFactor::check(true) before it will drop the factor. /config/set does not.
A logged-in session, or XSS/CSRF that can POST the page token, can send key=2fa and value=null. The next password login has no TOTP, WebAuthn, or Simple prompt. Durable strip, not a one-request skip.
| ID | no CVE yet |
| Class | Privilege leftover / missing step-up |
| Reach | Remote (authenticated cookie login; CSRF token required) |
| CWE | CWE-620 |
| CVSS | High: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Product | phpMyAdmin |
| Affected | 5.2.3 tag RELEASE_5_2_3 (962857e4f63d42e38f11ff4d63f5e722018add76) |
| Auth | cookie login. CSRF token required. Completes XSS or CSRF to a durable 2FA strip |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
Strip 2FA from a phpMyAdmin account without presenting TOTP, WebAuthn, or the Simple confirm field. The next password login is password-only. Full PMA as that MySQL user (SQL, users, export).
| Privilege | Method / path | Recon / integrity / availability | Lab-proven or inferred |
|---|---|---|---|
Persist 2fa to null and drop the second factor from pmadb | Authenticated POST index.php?route=/config/set key=2fa value=null plus a valid token | Integrity of the preference store | Lab-proven. success:true, pmadb config_data became [] |
| Log in with password only after that write | New cookie session, same MySQL user | Integrity of the login path | Lab-proven. No 2fa_confirm, landed in the normal UI |
| Keep the current session while 2FA disappears for later logins | Same handler; persistOption writes the store, not the live two_factor_check flag | Integrity of later sessions | Lab-proven. Session A stayed in. Session B had seen 2FA. Session C did not |
| Chain from stored XSS or CSRF that can POST with the page token | Same-review leftovers, not this lab | Integrity of 2FA after a same-origin POST | Inferred |
| Remove 2FA through Settings | POST /preferences/two-factor 2fa_remove=1 | Integrity of 2FA via the documented UI | Lab-proven closed. Without the factor, PMA shows the confirm page and pmadb still has backend=simple |
Closed on this leftover: unauthenticated access, skipping the MySQL password, OS RCE, reading other users' pmadb rows. I did not need leftover #1 (CustomServer signature). I did not skip the MySQL password.
Same pin: WebAuthn CustomServer leftover.
I pinned 5.2.3 after PMASA-2025-1/2/3. Cookie auth, pmadb pma__userconfig. The lab turns on $cfg['DBG']['simple2fa'] only so Settings can enroll the Simple backend without a hardware key. TOTP and WebAuthn live in the same 2fa preference key.
SetConfigController takes key and JSON value and calls Config::setUserValue. UserPreferences::persistOption is documented "No validation is done!" apply() allowlists 2fa so the key is not dropped as an unknown pref.
$result = $this->config->setUserValue(null, $key, json_decode($value));
json_decode('null') is PHP null. persistOption('2fa', null, $default) then unsets config_data['2fa'] because the default for that path is also null.
Settings -> Two-factor is the contrast. TwoFactorController will not configure('') until TwoFactor::check(true) returns true.
Wrong turns already recorded: treating Settings 2fa_remove as the hole (it still demands the factor); claiming unauthenticated access; claiming a skip of the MySQL password; treating this as leftover #1 (CustomServer); a reverse shell. Theatre.
python3 phpmyadmin-config-set-2fa-Abraxas-Labs.py
# or: cd lab && ./run.sh
Target only http://127.0.0.1:19280. Compose project phpmyadmin-config-set-2fa. Pin RELEASE_5_2_3. run.sh copies or clones that tag into lab/src/ for the image. Stack tears down after the run.
Settings enrolled Simple 2FA. A second browser hit the 2fa_confirm page. POST 2fa_remove without the factor stayed on the confirm UI and left backend=simple in pmadb. POST /config/set then returned success:true, pmadb became [], and the next login landed in the normal UI.
SUCCESS phpmyadmin-config-set-2fa enroll=simple ui-remove-blocked=yes config-set-cleared=yes next-login-password-only=yes PMA-CONFIG-SET-2FA-WITNESS
Deny 2fa (and other auth keys) on /config/set. Require TwoFactor::check(true) before any 2FA mutation. Keep the Settings path as the only writer.
962857e4f63d42e38f11ff4d63f5e722018add76)libraries/classes/Controllers/Config/SetConfigController.phplibraries/classes/Config/UserPreferences.phpGNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.