
phpMyAdmin 5.2.3 - Élevée - À distance - résiduel - vérification isSuperUser ignorée
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · phpmyadmin-issuperuser
Write-up : https://abraxaslabs.tech/research/phpmyadmin-issuperuser
Classe : Résidu de privilège Portée : Distante
phpMyAdmin 5.2.3 (RELEASE_5_2_3 / 962857e) - phpMyAdmin
DatabaseInterface::isSuperUser() n'est pas une vérification SUPER / SYSTEM_USER. C'est SELECT 1 FROM mysql.user LIMIT 1. Tout compte capable de lire mysql.user est mis en cache comme superutilisateur pour le reste de la session. $cfg['AllowUserDropDatabase'] vaut false par défaut. DestroyController, SqlController, ImportController et la page des bases de données empruntent tous le raccourci isSuperUser en contournant ce drapeau.
Un contournement de politique phpMyAdmin, pas un nouveau privilège MySQL. SELECT sur mysql.user plus DROP, sans SUPER, suffit pour supprimer via PMA.
| ID | pas encore de CVE |
| Classe | Résidu de privilège |
| Portée | Distante (connexion authentifiée par cookie en tant que compte MySQL) |
| CWE | CWE-863 |
| CVSS | Moyen : 6.3 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L |
| Produit | phpMyAdmin |
| Affecté | 5.2.3 tag RELEASE_5_2_3 (962857e4f63d42e38f11ff4d63f5e722018add76) |
| Auth | connexion authentifiée par cookie en tant que compte MySQL |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
Un compte MySQL connecté capable de lire mysql.user (typiquement un SELECT de sauvegarde/surveillance) plus DROP, sans SUPER, est traité comme un superutilisateur phpMyAdmin.
| Privilège | Méthode / sink | Reconnaissance / intégrité / disponibilité | Prouvé en lab ou déduit |
|---|---|---|---|
Contourner $cfg['AllowUserDropDatabase']=false et voir Drop sur la page des bases de données | GET index.php?route=/server/databases | Reconnaissance de l'interface Drop | Prouvé en lab. bulkActionDropButton / selected_dbs[] présents pour selectuser ; absents pour plaindrop |
| Supprimer une base de données via le gestionnaire destroy de PMA | POST index.php?route=/server/databases/destroy selected_dbs[]=dropme ajax_request=true | Intégrité / disponibilité de cette base de données | Prouvé en lab. JSON success:true, « 1 database has been dropped successfully. » dropme supprimée |
Exécuter DROP DATABASE depuis la zone SQL | POST index.php?route=/sql sql_query=DROP DATABASE \dropme_sql`` | Intégrité / disponibilité de cette base de données | Prouvé en lab. Base de données supprimée. Le compte négatif reçoit « DROP DATABASE statements are disabled. » |
Déverrouiller d'autres écrans PMA conditionnés par isSuperUser() | Privilèges, réplication, statut, groupes d'utilisateurs, moteurs, binlog, variables | Reconnaissance / intégrité de ces écrans | Déduit des sites d'appel. Non exercé |
Lire mysql.user | SELECT MySQL que PMA utilise comme oracle SUPER | Reconnaissance du catalogue que PMA confond avec SUPER | Prouvé en lab. C'est ce grant que le résidu confond avec SUPER |
Fermé sur ce lab : vrai SUPER MySQL (SHOW GRANTS reste USAGE + DROP de table + SELECT sur mysql.user). FILE, GRANT OPTION, shutdown et ALL PRIVILEGES ne sont pas accordés. Un compte avec DROP mais sans SELECT sur mysql.user ne peut toujours pas supprimer via PMA. Ce n'est pas un nouveau privilège MySQL ni une RCE.
Même pin : Résidu WebAuthn CustomServer.
J'ai épinglé phpMyAdmin RELEASE_5_2_3. Les contrôles de privilèges qui prétendent être SUPER devraient ressembler à SUPER. Celui-ci ressemble à un SELECT de catalogue :
$result = $this->tryQuery('SELECT 1 FROM mysql.user LIMIT 1');
$isSuperUser = false;
if ($result) {
$isSuperUser = (bool) $result->numRows();
}
SessionCache::set('is_superuser', $isSuperUser);
Les points de contrôle qui utilisent ce drapeau autour de AllowUserDropDatabase (par défaut false) :
Controllers/Server/DatabasesController.php is_drop_allowedControllers/Server/Databases/DestroyController.php (destruction AJAX)Controllers/Sql/SqlController.php / Sql::hasNoRightsToDropDatabaseControllers/Import/ImportController.phpControllers/Database/OperationsController.phpFausses pistes déjà consignées : prétendre à un véritable SUPER MySQL ; considérer un compte limité à DROP comme le bug (ce compte est le contrôle négatif) ; gonfler cela en RCE ; un reverse shell. Du théâtre.
python3 phpmyadmin-issuperuser-Abraxas-Labs.py
# or: cd lab && ./run.sh
Cibler uniquement http://127.0.0.1:19270. Images phpmyadmin:5.2.3 et mysql:8.0. Projet Compose phpmyadmin-issuperuser. $cfg['AllowUserDropDatabase']=false. La stack est démontée après l'exécution.
selectuser dispose de SELECT sur mysql.user ainsi que de DROP sur dropme / dropme_sql, sans SUPER. plaindrop dispose uniquement de DROP sur nodropme. La page Databases affiche Drop pour selectuser. POST /server/databases/destroy supprime dropme. POST /sql avec DROP DATABASE dropme_sql s'exécute également. plaindrop a toujours reçu le JSON d'erreur de destroy et « DROP DATABASE statements are disabled » de /sql.
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
Testez réellement SUPER / SYSTEM_USER via INFORMATION_SCHEMA ou SHOW GRANTS. Conservez un indicateur distinct « peut lire mysql.user » pour l'interface qui a réellement besoin de ce catalogue. Ne mettez pas en cache un SELECT de catalogue en tant que superutilisateur.
962857e4f63d42e38f11ff4d63f5e722018add76)libraries/classes/DatabaseInterface.php isSuperUser()GNU Affero GPL v3.0. Voir LICENSE. Laboratoire en boucle locale uniquement. Aucune garantie.