Skip to main content
VulnerabilityPublished Updated

CVE-2026-112900: Pillow · Argument Injection in Ghostscript call, potentially leading into RCE

Affects aiderSource GCVE Database

0
Compare
Authoritative record:CVE-2026-112900· Critical 9.2

Not known to be exploited · CVSS 9.2 Critical.

Data as of 30 Oct 2026, 07:30 UTC

CVSS 4.0 base score
9.2Critical
How severe the flaw is on the 0–10 CVSS scale.
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 19 Oct 2026

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponized: no evidence on record.

    No public exploit code indexed in Metasploit or Exploit-DB

  4. Exploited in the wild: no evidence on record.

    Not in CISA KEV and no source reports exploitation

  5. Observed by OffSeq Mirage: no evidence on record.

    No attack traffic recorded by OffSeq Mirage sensors

A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 29 Oct 2026.

What to do

Affected software

ProductAffectedFixed in
Homebrewaiderpkg:brew/aiderOther threatsAffected
  • >=0.86.1_1
Fixed in–

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

Description

### Summary `PIL.EpsImagePlugin.Ghostscript()` builds the Ghostscript command line using the input file path (`fp.name`) **without sanitization**, placing it at `-f <infile>`. A filename beginning with `-` is interpreted by Ghostscript as an option, giving command-line argument injection; chainable to RCE (see Impact). Reproduces with Pillow's default invocation (with `-dSAFER` enabled). ### Details _Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer._ #### Type CWE-88 (Argument Injection) (→ CWE-78 (OS Command Injection) optionally, with attacker controlled text file) #### Vulnerable Codes ```python infile_temp = None if hasattr(fp, "name") and os.path.exists(fp.name): infile = fp.name # user-controlled path used as-is, without validation else: in_fd, infile_temp = tempfile.mkstemp() os.close(in_fd) infile = infile_temp # ... (copy fp into a temporary file) # Build Ghostscript command command = [ gs_binary, "-q", f"-g{width:d}x{height:d}", f"-r{res_x:f}x{res_y:f}", "-dBATCH", "-dNOPAUSE", "-dSAFER", # default sandbox — does NOT stop this injection f"-sDEVICE={device}", f"-sOutputFile={outfile}", "-c", f"{-bbox[0]} {-bbox[1]} translate", "-f", infile, # injected here: inserted into argv with no check for # option prefixes (-, --) or Ghostscript's @-file token # => argument injection (CWE-88) "-c", "showpage", ] # ... subprocess.check_call(command, startupinfo=startupinfo) # spawns Ghostscript ``` #### Root cause. When rendering an EPS image, PIL.EpsImagePlugin.Ghostscript() constructs the Ghostscript command line using the input file path (fp.name) and places it directly at the -f <infile> position of the argument list. This path is user-controllable, yet Pillow performs no validation or neutralization of tokens that Ghostscript treats specially during command-line argument parsing — in particular a leading - or -- (which Ghostscript interprets as an option rather than a filename) and a leading @ (Ghostscript's argument-file syntax, which causes it to read additional command-line arguments from the named file). Because the filename is neither normalized (e.g., forced to an absolute path or prefixed with ./) nor checked against these prefixes before being handed to the subprocess, an attacker who controls the filename passed to Image.open() can inject arbitrary Ghostscript command-line arguments. This is a classic argument-injection flaw (CWE-88), and it is not mitigated by Pillow's default -dSAFER setting. #### Affected - Pillow: tested on 12.3.0 (latest); vulnerable code is long-standing and appears to affect all current versions (main included). - Component: `src/PIL/EpsImagePlugin.py`, `Ghostscript()` — `infile = fp.name`, and the command list entry `"-f", infile`. #### Suggested fix Normalize the filename to an absolute path before it is passed to Ghostscript, so it can never be parsed as an option (`-`/`--`) or as an argument-file token (`@`): ```python if hasattr(fp, "name") and os.path.exists(fp.name): infile = os.path.abspath(fp.name) # was: infile = fp.name ``` `os.path.abspath()` guarantees the path starts with a path separator (or a drive letter on Windows), so a leading `-` or `@` can no longer reach Ghostscript's argument parser. The `else` branch already builds a safe `tempfile.mkstemp()` path and needs no change. ### PoC dockerfile, Default run shows the argument-injection demo (poc.py); for the RCE PoC run python3 poc-rce.py. ``` FROM ubuntu:24.04 RUN apt update RUN apt install -y --no-install-recommends python3 python3-venv ghostscript ca-certificates RUN rm -rf /var/lib/apt/lists/* RUN python3 -m venv /venv && /venv/bin/pip install --upgrade pip Pillow==12.3.0 ENV PATH=/venv/bin:$PATH WORKDIR /poc COPY poc.py . COPY poc-rce.py . COPY arg . CMD ["python3", "poc.py"] ``` poc.py ``` import os from PIL import Image ############################################################################################ # Attacker controlled file ############################################################################################ EPS = b"%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: 0 0 100 100\n%%EndComments\nshowpage\n" name = "-h" with open(name, "wb") as f: f.write(EPS) ############################################################################################ print(f"[*] Pillow opens a file whose name is {name!r}") im = Image.open(name) print(f"[*] call `load()`; this paths calls `gs` command with '-f {name}'") try: im.load() except Exception as e: print(f"[*] load() raised: {type(e).__name__}: {e}") print("CWE-88: argument injection; Pillow code doesn't validate argument name") print(" This poc.py shows the vulnerability clearly.") print("

References

Community reviews

Loading reviews…