Pillow EPS flaw lets a filename run Ghostscript commands
CVE-2026-112900 lets a crafted filename inject Ghostscript options through Pillow's EPS loader. Who is exposed, the 13.0.0 fix, workarounds and checks.
By Yaali. October 25, 2026, 6 min read, Vulnerabilities, Patching.
Pillow, the Python imaging library that web apps and data pipelines use to resize, convert and thumbnail images, has an argument injection flaw in the way it calls Ghostscript to render EPS (Encapsulated PostScript) files. Tracked as CVE-2026-112900 (GitHub advisory GHSA-w889-685w-c8fh), it affects every release from 2.6.0 through 12.3.0 and is fixed in Pillow 13.0.0, released on October 16, 2026. A file name that starts with - or @ is read by Ghostscript as an option rather than a file, and with a second attacker-controlled file the reporter got Ghostscript to run arbitrary commands.
You are exposed if three things are true on the same host: Pillow older than 13.0.0, Ghostscript installed, and code that opens files whose names a user can influence. Upload handlers that keep the original filename, batch converters that walk shared folders, and document pipelines are the usual places. Pillow rates the issue Moderate and lists it as Medium in the 13.0.0 release notes, while the oss-security announcement of the release grouped it under High. No CVSS score had been published when we checked, and we found no reports of exploitation.

How it works
Pillow cannot decode PostScript itself. When code calls Image.open() on an EPS file and then touches the pixels (load(), thumbnail(), convert(), save()), the function Ghostscript() in src/PIL/EpsImagePlugin.py builds a command line for the system gs binary. It runs with -dSAFER, Ghostscript's sandbox mode, and passes the input path after -f.
Before 13.0.0, that path was fp.name exactly as the caller supplied it. If the application opened -h, Ghostscript received -f -h and treated -h as its help switch. That alone is single-argument injection: an attacker can add one option of their choosing to the command line.
The second prefix is the dangerous one. Ghostscript treats an argument that starts with @ as an argument file, a text file whose contents are read as further command-line arguments. If an attacker can make the application open a file named, say, @opts and also control the contents of a file Ghostscript will read under that name, they can feed Ghostscript as many options as they like. The reporter, Chanu Kang of the CysecLab at KAIST, showed this ending in execution of id, with Pillow's default invocation, -dSAFER included. The proof of concept ran on Ubuntu 24.04 with Pillow 12.3.0 and Ghostscript 10.02.1, and the reporter also confirmed it against Ghostscript 10.07.1.
Two limits matter when you judge your exposure. The injection only works when the path itself begins with - or @, which in practice means a bare relative filename opened from the current working directory; a path such as uploads/-h is safe. The code-execution chain also needs a second file the attacker controls. The advisory argues this is a Pillow bug rather than the documented risk of rendering untrusted PostScript, because the trigger is the file name, not the file contents.
The fix is four lines in commit a2c3631: Pillow now calls os.path.abspath() on the name before building the command, so the path always starts with / (or a drive letter on Windows). Code that hands Pillow a BytesIO object or other stream without a name was already safe, because Pillow writes those to a temporary file with a generated name.
What attackers are doing
Nothing public yet. Pillow published the advisory on October 17, the day after the release, and the CVE appeared in the oss-security announcement on October 21. The advisory includes working proof-of-concept scripts and a Dockerfile, so the bar for anyone looking for a vulnerable upload handler is low.
Ghostscript itself also needs attention. CVE-2026-101258, disclosed on October 6, is a -dSAFER sandbox bypass reachable through a crafted PostScript or EPS document, rated CVSS 7.8 by Red Hat. It is a separate bug, but any Python service that lets Pillow call Ghostscript on user files is exposed to both, so update the ghostscript package from your distribution at the same time.
What to do

1. Upgrade Pillow to 13.0.0
Find every copy, not only the one in your main application. Pillow is a dependency of many packages, and Docker images often pin old versions.
python -c "import PIL; print(PIL.__version__)"in each virtual environment or container.pip index versions pillowandpip install --upgrade "pillow>=13.0.0", then update your lock file.- Distribution packages lag: Debian's tracker listed bookworm (9.4.0), trixie (11.1.0) and forky (12.3.0) as vulnerable, with only sid on 13.0.0. If you install Pillow from the distribution, use a pinned pip version or rebuild once the package is updated.
The 13.0.0 release notes list 37 other security fixes, two of them rated Critical, so the upgrade is worth doing even where EPS is out of scope. Check the release notes for API changes before you deploy.
2. If you cannot upgrade today
- Never open a file by a name the user chose. Save uploads under a name your code generates (a UUID, for example) inside a fixed directory, and open them by absolute path.
- Wrap calls yourself:
Image.open(os.path.abspath(path))is the same fix Pillow shipped. - Restrict formats:
Image.open(path, formats=["JPEG", "PNG", "WEBP"])stops Pillow from treating the file as EPS at all. Pillow's security guide recommends this allowlist for untrusted input. - If no feature needs EPS, remove Ghostscript from the image or host. Pillow cannot render EPS without it.
3. Check whether you were hit
- List upload and working directories for files whose names begin with
-or@:find /srv/app -name '-*' -o -name '@*'(adjust the path). Any such file next to a Pillow-based service needs explaining. - Search web server and application logs for upload requests carrying
filename="-orfilename="@in the multipart headers. - If you run auditd or an EDR (endpoint detection and response) agent, look for
gsprocesses whose parent is a Python worker and which themselves started a shell,curl,wgetorpython. Ghostscript has no reason to start any of those.
If you find signs of compromise, treat the service account as taken: rebuild the host or container, rotate secrets in its environment (database passwords, cloud keys, API tokens), and check what else that account could reach.
The wider lesson
The flaw sat in every release since 2.6.0. File names are input, and any code that builds a command line from them needs an end-of-options marker or an absolute path. A quick review of where your services call Image.open(), subprocess or a converter library with a user-supplied name usually turns up more than one case like this.
Our red team code review traces untrusted input such as file names into libraries and subprocess calls, and our web penetration testing covers upload handlers end to end. Open the chat and Yaali, our AI agent, will pass your question to the engineer who would do the work.
Sources: Pillow security advisory GHSA-w889-685w-c8fh, Pillow fix commit, Pillow 13.0.0 release notes, Pillow security handbook, Debian security tracker, oss-security announcement, SUSE: CVE-2026-101258
Read next
- GoAhead TLS flaws: revoked certificates still get in
- HPE Aruba AOS: 72 flaws, eight rated 10.0, patch now
- Authenticator for Android handed work tokens to any app
Back to the blog, or tell us about your system in the chat. Yaali, our AI agent, answers first and brings in an engineer.