
CVE-2017-8570 Exp及利用样本分析
Причина: при открытии документа Office, FLTLDR.EXE используется для отображения встроенного EPS-файла, содержащего уязвимость. Этот файл написан на языке PostScript и может быть использован атакующим с помощью операции "save-restore"; по сути, это уязвимость типа Use-After-Free (UAF). Уязвимость может быть использована, когда пользователь открывает файл, содержащий некорректно сформированное графическое изображение, или когда пользователь вставляет такое изображение в файл Office.
Затрагиваемые версии: Microsoft Office 2010 Service Pack 2, Microsoft Office 2013 Service Pack 1, Microsoft Office 2016
POC: kcufId's Github
Долгое время искал в сети, но не нашёл пакета Office, содержащего EPSIMP32.FLT. К счастью, товарищ kcufId предоставил LoadEps.exe для загрузки EPS-файлов. Спасибо товарищу kcufId.
LoadEps.exe сначала загружает EPSIMP32.FLT:

Затем вызывает ImportGr для начала загрузки EPS-файла:

Здесь нажимаем F7 для входа, и успешно останавливаемся на точке останова, установленной внутри EPSIMP32.FLT.
Прежде чем перейти к основной теме, опишем структуру объекта PostScript.
// PostScript Object
struct PostScript object
{
dword type;
dword attr;
dword value1;
dword value2; // если массив, указывает на userdict, где хранится объект массива
}ps_obj;
Значения различных type:
0x0 nulltype
0x3 integertype
0x5 realtype
0x8 booleantype
0x10 operatortype
0x20 marktype
0x40 savetype
0x300 nametype
0x500 stringtype
0x900 filetype
0x30000 arraytype
0x0B0000 packedarraytype
0x70000 packedarraytype
0x110000 dicttype
0x210000 gstatetype
Рассмотрим на примере строки её структуру хранения. Установив точку останова на функции forall, можно увидеть, как она обрабатывает строки (как найти функцию forall, см. https://paper.seebug.org/368/).

Изображение 1 соответствует ps_obj, его поле value2 указывает на соответствующий элемент списка индексов (изображение 2); элемент индекса указывает на структуру размером 0x30, поле 0x24 которой содержит указатель на указатель на структуру размером 0x28 (изображение 5), поле 0x2C содержит размер строки (изображение 3); структура на изображении 5 в поле 0x4 хранит адрес соответствующего элемента этой структуры в списке индексов (т.е. 0x01DB5E94 с изображения 4), поле 0x20 указывает на конечное местоположение строки (изображение 6), поле 0x24 — фактический занимаемый размер памяти: размер строки + 1.
Структура размером 0x30:
+0x0 dword
+0x4 dword
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword
+0x24 dword pp_struct // указывает на указатель на структуру размером 0x28
+0x28 dword
+0x2c dword size // фактический размер строки
Структура размером 0x28 (для массива эта структура имеет размер 0x2C, а поле 0x28 указывает на элементы массива, каждый из которых является ps_obj):
+0x0 dword
+0x4 dword // хранит адрес соответствующего элемента этой структуры в списке индексов
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword ptr_object // указывает на конечное местоположение строки
+0x24 dword size // фактический занимаемый размер памяти: фактический размер строки + 1
Первое срабатывание уязвимости:

Сначала состояние VM сохраняется в переменной l62, затем для каждого символа в переменной l63 вызывается обработчик l61 → l59 → l56; операция l62 restore восстанавливает предыдущее состояние, в результате чего память, выделенная для l63 после оператора /l62 save def, освобождается и становится висящим указателем.

Переменные l95-l99 определяют дальнейший ход процесса, их значение равно 0 (т.е. 32 бита):

Второе срабатывание уязвимости: сначала выделяется память размером 0x27 (фактически будет занято 0x28) для хранения l63:

Затем l62 restore восстанавливает предыдущее состояние, что приводит к освобождению памяти, выделенной для l63, и она становится висящим указателем; далее выполняется l100, и память, ранее занимаемая l63, используется для хранения структуры размером 0x28 строки l102 (т.е. l136) (это объясняет, почему l63 запрашивал память размером 0x27):

Получаем значения полей 0x4, 0x20, 0x24 этой структуры:

Наконец, изменяем содержимое строки l136 (на рисунке показана лишь часть изменений):

Эти изменения тщательно сконструированы и будут использованы при третьем срабатывании уязвимости.
Третье срабатывание уязвимости: создаётся массив, содержащий 0x37 элементов, затем при обработке элемента с индексом 0x34 выполняется l62 restore:

После выполнения restore структура размером 0x30 массива перезаписывается содержимым строки l193:

Таким образом, объектом, обрабатываемым в последнем (0x36) вызове forall, становится структура размером 0x30, показанная на рисунке выше, а получение её элемента с индексом 0x36 приводит к строке, тщательно сконструированной при втором срабатывании уязвимости:

Полученным элементом массива является массив размером 4, первый элемент которого — строка с начальным адресом 0 и размером 0x7FFFFFFF:

Этот массив сохраняется в переменной l159, его первый элемент — строка с начальным адресом 0 и размером 0x7FFFFFFF — сохраняется в переменной l201, после чего через переменную l201 можно получать значения по произвольным адресам.
Получение базового адреса kernel32.dll:


Таким образом, в переменной l314 хранится базовый адрес EPSIMP32.FLT.

Примечание: синтаксис команды search следующий:

Поиск указанного гаджета:


Создание структуры типа file:
l199 l201 get_dword
/l487 exch def
l487 l201 get_dword
/l488 exch def
l488 36 my_add l201 get_dword
/l489 exch def
l489 l201 get_dword
/l490 exch def
l490 32 my_add l201 get_dword
/l491 exch def
l199 l491 l201 put_data_to_array
l199 12 my_sub 2304 l201 put_data_to_array

Запись сконструированных данных в адрес l492 (l491+0x32):
l492 0 l201 put_data_to_array %% 0x00 0
l492 4 my_add l375 l201 put_data_to_array %% 0x04 Address of <5E C3>
l492 8 my_add l373 l201 put_data_to_array %% 0x08 Address of <94 00 00 00 00 5E C3>
l492 12 my_add l377 l201 put_data_to_array %% 0x0C Address of <C2 0C 00>
l492 16 my_add l370 l201 put_data_to_array %% 0x10 Address of VirtualProtect()
l492 20 my_add 0 l201 put_data_to_array %% 0x14 0
l492 24 my_add 0 l201 put_data_to_array %% 0x18 0
l492 28 my_add 0 l201 put_data_to_array %% 0x1C 0
l492 32 my_add l368 l201 put_data_to_array %% 0x20 Address of Shellcode
l492 36 my_add l368 l201 put_data_to_array %% 0x24 Address of Shellcode——lpAddress
l492 40 my_add l349 l201 put_data_to_array %% 0x28 Size of Shellcode——dwSize
l492 44 my_add 64 l201 put_data_to_array %% 0x2C PAGE_EXECUTE_READWRITE——flNewProtect
l492 48 my_add l493 l201 put_data_to_array %% 0x30 lpflOldProtect
Наконец, при выполнении инструкции closefile происходит переход на Shellcode:

Эксплойт EPS находится в каталоге \word\media, его можно увидеть после распаковки. Данный образец эксплойта, за исключением части Shellcode, практически идентичен, поэтому в качестве примера для анализа возьмём образец, используемый организацией Patchword (название файла: Cyber_Secure_Pakistan.docx).
Имя файла: Cyber_Secure_Pakistan.docx
MD5: DD89BBB916A2C909630EC78CBB0E13E5
Переход на Shellcode, восстановление стека:

Выделение памяти:

Получение адресов функций:

В процессе отладки, возможно из-за проблем с окружением, адрес функции CreateToolhelp32Snapshot не удалось получить:

Вручную вводим адрес и открываем Word для продолжения анализа. Перечисление процессов, поиск WINWORD.exe:

Создание программы с именем MSBuild.exe в каталоге C:\ProgramData\Microsoft\DeviceSync:

Запись содержимого файла; его содержимое хранится в переменной payload_32 скрипта EPS:


Создание файла vmtools.dll:

Запись содержимого файла; его содержимое хранится в переменной payload_32_f2 скрипта EPS:


Создание файла VMwareCplLauncher.exe:

Его содержимое хранится в переменной payload_32_f1 скрипта EPS:

Этот файл является доверенным (белый) файлом, подписанным Vmware:

Внедрение следующего содержимого в explorer.exe:

Его функция — создание процесса VMwareCplLauncher.exe:

Дальнейший процесс упомянут в отчёте 360; в данной статье мы не рассматриваем его анализ:

Заинтересованные читатели могут ознакомиться с этим отчётом дополнительно.
Примечание: образцы эксплойта данной уязвимости в основном схожи, различие заключается лишь в финальной нагрузке MSBuild.exe, которая хранится в переменной payload_32 скрипта EPS. Её можно напрямую выгрузить, дополнить заголовком DOS-файла и загрузить в IDA для анализа.