P.K. SHARMA

Cyber security intelligence, AI governance, practitioner analysis

Python Pillow flaw turns image filename into Ghostscript code execution across 436M monthly downloads

Maintainers of Python's Pillow imaging library have released version 13.0.0 to fix CVE-2026-112900. The defect allows an attacker controlling an image filename to inject arbitrary command arguments into Ghostscript, bypassing default sandbox controls to achieve remote code execution.

By Parminder Kumar Sharma · · 6 min read

Twelve years in code, fixed in one line

For 4,386 days (derived), spanning twelve years and eleven major release trains, the most widely deployed image processing library in the Python ecosystem contained a flaw that turns a file path into operating system commands. Tracked as CVE-2026-112900 and rated CVSSv4.0 9.2 Critical, the vulnerability resided inside PIL.EpsImagePlugin.Ghostscript(), the module responsible for converting Encapsulated PostScript (EPS) vector drawings into raster pixels. In every release from Pillow 2.6.0 in October 2014 through Pillow 12.3.0 in October 2026, the library passed user-controlled filenames directly to Ghostscript without sanitising command-line switches or option delimiters.

That finding establishes an extraordinary blast radius across modern infrastructure: Pillow handles approximately 436 million downloads every month from PyPI, averaging 14.5 million downloads per day and 168 downloads every second (derived). However, that scale does not establish that every Python web service is automatically vulnerable to remote code execution. Exploitation requires Ghostscript to be installed on the host system, the application must process EPS input using file-based paths, and the attacker must be able to control or influence the filename string passed to Image.open(). Furthermore, the GitHub Security Advisory GHSA-w889-685w-c8fh establishes no recorded in-the-wild exploitation prior to the release of version 13.0.0.

What the vulnerability record establishes and what it leaves unstated

To evaluate the real operational risk to enterprise applications, developers and security engineers must distinguish the proven mechanics of CVE-2026-112900 from secondary alarmism.

Audit of verified technical claims in GHSA-w889-685w-c8fh versus unstated deployment assumptions

Technical DimensionExplicitly Established by MaintainersNot Stated or Dependent on Environment
Vulnerable CodePIL.EpsImagePlugin.Ghostscript() passes fp.name directly at -f [infile].Whether in-memory file objects (io.BytesIO) without an fp.name attribute can be abused (they use temporary files and are unaffected).
Injection TokensLeading hyphens (-, --) parsed as options; leading @ parsed as an argument file.The specific Ghostscript switches used in working proof-of-concept exploits (withheld to prevent automated weaponisation).
Sandbox BehaviourGhostscript's default -dSAFER sandbox does not prevent this argument-injection chain.Whether strict OS-level container isolation (such as Docker seccomp or AppArmor profiles) halts the spawned process.
Exploitation StateNo known public exploitation in the wild at the time of publication.Whether threat actors have historically exploited this vector during opportunistic web application scans.
RemediationUpgrading to Pillow 13.0.0 resolves the issue by forcing os.path.abspath().A workaround for legacy systems that cannot upgrade past major version boundaries without breaking API changes.

The friendly-name fallacy: why -dSAFER failed to protect the host

The architectural breakdown in CVE-2026-112900 illustrates the friendly-name fallacy in open-source helper tooling. When Pillow invokes Ghostscript, it includes the parameter -dSAFER in the argument list. For years, software architects and security reviewers assumed that passing -dSAFER provided complete containment, believing the flag guaranteed that Ghostscript could not read unauthorized files or invoke operating system shells.

In reality, -dSAFER is a PostScript interpreter sandbox setting; it does not protect the command-line argument parser itself. When Ghostscript starts, its argument parser evaluates command-line tokens sequentially. Because Pillow placed infile after -f without checking for argument delimiters, an attacker supplying a filename such as @arguments.txt caused Ghostscript to treat the filename as an instruction to open arguments.txt and ingest additional arbitrary command-line options. Through this argument file, an attacker can override settings, redefine device configurations, and execute commands in the context of the running Python application.

Mechanism of CWE-88 argument injection in EpsImagePlugin.py and the 12-year vulnerability timeline.

As demonstrated in the technical diagram, the vulnerability exists entirely because subprocess.check_call was given an array whose elements contained unneutralised command-line prefixes. While using an array prevents traditional shell injection (such as semicolon chaining), it provides zero protection against argument injection when a helper binary interprets leading characters as flags or parameter files.

The one-line remediation in Pillow 13.0.0

The engineering solution implemented by Pillow maintainers in commit a2c363141da31a265dd536c945677db4b64f3ec9 is remarkably compact. Rather than attempting to maintain an exhaustive denylist of dangerous Ghostscript parameters, maintainers modified a single line of logic in src/PIL/EpsImagePlugin.py:

# Before (vulnerable in Pillow 2.6.0 through 12.3.0):
if hasattr(fp, "name") and os.path.exists(fp.name):
    infile = fp.name

# After (fixed in Pillow 13.0.0):
if hasattr(fp, "name") and os.path.exists(fp.name):
    # Make the path absolute so that a filename beginning with "-" or "@"
    # can never be interpreted by Ghostscript's argument parser
    # as an option or an @-file token
    infile = os.path.abspath(fp.name)

By wrapping fp.name in os.path.abspath(), the resulting filename string is guaranteed to begin with a root directory separator (such as / on Linux and macOS, or C:\ on Windows). Because the path never begins with a hyphen or an @ symbol, Ghostscript's argument parser is forced to evaluate the token exclusively as a file path, neutralising the injection vector completely.

UK supply chain governance and defensive guidance

For UK engineering leads, compliance managers, and digital service operators, Pillow CVE-2026-112900 highlights persistent systemic risks in open-source software supply chains. Under the UK National Cyber Security Centre (NCSC) Principles for Supply Chain Security, organisations are expected to understand their third-party dependencies, monitor vulnerability advisories, and maintain automated patching pipelines.

Under Cyber Essentials Requirements for Infrastructure v3.3, all software on in-scope devices and servers must be supported by the vendor and updated within 14 days of a critical vulnerability disclosure where a fix is available. In enterprise Python environments, Pillow is rarely imported directly by end-user scripts; it is pulled in as a transitive dependency by web frameworks, data analysis tools, and computer vision libraries. Failure to inventory virtual environments leaves legacy application servers running unpatched 11.x or 12.x builds indefinitely.

Furthermore, under the UK Data Protection Act 2018 and UK GDPR Article 32, organisations processing user-submitted files (such as avatar uploads, passport photos, and scanned identity documents) must implement state-of-the-art technical security controls. Running untrusted image decoders with elevated host privileges violates the principle of secure design.

Defender checklist: hardening Python image processing pipelines

Take this with you

Ordered engineering actions to mitigate CVE-2026-112900 and image decoder risks

  • Upgrade Pillow immediately to version 13.0.0 or later across all production virtual environments and container images.
  • Audit container base images and remove Ghostscript binaries unless EPS/PS rendering is explicitly required by business workflows.
  • Inspect file upload endpoints to ensure user-supplied filenames are stripped and replaced with cryptographically random UUIDs before processing.
  • Reject uploaded files whose names begin with hyphens, @ symbols, or non-alphanumeric characters at the API gateway layer.
  • Pass image data to Pillow via in-memory byte streams (io.BytesIO) rather than passing raw filesystem paths where feasible.
  • Enforce strict container sandboxing: execute worker processes as non-root users with read-only root filesystems and dropped capabilities.
  • Run automated dependency scanners (such as pip-audit or Dependabot) in continuous integration pipelines to catch transitive Pillow versions.

The exposing question

When your application security team inspects user uploads by verifying file headers, MIME types, and magic bytes, how many backend services still hand the untrusted filename directly to an operating system binary that treats punctuation as code?

Key facts

Sources

  1. PrimaryAdvisory GHSA-w889-685w-c8fh: Argument Injection in Ghostscript call, potentially leading into RCEPython Pillow Maintainers (GitHub Security Advisory)accessed 2026-10-23
  2. PrimaryCVE-2026-112900 Official Vulnerability Record and CVSSv4.0 ScoringMITRE CVE Assigning Authorityaccessed 2026-10-23
  3. PrimaryCommit a2c3631: Transform EPS file name to an absolute path before invoking Ghostscript (#10090)Pillow Git Repositoryaccessed 2026-10-23
  4. PrimaryNCSC Guidance: Software Supply Chain Security and Managing Third-Party DependenciesUK National Cyber Security Centre (NCSC)accessed 2026-10-23

Share this briefing

Know someone who owns this problem? Send it to them.

Related briefings

The briefing, in your inbox

Practitioner analysis of cyber and AI security news. No vendor noise.

How often

Every new briefing in one email, at 7am, or at 7am, 12:30pm and 6pm. Nothing is sent when nothing is new. Unsubscribe any time.