
phpMyAdmin 5.2.3 - Élevé - À distance - résiduel - setup/validate toujours accessible
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · phpmyadmin-setup-validate
Classe : SSRF Portée : Distante
phpMyAdmin 5.2.3 (tag RELEASE_5_2_3, commit 962857e). setup/index.php s'arrête lorsque config.inc.php est présent. setup/validate.php ne le fait pas. Il se charge toujours sous $isMinimumCommon, récupère un jeton CSRF depuis la page de connexion publique, et appelle Validator::testDBConnection -> mysqli_connect vers l'hôte, le port, l'utilisateur et le mot de passe du champ JSON values.
Un client non authentifié fait un GET sur / pour récupérer un cookie de session et un jeton, puis POSTe sur setup/validate.php id=Server avec Servers/1/host et Servers/1/port de son choix. L'hôte phpMyAdmin ouvre une poignée de main TCP MySQL vers cette adresse. C'est du SSRF via le protocole MySQL : 3306 interne, credential stuffing contre un serveur interne, un oracle de port à partir du timing et des erreurs mysqli. Ce n'est pas du SSRF de métadonnées HTTP à moins que quelque chose sur ce port ne parle MySQL. hide_db interpole également une regex utilisateur dans preg_match. Je n'ai pas exécuté ce chemin ReDoS dans ce lab.
L'image officielle phpmyadmin:5.2.3 supprime setup/. Ce résidu ne s'applique que lorsque ce répertoire est encore dans la racine web, ce qui est le résidu courant après une installation. Le lab restaure setup/ depuis l'archive 5.2.3 afin que l'épinglage corresponde à RELEASE_5_2_3.
PMASA-2016-44 / CVE-2016-6621 couvrait une partie du SSRF de setup. La vérification « désactiver setup quand la config existe » n'a jamais été appliquée à validate.php / config.php.
| ID | pas encore de CVE |
| Classe | SSRF (protocole MySQL) |
| Portée | Distante (non authentifiée si setup/ est encore accessible via le web) |
| CWE | CWE-918 |
| CVSS | Élevé : 7.2 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N |
| Produit | phpMyAdmin |
| Affecté | 5.2.3 tag RELEASE_5_2_3 (962857e) avec setup/ encore dans la docroot |
| Auth | Non authentifiée. Jeton CSRF depuis la page de connexion publique. Pas de connexion MySQL. |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1:19230 uniquement |
Quiconque peut atteindre une arborescence setup/ résiduelle peut faire ouvrir à l'hôte phpMyAdmin une connexion TCP au protocole MySQL vers un hôte et un port de son choix.
| Privilège | Méthode / chemin | Prouvé en lab ou déduit |
|---|---|---|
| Ouvrir un TCP depuis l'hôte PMA vers hôte:port de l'attaquant (intégrité de la connexion sortante ; reconnaissance de ce port) | POST /setup/validate.php non authentifié id=Server avec Servers/1/host + Servers/1/port plus un jeton depuis GET / | Prouvé en lab. L'oracle sur oracle:13306 a journalisé PMA-SETUP-VALIDATE-WITNESS depuis le conteneur PMA |
| Confirmer que l'interface setup est désactivée tandis que validate reste actif | GET /setup/index.php | Prouvé en lab. Le corps contient « setup is disabled » / configuration already exists |
| Échouer en mode fermé sans le jeton CSRF de la page de connexion | Même POST, sans token | Prouvé en lab. Zéro hit sur l'oracle |
| Oracle de port / d'identifiants à partir des erreurs mysqli et du timing | Même testDBConnection | Déduit. Le corps du lab était mysqli_connect(): (HY000/2006): MySQL server has gone away après que l'oracle a accepté puis fermé |
| Parler MySQL à un 3306 interne et essayer l'utilisateur/mot de passe postés | Même mysqli_connect | Déduit. Non pointé vers le service db du lab |
ReDoS via validateRegex interpolant une regex utilisateur | id=Servers/1/hide_db | Déduit. Non exercé |
Fermé sur ce lab : SSRF de métadonnées HTTP (la poignée de main est MySQL, pas GET /latest/meta-data), connexion PMA non authentifiée, shell OS, et l'assistant setup lui-même (index.php reste désactivé). ÉCHEC si setup/ n'est pas déployé (défaut de l'image officielle).
Huit recherches parallèles dans le code source de RELEASE_5_2_3. setup/index.php a le fatal config-exists. setup/validate.php ne l'a pas. setup/lib/common.inc.php définit $isMinimumCommon et ne cherche pas CONFIG_FILE.
Fausses pistes : traiter l'image officielle comme ce résidu (elle supprime setup/) ; POSTer sans le jeton de la page de connexion (zéro hit sur l'oracle) ; traiter cela comme du SSRF de métadonnées HTTP (la poignée de main est MySQL).
python3 phpmyadmin-setup-validate-Abraxas-Labs.py
ou
cd lab
./run.sh
Loopback uniquement : http://127.0.0.1:19230. Projet Compose phpmyadmin-setup-validate. Le Dockerfile restaure setup/ depuis l'archive 5.2.3 et exécute un oracle TCP sur le port 13306. Témoin PMA-SETUP-VALIDATE-WITNESS.
SUCCESS phpmyadmin-setup-validate how=live tcp=yes setup-index-disabled=yes PMA-SETUP-VALIDATE-WITNESS
Placer le fatal file_exists(CONFIG_FILE) dans setup/lib/common.inc.php afin que chaque script setup s'arrête. Restreindre les hôtes de testDBConnection. Ne pas construire les délimiteurs de preg_match à partir de chaînes utilisateur.
962857e)setup/index.php fatal config-existssetup/validate.php pas cette vérificationlibraries/classes/Config/Validator.php testDBConnection / mysqli_connectGNU Affero GPL v3.0. Voir LICENSE. Lab en loopback uniquement. Aucune garantie.