
phpMyAdmin 5.2.3 - Alta - Remoto - remanescente - setup/validate ainda acessível
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · phpmyadmin-setup-validate
Write-up: https://abraxaslabs.tech/research/phpmyadmin-setup-validate
Classe: SSRF Alcance: Remoto
phpMyAdmin 5.2.3 (tag RELEASE_5_2_3, commit 962857e). setup/index.php encerra quando config.inc.php está presente. setup/validate.php não. Ele ainda carrega sob $isMinimumCommon, obtém um token CSRF da página de login pública e chama Validator::testDBConnection -> mysqli_connect para o host, porta, usuário e senha no campo JSON values.
Um cliente não autenticado faz GET em / para obter um cookie de sessão e um token, depois faz POST em setup/validate.php id=Server com Servers/1/host e Servers/1/port à sua escolha. O host do phpMyAdmin abre um handshake TCP MySQL para esse endereço. Isso é SSRF sobre o protocolo MySQL: 3306 interno, credential stuffing contra um servidor interno, um oráculo de portas a partir de timing e erros do mysqli. Não é SSRF de metadados HTTP a menos que algo naquela porta fale MySQL. hide_db também interpola uma regex do usuário em preg_match. Não executei esse caminho de ReDoS neste laboratório.
A imagem oficial phpmyadmin:5.2.3 remove setup/. Esse resquício só se aplica quando esse diretório ainda está na raiz web, o que é o resquício comum após uma instalação. O laboratório restaura setup/ a partir do tarball 5.2.3 para que o pin corresponda a RELEASE_5_2_3.
PMASA-2016-44 / CVE-2016-6621 cobria parte do SSRF de setup. A verificação "desabilitar setup quando a config existe" nunca foi aplicada a validate.php / config.php.
| ID | sem CVE ainda |
| Classe | SSRF (protocolo MySQL) |
| Alcance | Remoto (não autenticado se setup/ ainda estiver acessível pela web) |
| CWE | CWE-918 |
| CVSS | Alto: 7.2 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N |
| Produto | phpMyAdmin |
| Afetado | 5.2.3 tag RELEASE_5_2_3 (962857e) com setup/ ainda no docroot |
| Autenticação | Não autenticado. Token CSRF da página de login pública. Sem login MySQL. |
| Licença | GNU Affero GPL v3.0 |
| Laboratório | apenas 127.0.0.1:19230 |
Qualquer um que consiga alcançar uma árvore setup/ remanescente pode fazer o host do phpMyAdmin abrir uma conexão TCP de protocolo MySQL para um host e porta à sua escolha.
| Privilégio | Método / caminho | Comprovado em laboratório ou inferido |
|---|---|---|
| Abrir TCP do host PMA para host:porta do atacante (integridade da conexão de saída; reconhecimento dessa porta) | POST /setup/validate.php não autenticado id=Server com Servers/1/host + Servers/1/port mais um token de GET / | Comprovado em laboratório. O oráculo em oracle:13306 registrou PMA-SETUP-VALIDATE-WITNESS a partir do contêiner PMA |
| Confirmar que a UI de setup está desativada enquanto validate permanece ativo | GET /setup/index.php | Comprovado em laboratório. O corpo contém "setup is disabled" / configuração já existe |
| Falhar de forma segura sem o token CSRF da página de login | Mesmo POST, sem token | Comprovado em laboratório. Zero acertos no oráculo |
| Oráculo de porta / credencial a partir de erros do mysqli e timing | Mesmo testDBConnection | Inferido. O corpo do laboratório foi mysqli_connect(): (HY000/2006): MySQL server has gone away após o oráculo aceitar e então fechar |
| Falar MySQL com um 3306 interno e tentar usuário/senha enviados | Mesmo mysqli_connect | Inferido. Não apontado para o serviço db do laboratório |
ReDoS via validateRegex interpolando regex do usuário | id=Servers/1/hide_db | Inferido. Não exercitado |
Fechado neste lab: SSRF de metadados HTTP (o handshake é MySQL, não GET /latest/meta-data), login PMA não autenticado, shell do SO e o próprio assistente de configuração (index.php permanece desativado). FAIL se setup/ não estiver implantado (padrão da imagem oficial).
Oito buscas paralelas de fontes em RELEASE_5_2_3. setup/index.php tem o erro fatal de configuração existente. setup/validate.php não tem. setup/lib/common.inc.php define $isMinimumCommon e não procura por CONFIG_FILE.
Caminhos errados: tratar a imagem oficial como este resquício (ela exclui setup/); fazer POST sem o token da página de login (zero acertos no oráculo); tratar isto como SSRF de metadados HTTP (o handshake é MySQL).
python3 phpmyadmin-setup-validate-Abraxas-Labs.py
ou
cd lab
./run.sh
Apenas loopback: http://127.0.0.1:19230. Projeto Compose phpmyadmin-setup-validate. O Dockerfile restaura setup/ a partir do tarball 5.2.3 e executa um oráculo TCP na porta 13306. Testemunha PMA-SETUP-VALIDATE-WITNESS.
SUCCESS phpmyadmin-setup-validate how=live tcp=yes setup-index-disabled=yes PMA-SETUP-VALIDATE-WITNESS
Coloque o erro fatal file_exists(CONFIG_FILE) em setup/lib/common.inc.php para que todos os scripts de setup terminem. Restrinja os hosts de testDBConnection. Não construa delimitadores de preg_match a partir de strings do usuário.
962857e)setup/index.php erro fatal de config-existssetup/validate.php sem essa verificaçãolibraries/classes/Config/Validator.php testDBConnection / mysqli_connectGNU Affero GPL v3.0. Consulte LICENSE. Apenas laboratório em loopback. Sem garantias.