Злая картинка. Разбираем уязвимость в GhostScript, чтобы эксплуатировать Pillow и ImageMagick

Специалисты из Google Project Zero нашли несколько опасных уязвимостей в Ghostscript — популярной реализации PostScript. Правильно сформированный файл может позволить исполнять произвольный код в целевой системе. Уязвимости подвержена и библиотека Pillow, которую часто используют в проектах на Python, в том числе — на вебе. Как это эксплуатировать? Давай разбираться.

Python Imaging Library (PIL) и ее современный форк Pillow предназначены для работы с изображениями из Python. В общих чертах они напоминают модуль gd в PHP. Эти библиотеки используются во многих популярных фреймворках и модулях. Их вызовы можно встретить в самых разных примерах кода. В общем, Pillow нередко встречается в продакшене, если один из компонентов стека — это язык Python.

Для операций с файлами PIL и Pillow используют внешние утилиты, такие как Ghostscript. Ghostscript — это кросс-платформенный интерпретатор языка PostScript (PS). Он может обрабатывать файлы PostScript и конвертировать их в другие графические форматы, выводить содержимое и печатать на принтерах, не имеющих встроенной поддержки PostScript.

А PostScript, в свою очередь, — это не просто язык разметки, а полноценный язык программирования. В нем реализованы свои алгоритмы работы с текстом и изображениями.

Официальная документация Adobe на PostScript в данный момент насчитывает около 900 страниц текста и примеров. Так что развернуться тут есть где. Неудивительно, что настолько развесистая штуковина иногда позволяет проделывать вещи, которые не были предусмотрены разработчиками интерпретаторов.

На этот раз в интерпретаторе Ghostscript и была обнаружена пачка уязвимостей, которые снова нашел Тавис Орманди (Tavis Ormandy) из Google Project Zero. Он сообщил о своей находке осенью этого года. Найденные уязвимости — это, по сути, продолжение прошлогодней ошибки в Ghostscript, что получила название GhostButt.

Давай выясним, какие слабые места были обнаружены и каким образом их можно проэксплуатировать.

Стенд

Демонстрировать уязвимость я, как обычно, буду с помощью Docker и контейнера на основе Debian.

Если хочешь немного подебажить, то запускай контейнер с соответствующими ключами.

Обновляем репозитории и устанавливаем Python, менеджер пакетов pip и вспомогательные утилиты.

Теперь установим последнюю уязвимую версию Pillow.

Для удобства тестирования нам также понадобится Flask. Это популярный фреймворк для создания веб-приложений.

Теперь с его помощью напишем небольшой скриптик, который будет принимать пользовательские картинки и менять их размер. Довольно обычное поведение для современных веб-сервисов.

Осталось запустить этот скрипт и посмотреть на результат его работы в браузере.

Готовый стенд для тестирования уязвимости в PIL

Если не хочешь возиться со всеми предустановками вручную, то можешь воспользоваться готовым решением из репозитория Vulhub.

Также нам нужен собственно сам Ghostscript версии ниже 9.24. Я буду использовать две версии: 9.21 — для демонстрации уязвимости GhostButt и 9.23 — для тестирования текущего бага. Взять их можно на официальном сайте в разделе загрузок.

После распаковки в соответствующих папках ты найдешь бинарники gs-921-linux-x86_64 и gs-923-linux-x86_64. Я буду перемещать их в /usr/bin/gs по мере необходимости.

Еще я поставил вспомогательную утилиту для отладчика GDB — pwndbg.

И скачал исходники Ghostscript, чтобы скомпилировать дебаг-версии утилиты.

Готовые дебаг-бинарники будут лежать в папке debugbin. Вот теперь стенд готов.

Бинарник Ghostscript, скомпилированный с отладочной информацией
 

Оригинальный GhostButt (CVE-2017-8291) и причины уязвимости PIL

Прежде чем переходить к рассмотрению недавних уязвимостей, вернемся на год назад и посмотрим на их прародителя. Проблемные версии — 9.21 и ниже, поэтому берем 9.21.

Используем Ghostscript версии 9.21

Первым делом стоит обратить внимание на то, что PIL автоматически определяет тип передаваемого файла. По аналогии с ImageMagick библиотека смотрит на заголовок картинки и передает управление нужному участку кода.

При обработке файла отрабатывает функция _open_core. Она вызывает метод _accept из каждого класса, который отвечает за формат файла. В качестве аргументов передаются первые 16 байт обрабатываемого файла.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *