
phpMyAdmin 5.2.3 - Alta - Remoto - SQLi - ImportLdi TERMINATED BY leftover
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · phpmyadmin-importldi
Classe: SQLi Alcance: Remoto
phpMyAdmin 5.2.3 (RELEASE_5_2_3 / 962857e) - phpMyAdmin
ImportLdi.php constrói LOAD DATA com FIELDS/LINES TERMINATED BY a partir dos POST ldi_terminated e ldi_new_line. Essas duas strings são concatenadas em SQL entre aspas simples. ldi_enclosed e ldi_escaped passam por escapeString. O phpMyAdmin define PMA_ENABLE_LDI em /import para que MYSQLI_OPT_LOCAL_INFILE fique ativado.
Um usuário autenticado envia via POST format=ldi com uma aspa em ldi_terminated. O LOAD DATA emitido contém o POST bruto dentro de TERMINATED BY. Injeção de SQL na conexão do usuário, não na conexão de controle.
| ID | ainda sem CVE |
| Classe | SQLi |
| Alcance | Remoto (login autenticado por cookie; importação de tabela, formato ldi) |
| CWE | CWE-89 |
| CVSS | Médio: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Produto | phpMyAdmin |
| Afetado | 5.2.3 tag RELEASE_5_2_3 (962857e4f63d42e38f11ff4d63f5e722018add76) |
| Autenticação | login autenticado por cookie. Importação de tabela, formato ldi |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
Um usuário autenticado no phpMyAdmin que consegue abrir a importação de tabela pode injetar SQL em LOAD DATA ... TERMINATED BY na conexão MySQL dele.
| Privilégio | Método / sink | Reconhecimento / integridade / disponibilidade | Comprovado em lab ou inferido |
|---|---|---|---|
Quebrar o literal FIELDS TERMINATED BY | POST index.php?route=/import format=ldi ldi_terminated com uma aspa | Integridade da instrução que o PMA constrói | Comprovado em lab. Caixa SQL HTML: TERMINATED BY '',PMA-IMPORTLDI-WITNESS' |
Manter ENCLOSED BY in-band | Mesmo POST, aspa em ldi_enclosed | Controle negativo | Comprovado em lab. ENCLOSED BY é escapado |
| SQL extra na conexão do usuário (INTO OUTFILE / empilhado se permitido) | Mesmo sink assim que a string é fechada | Integridade da conexão do usuário | Inferido. O lab para no delimitador quebrado. O padrão mysqli::query é de instrução única |
| Leitura/escrita de arquivos no lado do MySQL | Mesmo LOAD DATA se a conta tiver FILE ou LOCAL INFILE para o servidor web | Integridade dos arquivos que o mysqld pode tocar | Inferido. Não concedido aqui. Não alegado |
Descartado neste lab: acesso não autenticado, usuário de controle / pmadb, LFI via include do SO e leitura/escrita condicionada a FILE. O usuário autenticado já tem uma caixa SQL; esse resquício é o delimitador não escapado dentro de uma instrução que o PMA constrói para ele.
Mesmo pin: WebAuthn CustomServer leftover.
Fixe no phpMyAdmin RELEASE_5_2_3. Plugins de importação que constroem LOAD DATA manualmente são uma auditoria de aspas. Dois dos quatro campos de delimitador eram escapados. Dois não eram:
$sql .= ' FIELDS TERMINATED BY \'' . $ldi_terminated . '\'';
...
$sql .= ' LINES TERMINATED BY \'' . $ldi_new_line . '\'';
ldi_enclosed / ldi_escaped chamam $dbi->escapeString. Common.php define PMA_ENABLE_LDI em /import + format=ldi. Rotas GET|POST /import e GET|POST /table/import.
Caminhos errados já registrados: alegar LFI sem concessão de FILE; tratar a caixa SQL como a descoberta (o resquício é o delimitador não escapado que o PMA concatena); alegar usuário de controle / pmadb; um reverse shell. Teatro.
python3 phpmyadmin-importldi-Abraxas-Labs.py
# or: cd lab && ./run.sh
Alvo apenas http://127.0.0.1:19290. Imagens phpmyadmin:5.2.3 e mysql:8.0. Projeto Compose phpmyadmin-importldi. A stack é desmontada após a execução.
Login por cookie. POST /import com format=ldi e uma aspa em ldi_terminated. Emitido LOAD DATA LOCAL INFILE ... INTO TABLE \ldi_target` FIELDS TERMINATED BY '',PMA-IMPORTLDI-WITNESS'. A mesma aspa em ldi_enclosedarmazenada comoENCLOSED BYescapado. O código-fonte fixado na imagem 5.2.3 ainda tem'.$ldi_terminated.'e$dbi->escapeString($ldi_enclosed)`.
SUCCESS phpmyadmin-importldi pin=RELEASE_5_2_3 how=live TERMINATED-BY-raw ENCLOSED-escaped PMA-IMPORTLDI-WITNESS
escapeString em ldi_terminated e ldi_new_line, da mesma forma que em enclosed e escaped. Cast para int em skip_queries.
962857e4f63d42e38f11ff4d63f5e722018add76)libraries/classes/Plugins/Import/ImportLdi.phpGNU Affero GPL v3.0. Veja LICENSE. Apenas lab em loopback. Sem garantia.