
Эксплойт Proof-of-concept для CVE-2023-36664, уязвимости внедрения команд в Ghostscript. Включает лабораторную среду Docker, подробный анализ обхода проверки каналов и пошаговое руководство по воспроизведению.
CVE ID: CVE-2023-36664
Продукт: Ghostscript (< 10.01.2)
Тип уязвимости: Remote Code Execution (RCE)
Данная уязвимость (CVE-2023-36664) вызвана недостаточной проверкой прав доступа к путям pipe-устройств (префиксы %pipe% или |) в Ghostscript, что приводит к выполнению произвольного кода (RCE).
Злоумышленник может создать вредоносный файл документа (PS/EPS), и при его разборе или обработке системой могут быть выполнены произвольные системные команды без авторизации пользователя.
Основная роль: Ghostscript читает текстовый код графических координат (например, "нарисуй линию в позиции 100 200") и преобразует его в изображение, видимое на мониторе или в файл для печати.
Pipe (канал) позволяет связать вывод одного приложения с вводом другого, обеспечивая их взаимодействие. В командах обозначается символом |.
Пример: cat /etc/hosts | grep localhost
cat, передаются через pipe (|) на вход команды grep, которая фильтрует только строки, содержащие "localhost".Ghostscript в защищённом режиме (-dSAFER) строго проверяет пути к файлам, чтобы предотвратить доступ к критическим системным файлам или выполнение внешних команд.
Проблема: Когда злоумышленник вместо обычного имени файла вставляет тщательно подогнанный префикс pipe-устройства (%pipe% или |), внутренняя логика проверки прав Ghostscript не распознаёт этот префикс должным образом и ошибочно считает его "безопасным путём" или "не подлежащим проверке", пропуская его.
Результат: Опасная команда, которая должна была быть отфильтрована, обходит проверку.
Внедрение вредоносного кода: Злоумышленник помещает внутрь PostScript-файла (.ps/.eps) код вида (%pipe%вредоносная_команда) (режим) file /DCTDecode filter.
Разбор и ошибка: Ghostscript при чтении и обработке этого файла проходит этап проверки прав (страж) без ошибок.
Передача в OS Shell: Поток команд, прошедший проверку, передаётся через функцию pipe-устройства напрямую в системную оболочку (sh в Linux, cmd в Windows), где выполняется с правами backend.
В исправленном коде были изменены два файла .c:
base/gpmisc.cbase/gslibctx.cДля понимания уязвимости достаточно рассмотреть gpmisc.c.

Суть уязвимости в том, что специальные пути, такие как %pipe%, не отличаются от обычных путей к файлам и ошибочно воспринимаются как обычные пути.
gp_file_name_reduceЧтобы это проверить, нужно рассмотреть функцию, очищающую путь к файлу. В gpmisc.c функцию, отвечающую за обработку пути, называется gp_file_name_reduce(...), которая внутренне вызывает gp_file_name_combine() и возвращает её результат.
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(), как следует из названия, удаляет избыточные элементы относительного пути, такие как ./, //, из переданного пути.
Проблема возникает здесь: если в эту функцию передать не обычный путь к файлу, а специальную строку, инициирующую выполнение команды, например %pipe%, функция не распознаёт её как корректный шаблон пути и возвращает исходную строку без какой-либо обработки.
gp_validate_path_len
gp_validate_path_len(...) внутренне вызывает gp_file_name_reduce() для проверки пути, но в этом процессе отсутствует отдельная логика проверки, различающая, является ли входное значение обычным путём к файлу или синтаксисом выполнения команды, таким как %pipe%.
Как результат, строка, содержащая %pipe%, беспрепятственно проходит без какой-либо фильтрации, что и является коренной причиной данной уязвимости.
Таким образом, если передать строку вида %pipe%touch /tmp/pwned в качестве пути к файлу, пользователь, намеревавшийся просто отрендерить изображение, на самом деле выполнит команду touch /tmp/pwned, что приведёт к созданию файла /tmp/pwned.
┌─────────────────────────────────┐
│ Docker Container │
│ (Ubuntu 22.04 + Ghostscript) │
│ │
│ /home/test/ ← Рабочая директория
│ ├── poc.py ← Скрипт создания PoC
│ | |
│ └── /var/www/html/config.php ← Файл с конфиденциальными данными
│ │
│ User: test (не root) │
│ Ghostscript: 10.01.1 (уязвимая) │
└─────────────────────────────────┘
Причина выбора обычной учётной записи вместо root в том, что на реальных рабочих серверах, как правило, не используют учётную запись root напрямую по соображениям безопасности. Поэтому для максимально точной симуляции атаки на реальный сервер среда настроена на обычную учётную запись.
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}: Передаёт переменную окружения, чтобы GUI-приложения внутри контейнера могли обращаться к X11-дисплею хоста./tmp/.X11-unix:/tmp/.X11-unix:ro: Монтирует Unix-сокет для связи между X11-дисплеем хоста и контейнером.
./poc.py:/home/test/poc.py: Монтирует локальный PoC-скрипт хоста в среду выполнения контейнера.
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"]
Для базовой настройки PoC-среды необходимы: wget (для загрузки уязвимой версии Ghostscript), build-essential (для сборки исходников после распаковки tar.gz), gedit (текстовый редактор, который будет вредоносно открыт при выполнении .ps) и другие.
Уязвимая версия Ghostscript 10.01.1 была скачана из git и установлена из исходников. Команды RUN ./configure && \ make && \ make install выполнены согласно инструкции, прилагаемой к скачанному исходному коду.
Обычно в файле /var/www/html/config.php содержатся ключевые настройки веб-приложения, например, имя базы данных и пароль. Поэтому мы создаём файл config.php, предполагая, что будет выполнен произвольный доступ к паролю БД.
Поскольку PoC будет выполняться от имени обычной учётной записи test, а не root, мы создаём нового пользователя.
# Сборка Docker-образа
docker compose -f docker-compose.yml up -d
# Запуск контейнера
docker exec -it cve_lab bash
Внутри контейнера:

python3 poc.py -p "gedit /var/www/html/config.php" -m r -f test
Результат создания:

Видно, что введённый вредоносный путь успешно помещён внутрь .ps-файла.
gs -dNOSAFER test.ps

Автоматически открывается gedit с содержимым файла /var/www/html/config.php.
DB_PASSWORD=SuperSecret1234!!!

Необходимо обновить Ghostscript до последней версии (10.01.2 или выше), в которой официально исправлена уязвимость.
В официальном исправлении в функцию gp_validate_path_len() была добавлена логика проверки перед вызовом gp_file_name_reduce().
Обязательное обнаружение и блокировка специальных строк pipe: добавлена ветвь, которая отдельно и полноценно определяет случаи, когда префикс входного пути начинается с %pipe% или символа |.
Официальная версия патча: https://github.com/ArtifexSoftware/ghostpdl/commit/5f56c6f6f989816fc9cc671116740acecbed5b6c