Skip to main content
Press slash or control plus K to focus the search. Use the arrow keys to navigate results and press enter to open a threat.
Reconnecting to live updates…

BabelDOC: Arbitrary Code Execution via CMap Pickle Deserialization in babeldoc/pdfminer/cmapdb.py (CVE-2026-54071)

0
High
Published: 07/10/2026 (07/10/2026, 19:32:44 UTC)
Source: GCVE Database
Product: BabelDOC

Description

## Arbitrary Code Execution via CMap Pickle Deserialization in babeldoc/pdfminer/cmapdb.py ### Summary BabelDOC's vendored PDF parser (`babeldoc/pdfminer/cmapdb.py`) deserializes untrusted pickle data when loading CMap files. The `_load_data()` method strips only NUL bytes from a PDF-controlled CMap name, then passes it directly to `os.path.join()` and `pickle.loads()`. Because Python's `os.path.join()` discards all preceding path components when it encounters an absolute path segment, an attacker who embeds a hex-encoded absolute path in a crafted PDF's `/Encoding` name (e.g., `/#2Ftmp#2Fattacker#2Fevil`) can redirect deserialization to any attacker-writable `.pickle.gz` file on the local system. Processing such a PDF results in arbitrary Python code execution with the privileges of the BabelDOC process. ### Details The vulnerable function is `CMapDB._load_data()` at `babeldoc/pdfminer/cmapdb.py:232–245`: ```python @classmethod def _load_data(cls, name: str) -> Any: name = name.replace("\0", "") # line 233 — only NUL is stripped filename = "%s.pickle.gz" % name # line 234 — attacker-controlled string ... for directory in cmap_paths: path = os.path.join(directory, filename) # line 241 — no realpath/canonical check if os.path.exists(path): gzfile = gzip.open(path) try: return type(str(name), (), pickle.loads(gzfile.read())) # line 245 — unconditional pickle ``` **Path injection via PDF name hex-encoding.** The PDF specification allows name objects to encode arbitrary bytes as `#xx`. The pdfminer literal-name parser (`psparser._parse_literal_hex`) decodes these sequences before handing the string to higher layers. Consequently, the PDF literal `/#2Ftmp#2Fattacker#2Fevil` is decoded to the Python string `/tmp/attacker/evil`. **Python `os.path.join()` absolute-path override.** When the decoded name starts with `/` (i.e., it is an absolute path), Python's `os.path.join(directory, name + ".pickle.gz")` ignores `directory` entirely and returns the absolute path unchanged. The trusted `cmap_paths` directories (`/usr/share/pdfminer/`, the package's own `cmap/` folder) are therefore completely bypassed. **Data flow from PDF to sink:** 1. `babeldoc/main.py:611–622` — CLI accepts a PDF path; only existence and `.pdf` suffix are checked. 2. `babeldoc/main.py:678–679` — path stored in `TranslationConfig(input_file=file)`. 3. `babeldoc/format/pdf/high_level.py:472–488` — `translation_config.input_file` enters the translate pipeline. 4. `babeldoc/format/pdf/high_level.py:805–848` — PDF saved to `temp_pdf_path` and parsed with `parse_prepared_pdf_with_new_parser_to_legacy_ir`. 5. `babeldoc/format/pdf/new_parser/native_parse.py:60–70` — prepared pages loaded and interpreted. 6. `babeldoc/format/pdf/new_parser/pymupdf_prepared_page_access.py:25–34` — PyMuPDF opens the PDF and builds page resources. 7. `babeldoc/format/pdf/new_parser/prepared_resource_builder.py:84–94` — font resources converted to `PreparedFontSpec`. 8. `babeldoc/format/pdf/new_parser/active_font_resource_runtime.py:21–35` — page resource bundle resolves root font map. 9. `babeldoc/format/pdf/new_parser/active_font_runtime.py:79–87` — each font spec projected and passed to `font_factory.create_font`. 10. `babeldoc/format/pdf/new_parser/active_direct_font_backend.py:291–292, 491–493` — CID fonts call `build_cid_cmap(spec, literal_name=literal_name)`. 11. `babeldoc/format/pdf/new_parser/runtime/cid_cmap_runtime.py:52–77` — PDF-controlled `/Encoding`/`CMapName` normalized and passed to `CMapDB.get_cmap`. `_normalize_cmap_name()` removes only a single leading `/`; all other path characters pass through. 12. `babeldoc/pdfminer/cmapdb.py:233–245` — **sink**: NUL-stripped name used verbatim to construct the path; file opened with gzip and deserialized with `pickle.loads()`. **Sanitization gaps:** - `name.replace("\0", "")` removes only the NUL byte; `..`, `/`, `\`, and hex-decoded path separators are unaffected. - There is no `os.path.realpath()`, `os.path.abspath()`, or `os.path.commonpath()` containment check before the file is opened. - There is no allowlist of known CMap names nor any integrity verification of the pickle data. **Recommended patch** (`babeldoc/pdfminer/cmapdb.py`): ```diff --- a/babeldoc/pdfminer/cmapdb.py +++ b/babeldoc/pdfminer/cmapdb.py @@ 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) + base_dir = os.path.realpath(directory) + path = os.path.realpath(os.path.join(base_dir, filename)) + try: + if os.path.commonpath([base_dir, path]) != base_dir: + continue + except ValueError: + continue if os.path.exists(path): gzfil

CVSS v3.1

Score 7.8high

Attack Vector
Local
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Affected software

PyPIghsa
BabelDOC
Affected versions
<0.6.3

Run on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.

AI-Powered Analysis

Machine-generated threat intelligence

AILast updated: 07/11/2026, 09:49:47 UTC

Technical Analysis

The vulnerability exists in BabelDOC's vendored PDF parser (babeldoc/pdfminer/cmapdb.py) in the _load_data() method, which deserializes pickle data from CMap files. The method strips only NUL bytes from a PDF-controlled CMap name and constructs a filename that is joined with trusted directories using os.path.join(). However, if the CMap name is an absolute path (achieved via PDF hex-encoding), os.path.join() discards the trusted directory and uses the absolute path directly. This allows an attacker to redirect deserialization to any attacker-writable .pickle.gz file on the local system. Since pickle.loads() is called unconditionally on this file, arbitrary Python code execution can occur. There is no path containment check or allowlist of CMap names, and no integrity verification of the pickle data. The vulnerability is identified as CWE-502 (Deserialization of Untrusted Data).

Potential Impact

Successful exploitation allows an attacker to execute arbitrary Python code with the privileges of the BabelDOC process by controlling the deserialized pickle data. This can lead to full compromise of the host system or further attacks depending on the environment and privileges of BabelDOC.

Mitigation Recommendations

Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. The recommended patch involves validating that the resolved file path is contained within trusted directories by using os.path.realpath() and os.path.commonpath() before deserializing pickle data. Until a patch is applied, avoid processing untrusted PDFs with BabelDOC or run BabelDOC with minimal privileges and in isolated environments to limit impact.

Pro Console: star threats, build custom feeds, automate alerts via Slack, email & webhooks.Upgrade to Pro

Technical Details

Gcve Source
db.gcve.eu
Osv Id
GHSA-m8gf-v64p-gfmg
Osv Schema Version
1.4.0
Aliases
["CVE-2026-54071"]
Ecosystems
["PyPI"]
Database Specific Severity
HIGH
Cvss Version
3.1

Threat ID: 6a520eb368715ace438f526a

Added to database: 07/11/2026, 09:36:51 UTC

Last enriched: 07/11/2026, 09:49:47 UTC

Last updated: 07/31/2026, 12:27:30 UTC

Views: 47

Community Reviews

0 reviews

Crowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.

Sort by
Loading community insights…

Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.

Actions

PRO

Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.

Please log in to the Console to use AI analysis features.

Need more coverage?

Upgrade to Pro Console for AI refresh and higher limits.

For incident response and remediation, OffSeq services can help resolve threats faster.

Latest Threats

Breach by OffSeqOFFSEQFRIENDS — 25% OFF

Check if your credentials are on the dark web

Instant breach scanning across billions of leaked records. Free tier available.

Scan now
OffSeq TrainingCredly Certified

Lead Pen Test Professional

Technical5-day eLearningPECB Accredited
View courses