
Pillow — the Python image library installed on nearly every Python server — patched two critical remote code execution vulnerabilities in version 13.0.0, released October 16. Both carry a CVSS score of 9.2. The first turns a filename into a shell command. The second turns any FPX file into a heap overflow. If you haven’t upgraded, this needs to move up your queue today.
CVE-2026-112900: The Filename That Becomes a Command
This one is subtle enough to miss in code review. When Pillow processes an EPS image, it invokes Ghostscript and passes the input filename with a -f flag. The vulnerable code did this without normalizing the path first:
command = ["gs", "-dNOSAFER", ..., "-f", fp.name]
If fp.name starts with @, Ghostscript reads that as an argument file — loading and executing commands from whatever file that path points to. A filename like @/tmp/attacker.txt is all it takes. The EPS file itself doesn’t need to be malicious. A completely benign EPS processed with an attacker-controlled filename triggers it.
The common vector is upload endpoints that preserve the user’s original filename:
# Dangerous if attacker controls uploaded.name
img = Image.open(uploaded.name)
Django’s request.FILES['image'].name is the classic example. The fix in 13.0.0 is a one-liner: os.path.abspath(fp.name) before Ghostscript is called. An absolute path cannot start with @ or -.
CVE-2026-112398: Open an FPX File, Crash the Heap
The second critical CVE is in Pillow’s FlashPix (FPX) JPEG tile decoder. The decoder allocates a 2-bytes-per-pixel buffer but writes 4-component JPEG scanlines into it when the JPEG mode is unrecognized. That’s a heap buffer overflow leading to arbitrary code execution.
What makes this worse: FPX is recognized by magic bytes (OLE2 header), not file extension. You can rename a malicious FPX to .jpg and Pillow will still open it as FlashPix if the olefile optional dependency is installed. Any service that accepts image uploads without strict format allowlisting is potentially reachable with no user interaction beyond uploading the file.
The Python 3.10 Problem
Here is the part that makes this messy for teams in a hurry: Pillow 13.0.0 dropped Python 3.10. Python 3.10 itself reached end of life on October 4. If you’re still running 3.10, you cannot install Pillow 13.0.0, which means you’re stuck on 12.3.0 — the version with both CVEs.
The upgrade path is therefore two steps: bump the Python runtime to 3.11 or later, then upgrade Pillow. For teams with locked runtime environments, that’s a real constraint. The workarounds below buy time while you schedule the migration.
What to Do Right Now
If you’re on Python 3.11+, upgrade immediately:
pip install --upgrade "pillow>=13.0.0"
If you’re stuck on Python 3.10 or cannot upgrade Pillow yet, apply both mitigations at application startup:
from PIL import EpsImagePlugin
EpsImagePlugin.gs_binary = False # Disables CVE-2026-112900 attack surface
pip uninstall olefile # Removes CVE-2026-112398 attack surface
Disabling Ghostscript kills EPS rendering entirely — if your app doesn’t process EPS files, you won’t notice. Removing olefile kills FPX support, which is vanishingly rare in production applications. To check your current installed version:
python -c "import PIL; print(PIL.__version__)"
This Pattern Has a Name
CVE-2026-112900 rhymes with ImageTragick. In 2016, ImageMagick allowed arbitrary code execution via Ghostscript for the same reason: user-supplied input fed into a shell invocation without sanitization. LibreOffice had a similar EPS/Ghostscript RCE in 2018. The lesson is consistently ignored: any library that hands user data to Ghostscript is a shell injection waiting to be found. Pillow’s fix is correct and minimal. The question is whether other libraries have the same pattern sitting unreviewed.
Pillow 13.0.0 fixed 38 security issues in total. Two are critical. Upgrade now if you can. Mitigate if you can’t.

