
Exploit de preuve de concept pour CVE-2023-36664, une vulnérabilité d'injection de commandes Ghostscript. Comprend un environnement de laboratoire Docker, une analyse détaillée du contournement de validation des pipes et un guide de reproduction étape par étape.
CVE ID: CVE-2023-36664
Produit: Ghostscript (< 10.01.2)
Type de vulnérabilité: Remote Code Execution (RCE)
Cette vulnérabilité (CVE-2023-36664) est une vulnérabilité d'exécution de code arbitraire (RCE) résultant d'une validation insuffisante des autorisations de chemin pour les périphériques de type pipe (préfixe %pipe% ou |) dans Ghostscript.
Lorsqu'un système analyse ou traite un fichier de document malveillamment conçu (PS/EPS) par un attaquant, des commandes système arbitraires peuvent être exécutées sans autorisation de l'utilisateur.
Rôle principal : Il lit essentiellement un code de coordonnées graphiques textuel (par exemple, 'tracer une ligne à la position 100 200') et le convertit en une image visible sur un écran d'ordinateur ou un fichier image destiné à une sortie imprimante.
Un pipe est une fonctionnalité qui permet de connecter la sortie d'une application à l'entrée d'une autre, permettant ainsi aux logiciels de communiquer entre eux. Dans les commandes, il est représenté par le symbole |.
Exemple : cat /etc/hosts | grep localhost
Ghostscript vérifie strictement les chemins de fichiers lorsque le mode sécurisé (-dSAFER) est activé par défaut, empêchant ainsi l'accès aux fichiers sensibles du système ou l'exécution de commandes externes non autorisées.
Problème : Lorsqu'un attaquant insère, à la place d'un nom de fichier normal, un préfixe de périphérique pipe (%pipe% ou |) soigneusement manipulé, la logique de validation des autorisations de Ghostscript ne reconnaît pas correctement ce préfixe et le considère à tort comme un « chemin sûr » ou « ne nécessitant pas de validation », le laissant passer.
Résultat : Les commandes dangereuses qui auraient dû être filtrées contournent facilement la boucle de validation.
En examinant le code corrigé, on constate que deux fichiers .c ont été modifiés :
base/gpmisc.cbase/gslibctx.cIci, en observant gpmisc.c, on peut comprendre la vulnérabilité.

Le cœur de cette vulnérabilité est que des chemins spéciaux comme %pipe% ne sont pas correctement distingués des chemins de fichiers normaux et sont reconnus comme des chemins normaux.
gp_file_name_reducePour le vérifier, il est nécessaire d'examiner la fonction qui nettoie les chemins. Dans gpmisc.c, la fonction chargée de nettoyer les chemins est gp_file_name_reduce(...), et elle appelle en interne gp_file_name_combine() dont elle retourne le résultat.
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(), comme son nom l'indique, est une fonction qui supprime les expressions de chemin relatif inutiles telles que ./, // du chemin de fichier reçu.
Le problème se situe ici. Si cette fonction reçoit, au lieu d'un chemin de fichier normal, une chaîne spéciale destinée à exécuter une commande comme %pipe%, elle ne la reconnaît pas comme un modèle de chemin valide et retourne la chaîne originale sans aucun traitement.
gp_validate_path_len
gp_validate_path_len(...) est une fonction qui valide un chemin en appelant en interne gp_file_name_reduce(), mais dans ce processus, il n'existe aucune logique de validation distincte permettant de déterminer si l'entrée est un chemin de fichier normal ou une syntaxe d'exécution de commande comme %pipe%.
Par conséquent, une chaîne contenant %pipe% passe sans aucun filtrage, ce qui est la cause fondamentale de cette vulnérabilité.
Ainsi, si une chaîne comme %pipe%touch /tmp/pwned est transmise comme chemin de fichier, l'utilisateur pensait simplement vouloir afficher un fichier image, mais en réalité, la commande touch /tmp/pwned est exécutée, créant le fichier /tmp/pwned.
┌─────────────────────────────────┐
│ Docker Container │
│ (Ubuntu 22.04 + Ghostscript) │
│ │
│ /home/test/ ← Working directory
│ ├── poc.py ← Script de génération du PoC
│ | |
│ └── /var/www/html/config.php ← Fichier d'informations sensibles
│ │
│ User: test (non root) │
│ Ghostscript: 10.01.1 (vulnérable) │
└─────────────────────────────────┘
La raison pour laquelle un compte normal et non root a été choisi ici est que, dans les serveurs de production réels, il est courant de ne pas utiliser directement un compte root pour des raisons de sécurité. Par conséquent, afin de reproduire aussi fidèlement que possible le scénario d'une attaque réelle sur un serveur, l'environnement a été configuré sur la base d'un compte normal.
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} : Transmet la variable d'environnement pour permettre aux applications GUI du conteneur d'accéder au serveur d'affichage X11 de l'hôte./tmp/.X11-unix:/tmp/.X11-unix:ro : Monte le socket Unix pour la communication entre le serveur d'affichage X11 de l'hôte et le conteneur.
./poc.py:/home/test/poc.py : Monte le script PoC local de l'hôte dans l'environnement d'exécution du conteneur.
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"]
Pour configurer un environnement PoC de base, on a besoin de wget pour récupérer la version vulnérable de Ghostscript, build-essential pour installer les sources après avoir décompressé le tar.gz, et un éditeur de texte (gedit) pour l'afficher malicieusement lors de l'exécution du fichier .ps, entre autres.
La version vulnérable 10.01.1 de Ghostscript a été installée en téléchargeant les sources depuis git. Les commandes RUN ./configure && \ make && \ make install sont écrites dans le dossier où les sources ont été téléchargées, indiquant ainsi comment procéder à l'installation.
Généralement, le fichier /var/www/html/config.php contient les paramètres essentiels de l'application web, par exemple le nom de la base de données et le mot de passe. Donc, on suppose que l'on veut dérober le mot de passe de la base de données et on crée un fichier config.php arbitraire.
Comme le PoC sera effectué avec un compte normal (non root) nommé test, on crée un nouvel utilisateur.
# Docker 이미지 빌드
docker compose -f docker-compose.yml up -d
# 컨테이너 실행
docker exec -it cve_lab bash
Dans le conteneur :

python3 poc.py -p "gedit /var/www/html/config.php" -m r -f test
Résultat de la génération :

On peut voir que le chemin malveillant saisi est bien intégré dans le fichier .ps.
gs -dNOSAFER test.ps

gedit s'ouvre automatiquement et affiche le contenu de /var/www/html/config.php.
DB_PASSWORD=SuperSecret1234!!!

Il est nécessaire de mettre à jour vers la version corrigée officielle de Ghostscript 10.01.2 ou une version ultérieure.
Dans le correctif officiel, une logique de validation a été ajoutée dans la fonction gp_validate_path_len() avant d'appeler gp_file_name_reduce().
Traitement d'exception et blocage forcé des chaînes spéciales de pipe : Une branche conditionnelle a été ajoutée pour détecter complètement et séparément les cas où le préfixe du chemin d'entrée commence par %pipe% ou le symbole |.
Version du correctif officiel : https://github.com/ArtifexSoftware/ghostpdl/commit/5f56c6f6f989816fc9cc671116740acecbed5b6c