
phpMyAdmin 5.2.3 - Élevé - À distance - SQLi - ImportLdi TERMINATED BY résiduel
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · phpmyadmin-importldi
Write-up : https://abraxaslabs.tech/research/phpmyadmin-importldi
Classe : SQLi Portée : Distante
phpMyAdmin 5.2.3 (RELEASE_5_2_3 / 962857e) - phpMyAdmin
ImportLdi.php construit LOAD DATA avec FIELDS/LINES TERMINATED BY à partir des POST ldi_terminated et ldi_new_line. Ces deux chaînes sont concaténées dans du SQL entre guillemets simples. ldi_enclosed et ldi_escaped passent par escapeString. phpMyAdmin active PMA_ENABLE_LDI sur /import, ce qui active MYSQLI_OPT_LOCAL_INFILE.
Un utilisateur authentifié envoie en POST format=ldi avec un guillemet dans ldi_terminated. Le LOAD DATA émis contient le POST brut à l'intérieur de TERMINATED BY. Injection SQL dans la connexion de l'utilisateur, pas dans celle du control user.
| ID | pas encore de CVE |
| Classe | SQLi |
| Portée | Distante (connexion authentifiée par cookie ; import de table, format ldi) |
| CWE | CWE-89 |
| CVSS | Moyenne : 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Produit | phpMyAdmin |
| Affecté | 5.2.3 tag RELEASE_5_2_3 (962857e4f63d42e38f11ff4d63f5e722018add76) |
| Auth | connexion authentifiée par cookie. Import de table, format ldi |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
Un utilisateur phpMyAdmin authentifié capable d'ouvrir l'import de table peut injecter du SQL dans LOAD DATA ... TERMINATED BY sur sa connexion MySQL.
| Privilège | Méthode / sink | Reconnaissance / intégrité / disponibilité | Prouvé en lab ou déduit |
|---|---|---|---|
Casser le littéral FIELDS TERMINATED BY | POST index.php?route=/import format=ldi ldi_terminated avec un guillemet | Intégrité de l'instruction construite par PMA | Prouvé en lab. Boîte SQL HTML : TERMINATED BY '',PMA-IMPORTLDI-WITNESS' |
Garder ENCLOSED BY dans la bande | Même POST, guillemet dans ldi_enclosed | Contrôle négatif | Prouvé en lab. ENCLOSED BY est échappé |
| SQL supplémentaire sur la connexion utilisateur (INTO OUTFILE / empilé si autorisé) | Même sink une fois la chaîne fermée | Intégrité de la connexion utilisateur | Déduit. Le lab s'arrête au délimiteur cassé. Par défaut, mysqli::query est mono-instruction |
| Lecture/écriture de fichiers côté MySQL | Même LOAD DATA si le compte dispose de FILE ou de LOCAL INFILE vers le serveur web | Intégrité des fichiers accessibles à mysqld | Déduit. Non accordé ici. Non revendiqué |
Écarté sur ce lab : accès non authentifié, control user / pmadb, LFI via include du système d'exploitation, et lecture/écriture conditionnée par FILE. L'utilisateur authentifié dispose déjà d'une boîte SQL ; ce reliquat est le délimiteur non échappé à l'intérieur d'une instruction que PMA construit pour lui.
Même version épinglée : reliquat WebAuthn CustomServer.
J'ai épinglé phpMyAdmin RELEASE_5_2_3. Les plugins d'import qui construisent LOAD DATA à la main constituent un audit de quoting. Deux des quatre champs de délimiteur étaient échappés. Deux ne l'étaient pas :
$sql .= ' FIELDS TERMINATED BY \'' . $ldi_terminated . '\'';
...
$sql .= ' LINES TERMINATED BY \'' . $ldi_new_line . '\'';
ldi_enclosed / ldi_escaped appellent $dbi->escapeString. Common.php définit PMA_ENABLE_LDI sur /import + format=ldi. Routes GET|POST /import et GET|POST /table/import.
Fausses pistes déjà consignées : revendiquer une LFI sans privilège FILE ; considérer la zone SQL comme la découverte (le résidu est le délimiteur non échappé que PMA concatène) ; revendiquer control-user / pmadb ; un reverse shell. Du théâtre.
python3 phpmyadmin-importldi-Abraxas-Labs.py
# or: cd lab && ./run.sh
Cibler uniquement http://127.0.0.1:19290. Images phpmyadmin:5.2.3 et mysql:8.0. Projet Compose phpmyadmin-importldi. La stack est démontée après l'exécution.
Connexion par cookie. POST /import avec format=ldi et un guillemet dans ldi_terminated. A émis LOAD DATA LOCAL INFILE ... INTO TABLE \ldi_target` FIELDS TERMINATED BY '',PMA-IMPORTLDI-WITNESS'. Le même guillemet dans ldi_enclosedest stocké comme unENCLOSED BYéchappé. Le code source épinglé sur l'image 5.2.3 contient toujours'.$ldi_terminated.'et$dbi->escapeString($ldi_enclosed)`.
SUCCESS phpmyadmin-importldi pin=RELEASE_5_2_3 how=live TERMINATED-BY-raw ENCLOSED-escaped PMA-IMPORTLDI-WITNESS
escapeString sur ldi_terminated et ldi_new_line, de la même manière que pour enclosed et escaped. Cast en int de skip_queries.
962857e4f63d42e38f11ff4d63f5e722018add76)libraries/classes/Plugins/Import/ImportLdi.phpGNU Affero GPL v3.0. Voir LICENSE. Laboratoire en boucle locale uniquement. Aucune garantie.