Анализ эксплуатации CVE-2018-8174
В конце апреля 2018 года мы описали новую уязвимость нулевого дня CVE-2018-8174 для Internet Explorer, найденную благодаря нашей «песочнице». Уязвимость использует известную из эксплойта Proof of Concept для CVE-2014-6332 технику, суть которой в «повреждении» двух объектов памяти и изменении типа одного объекта на Array для возможности чтения и записи адресного пространства, а другого объекта — на Integer для получения адреса произвольного объекта.
В то время как эксплойт для CVE-2014-6332 был нацелен на эксплуатацию целочисленного переполнения, которое позволяло производить запись в произвольные участки памяти, меня заинтересовало, как эта техника была адаптирована для использования уязвимости Use-After-Free. Для ответа на этот вопрос рассмотрим внутреннее устройство VBScript-интерпретатора.
Недокументированная платформа
Отладка исполняемого скрипта VBScript — сложная задача. Перед исполнением он компилируется в p-code, который интерпретируется виртуальной машиной. В открытых источниках отсутствует какая-либо информация о внутреннем устройстве этой виртуальной машины и ее инструкциях. Тем не менее, мне удалось найти две веб-страницы с сообщениями инженеров Microsoft, датированные 1999 и 2004 годами и дающие некоторое представление о p-code. Этой информации оказалось достаточно для того, чтобы полностью разобрать все инструкции VM и написать дизассемблер! Созданные в ходе исследования скрипты для дизассемблирования VBScript p-code в памяти отладчиков IDA Pro и WinDBG доступны в нашем репозитории GitHub.
Способность понять интерпретируемый код дает нам возможность очень точно следить за исполнением скрипта: мы владеем полной информацией о том, где код исполняется в данный момент, и можем наблюдать все объекты, которые он создает и использует. Все это значительно помогает в анализе.
Лучшее место для запуска дизассемблирующего скрипта — функция CScriptRuntime::RunNoEH, ответственная непосредственно за интерпретирование p-code.
Класс CScriptRuntime содержит всю информацию о состоянии интерпретатора: локальные переменные, аргументы функции, указатель на вершину стека и текущую инструкцию, а также адрес скомпилированного скрипта.
Виртуальная машина VBScript стек-ориентирована и состоит из чуть более 100 инструкций.
Все переменные (локальные аргументы и на стеке) представлены в виде структуры VARIANT, занимающей 16 байт, верхнее слово которой обозначает тип данных. Часть значений типов представлена на соответствующей странице в MSDN.
Эксплуатация CVE-2018-8174
Ниже представлен код и дизассемблированный p-code класса Class1:
|
|
Функция 34 — это конструктор класса Class1.
Инструкция OP_CreateClass вызовет функцию VBScriptClass::Create для создания объекта VBScriptClass.
Инструкции OP_FnBindEx и OP_CreateVar попытаются получить переменные, переданные в аргументах. Так как они еще не существуют, функция VBScriptClass::CreateVar создаст их.
Эта диаграмма показывает, как переменные могут быть получены из объекта VBScriptClass. Значение переменной хранится в структуре VVAL:
Для понимания эксплуатации важно знать, как переменные представлены в структуре VBScriptClass.
Когда инструкция OP_NamedSt ‘mem’ выполняется в функции 36 (SetProp), она вызовет Default Property Getter экземпляра класса, который перед этим был сохранен на стеке, и затем сохранит возвращенное значение в переменную mem.
***BOS(8292,8301)*** mem=Value *****
0000OP_Bos1 0
0002OP_LocalAdr -1 <——— сохраняет аргумент на стеке
0005OP_NamedSt ‘mem’ <——— если это диспетчер класса с Default Property Getter вызвать и сохранить возвращенное значение в 'mem'
Ниже код и дизассемблированный p-code функции 30 (p), которая будет вызвана во время исполнения инструкции OP_NamedSt:
|
|
Первый базовый блок этой функции:
***BOS(8626,8656)*** P=CDbl(«174088534690791e-324») *****
0000OP_Bos1 0
0002OP_StrConst ‘174088534690791e-324’
0007OP_CallNmdAdr’CDbl’ 1
000EOP_LocalSt 0
Этот блок конвертирует строку 174088534690791e-324 в VARIANT и сохраняет в локальную переменную 0, зарезервированную для возвращаемого значения функции.
После установки возвращаемого значения, но перед непосредственно возвратом эта функция выполняет:
For IIIl=0 To 6
IIIlI(IIIl)=0
Next
Это вызывает сборщик мусора для экземпляра Class1 и приводит к использованию висячего указателя из-за Use-After-Free уязвимости в Class_Terminate(), о которой мы писали ранее.
В строке
***BOS(8855,8874)*** Set llII=New Class2 *****
0047OP_Bos1 4
0049OP_InitClass ‘Class2’
004EOP_LocalSet 1
инструкция OP_InitClass ‘Class2’ создает экземпляр «злого близнеца» класса Class1 на месте предварительно освобожденного VBScriptClass, который все еще используется инструкцией OP_NamedSt ‘mem’ в функции 36 (SetProp).
Класс Class2 — «злой близнец» класса Class1:
|
|
Расположение переменных в памяти предсказуемо. Размер данных, занимаемых структурой VVAL, рассчитывается по формуле 0x32 + длина имени переменной в UTF-16.
Ниже представлена диаграмма, которая показывает расположение переменных Class1 относительно переменных Class2, когда Class2 выделен на месте освобожденного Class1.
В конце исполнения инструкции OP_NamedSt ‘mem’ в функции 36 (SetProp) значение, возвращенное функцией 30 (p), будет записано по висячему указателю VVAL ‘mem’ в Class1, переписав тип VARIANT у VVAL ‘mem’ в Class2.
Таким образом, объект с типом String превращается в объект с типом Array, и данные, которые раньше считались строкой, третируются как контрольная структура Array, позволяющая получить доступ ко всему адресному пространству процесса.
Заключение
Наши скрипты для дизассемблирования VBScript, скомпилированного p-code, дают возможность осуществлять отладку VBScript на уровне байт-кода, что значительно помогает в анализе эксплойтов и понимании принципов работы VBScript. Они доступны в нашем публичном репозитории Github.
Случай с CVE-2018-8174 демонстрирует, что когда выделения памяти очень предсказуемы, эксплуатация Use-After-Free уязвимостей не составляет труда. Эксплойт, найденный в «дикой природе», нацелен на более старые версии Windows. Для Windows 7 и Windows 8.1 необходимое для его эксплуатации расположение объектов в памяти наиболее вероятно.
Продукты «Лаборатории Касперского» блокируют компонентом Automatic Exploit Protection (AEP) все стадии эксплойта со следующими вердиктами:
- HEUR:Exploit.MSOffice.Generic
- HEUR:Exploit.Script.CVE-2018-8174.a
- HEUR:Exploit.Script.Generic
- HEUR:Trojan.Win32.Generic
- PDM:Exploit.Win32.Generic
Погружение в VBScript