Vulnerability Chain: Path Traversal → Arbitrary File Write → Code Execution
Introduction
Compiled HTML Help (CHM) files are Microsoft's legacy help file format. Despite being decades old, they remain widely used — and widely converted. Calibre, the most popular ebook management application with millions of users, supports CHM as an input format, converting it to EPUB, MOBI, PDF, and other formats.
While auditing Calibre's format parsers, I found a path traversal vulnerability in the CHM reader that allows a malicious help file to write arbitrary files to the victim's system. On Windows, this is a direct path to code execution.
This is a separate finding from the EPUB path traversal vulnerability I disclosed previously. Both affect Calibre 9.1.0, but target different parsing components with different exploitation strategies.
The Discovery
CHM files are essentially archives containing HTML pages, images, and other resources. When Calibre converts a CHM file, it extracts these resources to a temporary directory before processing them. The extraction logic lives in src/calibre/ebooks/chm/reader.py.
The vulnerability is straightforward: Calibre strips only the leading slash from internal CHM paths, but never sanitizes .. directory traversal sequences. An attacker can craft a CHM file with internal paths that escape the extraction directory and write to arbitrary locations on disk.
Technical Deep Dive
The First Bug: Insufficient Sanitization (Line 351)
When building the list of files inside the CHM archive, Calibre runs:
paths.append(path.lstrip('/'))
This strips the leading / from each path — but that's it. Path components like .. pass through completely untouched.
The Second Bug: Uncontained File Write (Lines 184-198)
During extraction, each path from the CHM is joined directly with the output directory and written to disk:
for path in self.Contents():
fpath = path # ATTACKER CONTROLLED
lpath = os.path.join(output_dir, fpath) # PATH TRAVERSAL
self._ensure_dir(lpath) # Creates dirs OUTSIDE output_dir
data = self.GetFile(path)
# ...
with open(lpath, 'wb') as f:
f.write(data) # ARBITRARY FILE WRITE
The critical issues:
fpathis taken directly from the CHM archive without sanitizationos.path.join(output_dir, fpath)with..sequences escapes the extraction directory_ensure_dir()helpfully creates any intermediate directories needed along the traversal pathopen(lpath, 'wb')writes attacker-controlled data to the escaped path
Unlike the EPUB vulnerability (which corrupts existing files via XOR), this CHM bug gives full arbitrary file write — the attacker controls both the path and the entire file content.
Exploitation: Arbitrary File Write to RCE on Windows
Target: The Windows Startup Folder
On Windows, any executable or script placed in the user's Startup folder runs automatically on login:
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\
The challenge is reaching this path from the temporary extraction directory using .. traversal. The Calibre temp directory sits several levels deep, so we need enough ..\ components to escape up to the %APPDATA% root.
The 8.3 Short Name Trick
The "Start Menu" directory contains a space, which can cause issues in path handling. Windows NTFS supports 8.3 short filenames — an alias system from the DOS era. The short name for "Start Menu" is STARTM~1. Using this short name avoids any space-related parsing issues:
/..\\..\\..\\..\\Roaming\\Microsoft\\Windows\\STARTM~1\\Programs\\Startup\\rce.bat
After lstrip('/') removes the leading slash, and os.path.join() resolves the .. sequences, this path escapes the temp directory and lands directly in the Startup folder.
The Payload
The CHM file embeds a batch script as the file content:
@echo off
calc.exe
This is a benign proof-of-concept payload. A real attacker could drop a reverse shell, ransomware, or any other executable.
The Attack in Action
Step 1: Attacker Creates Malicious CHM
The attacker generates a CHM file containing the traversal path and payload. The file looks and behaves like a normal help file — Calibre doesn't validate internal paths before extraction.
Step 2: Victim Converts the CHM
The victim imports the CHM into Calibre and converts it to another format (GUI: "Convert books" or CLI: ebook-convert):
$ ebook-convert poc.chm output.epub
Step 3: Payload Drops to Startup
During conversion, Calibre extracts the CHM contents. The traversal path escapes the temp directory and rce.bat is written to the Startup folder:
> dir "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup"
rce.bat 78 bytes
Step 4: Code Execution on Next Login
On the next Windows login (or reboot), rce.bat executes automatically. In the PoC, calc.exe launches — confirming arbitrary code execution.
Proof of Concept
The video above demonstrates the full attack chain: crafting the malicious CHM, converting it in Calibre, and calc.exe launching on the next login.
Impact Analysis
Why This Is Worse Than the EPUB Bug
| Factor | EPUB Vulnerability | CHM Vulnerability |
|---|---|---|
| Write primitive | XOR corruption of first 1040 bytes | Full arbitrary file write (any content, any size) |
| Prerequisite | UUID brute-force (~minutes) | None — direct exploitation |
| Target constraint | File must exist with predictable content | No constraints — creates new files |
| RCE path | Shell init script corruption (Linux) | Startup folder drop (Windows) |
Direct Impacts
- Arbitrary File Write: Attacker writes any file anywhere the user has permissions
- Code Execution: Via Windows Startup folder, cron jobs, shell profiles, or any auto-executed path
- Persistence: The dropped payload survives reboots and runs on every login
- Stealth: The conversion appears to work normally — no errors or warnings
Platform Testing
| Platform | Interface | Status |
|---|---|---|
| Windows | GUI (Convert books) | Vulnerable |
| Windows | CLI (ebook-convert) | Vulnerable |
| Linux | CLI (ebook-convert) | Vulnerable |
Root Cause Analysis
- Insufficient Sanitization:
lstrip('/')only removes leading slashes —..sequences are preserved entirely - No Path Containment: Calibre never verifies that resolved paths stay within the extraction directory
- Helpful Directory Creation:
_ensure_dir()creates directories along the traversal path, ensuring the write succeeds - Trust in Untrusted Data: Internal CHM paths are treated as safe without validation
Timeline
| Date | Event |
|---|---|
| 2025-02-01 | Vulnerability discovered |
| 2025-02-01 | RCE escalation confirmed on Windows |
| 2026-02-06 | Vendor patch release |
| 2026-02-06 | Public disclosure |
Conclusion
This vulnerability is a textbook case of path traversal leading to code execution. Unlike the EPUB vulnerability that required cryptographic brute-forcing to weaponize a limited XOR corruption primitive, the CHM bug provides unrestricted arbitrary file write — making exploitation trivial. No brute-forcing, no constraints on content, no requirement that the target file already exists.
On Windows, the Startup folder provides a clean and reliable path to code execution. On Linux, the same traversal could target ~/.bashrc, cron directories, or SSH authorized keys. The vulnerability affects both the GUI and CLI on all platforms.
Given that CHM files are commonly shared and converted, and that Calibre is the de facto standard for ebook management, users should update immediately.