Sploitus

Exploit for Heap-based Buffer Overflow in Postgresql

githubexploit Β· 2026-08-10

Exploit Code

README166 lines
## 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/)