
phpMyAdmin 5.2.3 - High - Remote - SQLi - ImportLdi TERMINATED BY leftover
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · phpmyadmin-importldi
Class: SQLi Reach: Remote
phpMyAdmin 5.2.3 (RELEASE_5_2_3 / 962857e) - phpMyAdmin
ImportLdi.php builds LOAD DATA with FIELDS/LINES TERMINATED BY from POST ldi_terminated and ldi_new_line. Those two strings are concatenated into single-quoted SQL. ldi_enclosed and ldi_escaped go through escapeString. phpMyAdmin sets PMA_ENABLE_LDI on /import so MYSQLI_OPT_LOCAL_INFILE is on.
A signed-in user POSTs format=ldi with a quote in ldi_terminated. The issued LOAD DATA contains the raw POST inside TERMINATED BY. SQL injection into the user connection, not the control user.
| ID | no CVE yet |
| Class | SQLi |
| Reach | Remote (authenticated cookie login; table import, format ldi) |
| CWE | CWE-89 |
| CVSS | Medium: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Product | phpMyAdmin |
| Affected | 5.2.3 tag RELEASE_5_2_3 (962857e4f63d42e38f11ff4d63f5e722018add76) |
| Auth | authenticated cookie login. Table import, format ldi |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
A signed-in phpMyAdmin user who can open Table import can inject SQL into LOAD DATA ... TERMINATED BY on their MySQL connection.
| Privilege | Method / sink | Recon / integrity / availability | Lab-proven or inferred |
|---|---|---|---|
Break the FIELDS TERMINATED BY literal | POST index.php?route=/import format=ldi ldi_terminated with a quote | Integrity of the statement PMA builds | Lab-proven. HTML SQL box: TERMINATED BY '',PMA-IMPORTLDI-WITNESS' |
Keep ENCLOSED BY in-band | Same POST, quote in ldi_enclosed | Negative control | Lab-proven. ENCLOSED BY is escaped |
| Extra SQL on the user connection (INTO OUTFILE / stacked if allowed) | Same sink once the string is closed | Integrity of the user connection | Inferred. Lab stops at the broken delimiter. Default mysqli::query is single-statement |
| MySQL-side file read/write | Same LOAD DATA if the account has FILE or LOCAL INFILE to the web server | Integrity of files mysqld can touch | Inferred. Not granted here. Not claimed |
Closed on this lab: unauthenticated access, control-user / pmadb, OS include LFI, and FILE-gated read/write. The signed-in user already has a SQL box; this leftover is the unescaped delimiter inside a statement PMA builds for them.
Same pin: WebAuthn CustomServer leftover.
I pinned phpMyAdmin RELEASE_5_2_3. Import plugins that build LOAD DATA by hand are a quoting audit. Two of four delimiter fields were escaped. Two were not:
$sql .= ' FIELDS TERMINATED BY \'' . $ldi_terminated . '\'';
...
$sql .= ' LINES TERMINATED BY \'' . $ldi_new_line . '\'';
ldi_enclosed / ldi_escaped call $dbi->escapeString. Common.php sets PMA_ENABLE_LDI on /import + format=ldi. Routes GET|POST /import and GET|POST /table/import.
Wrong turns already recorded: claiming LFI without a FILE grant; treating the SQL box as the finding (the leftover is the unescaped delimiter PMA concatenates); claiming control-user / pmadb; a reverse shell. Theatre.
python3 phpmyadmin-importldi-Abraxas-Labs.py
# or: cd lab && ./run.sh
Target only http://127.0.0.1:19290. Images phpmyadmin:5.2.3 and mysql:8.0. Compose project phpmyadmin-importldi. Stack tears down after the run.
Cookie login. POST /import with format=ldi and a quote in ldi_terminated. Issued LOAD DATA LOCAL INFILE ... INTO TABLE \ldi_target` FIELDS TERMINATED BY '',PMA-IMPORTLDI-WITNESS'. The same quote in ldi_enclosedstored as an escapedENCLOSED BY. Pin source on the 5.2.3 image still has '.$ldi_terminated.'and$dbi->escapeString($ldi_enclosed)`.
SUCCESS phpmyadmin-importldi pin=RELEASE_5_2_3 how=live TERMINATED-BY-raw ENCLOSED-escaped PMA-IMPORTLDI-WITNESS
escapeString on ldi_terminated and ldi_new_line, the same as enclosed and escaped. Int-cast skip_queries.
962857e4f63d42e38f11ff4d63f5e722018add76)libraries/classes/Plugins/Import/ImportLdi.phpGNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.