Содержание статьи

Патчи к Windows — замечательный источник пусть и уже закрытых, но по-прежнему интересных уязвимостей. Мы разберем патч MS16-051, который латает уязвимость, приводящую к повреждению памяти в скриптовом движке. Эксплоит позволяет использовать ее для выполнения произвольного кода.

 

CVSSv2

Нет

 

BRIEF

Дата релиза: 22 июня 2016 года
Автор: Theori
CVE: CVE-2016-0189

Первым эту тему изучили и описали в своем блоге исследователи из коллектива Theori. Для анализа использовалась утилита BinDiff, исследовалась библиотека vbscript.dll.

Сравнение уязвимой и запатченной версий библиотеки vbscript.dll
Сравнение уязвимой и запатченной версий библиотеки vbscript.dll

На скриншоте заметно, что изменения коснулись только нескольких функций. Наиболее подозрительными выглядят исправления в функции AccessArray(). Проанализируем ее в IDA.

Анализ изменений в функции AccessArray()
Анализ изменений в функции AccessArray()

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

Следующие изменения, связанные с безопасностью, нашлись в функции IsUnsafeAllowed(). Сравним ее апрельскую и майскую версии.

Псевдокод функции `IsUnsafeAllowed()` в версии за апрель
Псевдокод функции `IsUnsafeAllowed()` в версии за апрель
Псевдокод функции `IsUnsafeAllowed()` в версии за май
Псевдокод функции `IsUnsafeAllowed()` в версии за май

Снова изменения бросаются в глаза. В прошлой версии IsUnsafeAllowed() вызывала функцию, которая всегда возвращала ноль без рассмотрения политик, тогда как исправленная версия вызывает указатель на функцию, расположенную в QueryProtectedPolicyPtr. InitializeProtectedPolicy() инициализирует указатель на функцию, используя GetProcAddress.

Псевдокод функции InitializeProtectedPolicy()
Псевдокод функции InitializeProtectedPolicy()

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

 

EXPLOIT

Первая уязвимость — отсутствие блокировки SafeArray в AccessArray. В патче добавился код для блокировки массива, а это значит, что атакующий мог как-то модифицировать массив, что, вероятно, вело к несоответствию некоторых свойств (к примеру, cDims или cbElements).

...
while ( 1 )
{
  curVar = VAR::PvarCutAll(curVar_);
  if ( VT_I2 == curVar->vt )
  {
    v14 = curVar->iVal;
  }
  else if ( VT_I4 == curVar->vt )
  {
    v14 = curVar->lVal;
  }
  else
  {
    v22 = 0;
    v18 = rtVariantChangeTypeEx(curVar, &v22, 0x400, 2, 3u, v20, v21);
    if ( v18 < 0 )
      return CScriptRuntime::RecordHr(a4, v18, v19, v20, v21);
    v14 = v23;
  }
  v15 = v14 - v25->lLbound; // lLbound всегда 0
  if ( v15 < 0 || v15 >= v25->cElements )
    return CScriptRuntime::RecordHr(a4, 0x8002000B, v25, v20, v21);
  numDim = (numDim - 1);
  idx = v15 + v11;
  if ( numDim <= 0 )
    break;
  ++v25;
  v11 = v25->cElements * idx;
  curVar_ = (a4 + 16);
  a4 = (a4 + 16);
}
*v24 = arr->pvData + idx * arr->cbElements; // cbElements == 16
...

Код в основном цикле начинает с крайнего правого измерения индексов массива и использует их для вычисления указателя на данные. Заметь, что если тип Variant для индекса — VT_I2 или VT_I4, то значения читаются напрямую как short и long соответственно. Однако для любого другого типа Variant вызывается rtVariantChangeTypeEx для вычисления индекса. Когда эта функция получает объект JavaScript, она извлекает значение, вызывая под конец valueOf для этого объекта. Если предоставить ей объект с методом valueOf, то это даст возможность выполнять произвольный код на VBScript или JavaScript внутри rtVariantChangeTypeEx.

// Эксплоит и triggerBug определены в VBScript
var o;
o = {"valueOf": function () {
  triggerBug();
  return 1;
}};
setTimeout(function() {exploit(o);}, 50);

Мы можем использовать это для изменения размера массива, в котором находимся в текущий момент. К примеру, представим, что у нас есть двумерный массив.

ReDim Preserve A(1, 2000)

Когда мы получим доступ к массиву, например через A(1, 2), то idx в AccessArray будет рассчитываться как 1 + (2 * (2 - 0)), что равняется 5. Далее это число умножается на значение cbElements, которое всегда равно 16 (sizeof(VARIANT)). Результат добавляется к указателю на данные (pvData), и возвращаются значения по адресу A(1, 2).

В обычном случае это не ошибка, потому что выделенный буфер равен примерно 16 * 2 * 2001 == 64 032 байта. Тем не менее смещение окажется вне скоупа, если буфер изменится на меньший. Другими словами, мы можем получить доступ к A(1, 2), когда массив определен как A(1, 1).

Наш эксплоит будет перезаписывать память, освободившуюся в результате изменения размера массива. Это позволит нам создать VBScript и Variant для чтения и записи вне скоупа. Следующий шаг — получение адреса объекта, чтение памяти по этому адресу и запись в память при помощи многократного вызова ошибки.

Теперь рассмотрим вторую уязвимость — обход IsUnsafeAllowed().

До патча функция IsUnsafeAllowed() всегда возвращала единицу, потому что COleScript::OnEnterBreakPoint — это пустая функция, которая всегда возвращает ноль. В патче это было исправлено. Теперь QueryProtectedPolicy выполняется должным образом, если доступна в системе (она есть в версиях Windows 8.1 и выше).

По умолчанию Internet Explorer предотвращает запуск скриптов, которые могут навредить системе. Функция InSafeMode() проверяет флаг режима безопасности (по умолчанию 0xE), а также проводит проверку на небезопасные расширения — такие как Shell.Application. К счастью, мы узнаем, что IsUnsafeAllowed() всегда возвращает True, то есть мы можем изменить значение флага с помощью первой уязвимости.

Псевдокод функции InSafeMode()
Псевдокод функции InSafeMode()

Однако такой вариант не победит Protected Mode (песочницу) Internet Explorer. Поэтому мы рассмотрим пример обхода песочницы и запустим код с Medium Integrity Level.

Следующий пример кода вызывает падение Internet Explorer из-за обращения к памяти, которая больше не доступна. Размер второго массива изменяется на меньшее значение в процессе доступа к нему.

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


Check Also

Павел Дуров раскритиковал WhatsApp и призвал пользователей удалить мессенджер

Недавно в WhatsApp была обнаружена очередная уязвимость, и Павел Дуров, пользуясь случаем,…

Оставить мнение