// 3 ZERO-DAY · 8 CVE · 7 EXPLOIT IN THE LAST 24H→
A critical argument-injection flaw in Pillow, the most popular Python imaging library with nearly 436 million monthly PyPI downloads, allowed remote code execution via attacker-controlled filenames passed to Ghostscript. The vulnerability persisted for roughly twelve years and is fixed in version 13.0.0, released October 16, 2026, with a CVSS 4.0 score of 9.2.

Pillow, the Python imaging library with nearly 436 million monthly downloads from PyPI, released version 13.0.0 on October 16, 2026, fixing a critical vulnerability that persisted for roughly twelve years. The issue lies not in the EPS format or its PostScript content, but in how Pillow passes the filename to Ghostscript: a user-controlled filename is inserted as a -f argument without normalization, allowing arbitrary command injection. The Common Vulnerabilities and Exposures identifier CVE-2026-112900, assigned by GitHub, records a CVSS 4.0 score of 9.2, rated CRITICAL.

Key Takeaways
  • The vulnerability resides exclusively in the filename passed to Image.open(), not in the PostScript content of the EPS: a completely benign file becomes an attack vector if the filename is manipulated.
  • Ghostscript interprets -, --, and @ prefixes as options or references to external argument files, allowing configuration override and code execution even with the -dSAFER sandbox active.
  • The exposure window spans Pillow from version 2.6.0 (October 2014) through 12.3.0 inclusive; the fix in 13.0.0 normalizes the path via os.path.abspath(), prepending the directory separator.
  • Upgrading to Pillow 13.0.0 requires Python 3.11 or later, dropping support for Python 3.10 and creating friction for legacy enterprise environments.

The Mechanism: When the Argument Parser Becomes the Weapon

Source-code analysis in EpsImagePlugin.py reveals the exact vulnerability chain. Pillow extracts fp.name from the file object and inserts it into a command list passed to subprocess.check_call(): the '-f' element precedes the filename with no intermediate validation. This pattern, classified as CWE-88 (Argument Injection), turns into remote execution because Ghostscript does not treat the value following -f as an opaque string but subjects it to its own argument parser.

Ghostscript's behavior is documented and consistent: a filename starting with - or -- is read as an option; one starting with @ is read as a reference to a file containing additional arguments. The proof-of-concept published in the GitHub advisory GHSA-w889-685w-c8fh demonstrates execution of the id command via a filename @arguments.txt pointing to an external file with manipulated Ghostscript parameters. The test reproduces on Ghostscript 10.02.1 and 10.07.1.

"The flaw is triggered by the filename, not by anything in the PostScript document. A completely benign EPS file still results in Ghostscript argument injection whenever the filename passed to Image.open() is attacker-influenced."
— GitHub Security Advisory GHSA-w889-685w-c8fh

The Sandbox Failure: -dSAFER Was Not Enough

An element that makes this vulnerability particularly insidious is the neutralization of the -dSAFER sandbox, active by default in Pillow invocations. This Ghostscript option restricts dangerous PostScript operations — arbitrary filesystem access, shell command execution from the language — but operates downstream of command-line argument parsing. The malicious filename does not bypass -dSAFER through EPS content; it precedes it, manipulating the very command line that spawns the process.

Pillow's official documentation on the threat model explicitly acknowledges this gap: the subprocess invocation with user-controlled arguments constitutes an attack surface distinct from PostScript language parsing. The GitHub advisory emphasizes that the responsibility for sanitization falls on Pillow as the command-line builder, not on downstream callers.

The Filename Trap: When Content Checks Fail

The filename-based vector evades conventional defenses based on content and MIME type. A web service that verifies magic numbers, EPS headers, or extension/content matching remains vulnerable if the uploaded filename — often derived from user input or generated with an attacker-controlled prefix — traverses the filesystem path to Image.open(). Exploit conditions are threefold: Ghostscript installed on the system, processing of EPS files via filesystem path (not an already-open file object), and attacker influence over the filename.

The source pk-sharma.com calculates a twelve-year exposure window from the 2.6.0 release, with the fix commit identified as a2c363141da31a265dd536c945677db4b64f3ec9. The change is minimalist: a single line replaces infile = fp.name with infile = os.path.abspath(fp.name). The os.path.abspath() function ensures the path begins with the operating system's directory separator, making it impossible for a - or @ prefix to reach the Ghostscript parser.

What to Do Now

For system administrators and application maintainers processing image uploads via Pillow, the priority actions are as follows. Verify whether the application opens EPS files with Image.open() passing a filesystem path; if the filename derives from the user, exposure is confirmed.

Upgrade to Pillow 13.0.0 or later, with the awareness that this requires Python 3.11 or newer; environments pinned to Python 3.10 must evaluate runtime compatibility. Verify the presence of Ghostscript in the operating system or deployment containers, since the vulnerability triggers only if the gs binary is available and invocable by Pillow.

Absent an immediate upgrade, assess whether the application actually processes EPS formats: most thumbnail and conversion pipelines limit themselves to JPEG, PNG, and WebP, formats that do not invoke the EpsImagePlugin path.

No in-the-wild exploitation has emerged at the time of publication, according to The Hacker News and pk-sharma.com. This grace period does not eliminate the urgency of remediation: the simplicity of the vector and the availability of proof-of-concept code significantly lower the barrier to adoption by malicious actors.

Why This Vulnerability Recalibrates the Threat Model

The CVE-2026-112900 case inverts the conventional logic of file security: the content is harmless, the container — understood as the filename — is the danger. This pattern takes a precise technical form here because the name crosses a boundary between application and external tool without adequate delimitation. The structural lesson extends beyond Pillow: every interface that passes user data to external processes via command-line arguments must consider the recipient's parsing, not just its own intended semantics.

The fix with os.path.abspath() is technically elegant but also telling: the solution does not require a new sandboxing architecture, only the recognition that a path and an opaque string are different entities when they enter an external parser. Twelve years of exposure suggest this distinction was not evident to maintainers, despite Ghostscript documenting the behavior of special prefixes for a long time.

FAQ

My application only converts JPEG/PNG, is it vulnerable?

No. The vulnerability is confined to the EPS processing path in EpsImagePlugin.py. If the application does not call Image.open() on EPS files or does not have Ghostscript installed, the vector does not trigger.

Why didn't -dSAFER protect?

-dSAFER sandboxes PostScript execution, not command-line argument parsing. The malicious filename acts before the EPS content is evaluated, manipulating the very construction of the Ghostscript process.

Can I mitigate without upgrading Pillow?

Available sources do not indicate documented alternative mitigations. Upgrading to Pillow 13.0.0 remains the confirmed intervention for this vulnerability.

Sources


Sources and references
  1. thehackernews.com
  2. pk-sharma.com
  3. cve.org
  4. pillow.readthedocs.io
  5. github.com