Технический анализ CVE-2014-1761 RTF Vulnerability

Технический анализ CVE-2014-1761 RTF Vulnerability

Недавно компания Microsoft сообщила о том, пример файла RTF, эксплуатирующий уязвимость CVE-2014-1761, гуляет в «дикой природе». Пример файла также стал достоянием общественности.

Автор: Matt_Oh

Недавно компания Microsoft сообщила о том, пример файла RTF, эксплуатирующий уязвимость CVE-2014-1761, гуляет в «дикой природе». Пример файла также стал достоянием общественности. Я проанализировал этот файл и в этой статье расскажу о результатах своего анализа. Анализируемый мной пример содержит хеш SHA1 200f7930de8d44fc2b00516f79033408ca39d610. При анализе я работал с библиотекой wwlib.dll версии 14.0.7113.5001, которая используется в Microsoft Office 2010.

Главный анализатор (parser) находится по адресу 0x31D0BAFF в файле wwlib.dll.

Рисунок 1: Главный анализатор RTF

Функция чрезмерно большая и содержит несколько switch-конструкций. На Рисунке 2 показана общая структура функции, чтобы вы имели приблизительное представление о том, как она работает. Большинство кода, связанное с уязвимостью и эксплоитом, находится внутри этой функции.

Рисунок 2: Общая структура главного анализатора RTF

Перезапись массива за пределами границ

Рассмотрим общие принципы уязвимости, связанной с перезаписью памяти за пределами границ. В памяти создается массив при использовании контрольного слова listoverridecount. Количество аргументов этого контрольного слова, в данном случае равное 25, используется для размещения массива в памяти для хранения указателей памяти (см. Рисунок 3). Размер каждого элемента массива – 8 байт.

Рисунок 3: Контрольное слово listoverridecount

На Рисунке 4 показан код, который размещает в памяти этот массив. Инструкция по адресу 0x31D0C67F добавляет в стек массив из 25 элементов. Инструкция по адресу 0x31D0C690 сохраняет указатель памяти на экземпляр структуры, находящийся в памяти (со смещением 0x80FC). Регистр ecx указывается на структуру с разобранной информацией об RTF файле. По вышеуказанному смещению (0x80FC) мы понимаем, что размер структуры огромен. Эта структура используется во множестве мест кода. Далее в статье этот базовый адрес, на который указывает регистр ecx, я буду обозначать как RTF parse object.

Рисунок 4: Код, отвечающий за размещение массива

На Рисунке 5 показана процедура копирования памяти, где происходит фактическое заполнение массива. В регистре edi по адресу 0x31D131FF находится индекс конечного массива. Вы можете проверить, что инструкция по адресу 0x31D131F9 получает базовый адрес массива по адресу eax+0x80FC, который содержит адрес массива памяти. В данном случае регистр eax указывает на RTF parse object.

Рисунок 5: Процедура копирования массива

Проблема заключается в том, что при копировании памяти значение индекса, который находится в регистре edi (адрес 0x31D131FF), не сравнивается с максимальным размером массива и происходит перезапись за пределами границ массива памяти. Следующий вопрос: при каких условиях это происходит. Я обнаружил, что каждое контрольное слово lfolevel инициирует заполнение массива и увеличивает значение глобального индекса.

На Рисунке 6 показаны контрольные слова, инициирующие операции с памятью. На рисунке показано 34 контрольных слов, но первоначальный массив может содержать меньшее их количество.

Рисунок 6: Контрольное слово (lfolevel), инициирующее заполнение массива

Содержимое для перезаписи массива

Первые 4 байта, которые копируются в каждый массив, - адреса памяти, где размещается куча. Иногда копируются пустые значения. На Рисунке 7 показан один из участков памяти (0x067d8870), который копируется по индексу 0x1d. Индекс 0x1d находится вне границ первоначального массива, но внутри объектов файла GFX.dll.

Рисунок 7: Содержимое, копируемое в массив

На Рисунке 8 показано, как происходит порча объекта памяти. Как видно из рисунка, перезаписываются байты, находящиеся в файле gfx.dll. Первые 4 байта (те, которые перезаписываются) 8-байтового массива указывают на vtable, что делает возможным последующий запуск кода при вызове методов, которые относятся к объектам GFX.

Рисунок 8: Состояние объекта до и после перезаписи

Содержимое памяти по адресу 0x067d8870 показано на Рисунке 9. Новое содержимое функционирует как объект vtable после того, как произошла порча памяти.

Рисунок 9: Содержимое памяти по адресу 0x067d8870

На Рисунке 10 показан код, который копирует содержимое в адрес 0x067d8870. Копируются 0x30 байт из участка по адресу RTF parse object+0x8104. Регистр ecx при вызове функции memcpy (по адресу 0x31D12F78) указывает на глобальный адрес RTF parse object+0x8104.

Рисунок 10: Процедура копирования содержимого по адресу 0x067d8870

После того как все акты по заполнению массива завершены, финальный участок памяти, относящийся к эксплуатации уязвимости выглядит, как показано на Рисунке 11. В ArrayData находится содержимое, скопированное из RTF parse object+0x8104.

Рисунок 11: Участки памяти, имеющие отношение к эксплуатации уязвимости

Как формируется содержимое RTF parse object+0x8104

Как мы уже знаем, вредоносный файл портит память, перезаписывая туда содержимое, которое берется по адресу RTF parse object+0x8104. Теперь возникает два вопроса: как создается копируемое содержимое, и может ли злоумышленник контролировать формирование этого содержимого. Ответ на второй вопрос – «да». Я уже говорил, что RTF parse object содержит информацию о спарсенном RTF-потоке. Участок памяти в районе адреса RTF parse object+0x8104 используется несколькими контрольными словами. Далее рассматриваются «зоны ответственности» каждого контрольного слова.

Для начала следует отметить, что содержимое RTF parse object+0x8104 изменяется по мере анализа новых контрольных слов. При объяснении я буду приводить пример заполнения массива по индексу 0x1d.

Добавление двойного слова по адресу RTF parse object+0x8104

На Рисунке 12 показаны инструкции, которые устанавливают значение по адресу RTF parse object+0x8104

Рисунок 12: Инструкции, изменяющие содержимое по адресу RTF parse object+0x8104

К этой процедуре имеет отношение контрольное слово levelstartat. На Рисунке 13 показана часть, ответственная за байты, которые записываются из эксплоита. Как вы могли догадаться, число 31611 в шестнадцатеричном виде выглядит как 0x7b7b.

Рисунок 13: Соответствующее контрольное слово

На Рисунке 14 показано 4 байта, записанные по адресу RTF parse object+0x8104.

Рисунок 14: Содержимое по адресу RTF parse object+0x8104 (DWORD)

Добавление одного байта по адресу RTF parse object+0x8108

На Рисунке 15 показаны инструкции, которые записывают содержимое по адресу RTF parse object+0x8108.

Рисунок 15: Инструкции, изменяющие содержимое по адресу RTF parse object+0x8108

К этой процедуре имеет отношение контрольное слово levelnfcn. Из Рисунка 16 видно, что число 232 в шестнадцатеричной системе равно 0xe8. Именно это значение и записывается по адресу RTF parse object+0x8108.

Рисунок 16: Соответствующее контрольное слово

Рисунок 17: Содержимое памяти по адресу RTF parse object+0x8108 (BYTE)

Добавление одного байта по адресу RTF parse object+0x8109

На этот участок памяти влияют контрольные слова levelnorestart, levelold, jclisttab, leveljcn. На Рисунке 18 показан код, модифицирующий содержимое этого участка.

Рисунок 18: Инструкции, отвечающие за изменение участка RTF parse object+0x8109

Байт памяти по этому смещению показан на Рисунке 19.

Рисунок 19: Содержимое памяти по адресу RTF parse object+0x8109

На Рисунке 20 показаны контрольные слова levelnorestart1 (формирует значение 8) и levelold1 (формирует значение 40). Оба контрольных слова формируют значение 0x48, когда над ними производится операция xor по соответствующему адресу.

Рисунок 20: Соответствующие контрольные слова

Добавление содержимого по адресу RTF parse object+0x810A

По адресу RTF parse object+0x810A заносится значение из аргумента контрольного слова levelnumbers. Из Рисунка 21 видно, что первые 5 байт (\’5A’) экранируются и интерпретируются как 0x5A.

Рисунок 21: Соответствующее контрольное слово

Шестнадцатеричное представление байтов показано на Рисунке 22.

Рисунок 22: Шестнадцатеричное представление байтов контрольного слова

На Рисунке 23 показан код, который копирует байты по адресу RTF parse object+0x810A.

Рисунок 23: Инструкции, отвечающие за модификацию участка памяти по адресу RTF parse object+0x810A

Содержимое памяти показано на Рисунке 24.

Рисунок 24: Содержимое памяти по адресу RTF parse object+0x810A

Добавление одного байта по адресу RTF parse object+0x8113

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

Рисунок 25: Инструкции, отвечающие за модификацию памяти по адресу RTF parse object+0x8113

За это изменение отвечает контрольное слово levelfollow (см. рисунок ниже).

Рисунок 26: Соответствующее контрольное слово

Аргумент 39 в шестнадцатеричной системе преобразуется в 27.

Рисунок 27: Содержимое участка памяти по адресу RTF parse object+0x8113 (BYTE)

Добавление двойного слова по адресу RTF parse object+0x8114

За изменение участка памяти по адресу RTF parse object+0x8114 отвечают инструкции, показанные на Рисунке 28.

Рисунок 28: Инструкции, отвечающие за изменение участка памяти по адресу RTF parse object+0x8114

За это изменение отвечает контрольное слово levelspace (см. рисунок ниже).

Рисунок 29: Соответствующее контрольное слово

Число 22873 преобразуется в шестнадцатеричной системе в 0x5959.

Рисунок 30: Содержимое участка памяти по адресу RTF parse object+0x8114 (DWORD)

Добавление двойного слова по адресу RTF parse object+0x8118

На Рисунке 31 показаны инструкции, отвечающие за изменение 4-х байт по адресу RTF parse object+0x8118.

Рисунок 31: Инструкции, отвечающие за изменение участка памяти по адресу object+0x8118

За это изменение отвечает контрольное слово levelindent (см. рисунок ниже)

Рисунок 32: Соответствующее контрольное слово

Число 23130 в шестнадцатеричной системе преобразуется в 0x5a5a.

Рисунок 33: Содержимое участка памяти по адресу RTF parse object+0x8118 (DWORD)

Заключение

Эта уязвимость и эксплоит крайне интересны, поскольку злоумышленник может изменять большинство байтов, используемых при перезаписи памяти. Тот факт, что фальсифицированный объект vtable злоумышленник изменяет на первоначальный, заметно увеличивает устойчивость работы первой фазы эксплоита. Тот факт, что злоумышленник смог контролировать нужные байты при эксплуатации, используя несколько контрольных слов, говорит о том, что у хакера хорошие знания о внутреннем механизме обработки RTF при помощи модуля wwlib.dll.

Устали от того, что Интернет знает о вас все?

Присоединяйтесь к нам и станьте невидимыми!