Блог 360 Total Security

Анализ новых проблем с использованием патча CVE-2018-8174 и 0-day уязвимости VBScript

В мае 2018 года Advanced Threat Response Team of 360 Core Security Division обнаружил атаку APT с использованием 0-day уязвимости и захватила первый в мире вредоносный офисный образец, который использует 0-day уязвимость браузера

В образце была использована уязвимость use-after-free в движке VBScript, которая была исправлена Microsoft как CVE-2018-8174 в обновлении для системы безопасности в мае 2018 года, подробный анализ этого бага может быть найден по адресу:https://blog.360totalsecurity.com/en/analysis-cve-2018-8174-vbscript-0day-apt-actor-related-office-targeted-attack/

После тщательного анализа патча CVE-2018-8174 мы обнаружили, что исправление не было таким полным, и все еще существуют аналогичные проблемы, которые могут быть использованы для обеспечения надежного удаленного выполнения кода в движке VBScript. Мы сразу сообщили о проблемах, и Microsoft обратилась к ним с новым исправлением (CVE-2018-8242) в обновлении для системы безопасности в июле 2018 года.

В этом блоге мы расскажем о новых проблемах с использованием патча CVE-2018-8174, который мы нашли. Мы также обсудим, как мы используем новый баг и получаем надежное удаленное выполнение кода в дальнейшем блоге (часть II).

Исходный 0-day ITW

Прежде чем начать рассказать новый баг, давайте сначала рассмотрим старый баг. PoC для исходного бага выглядит следующим образом:

Вот как работает poc:
1. Он определяет класс VBScript с именем | cla1 |, класс имеет функцию | Class_Terminate | , которая будет вызываться каждый раз, когда экземпляр | cla1 | разрушается.
2. Он определяет vbscript SafeArray | arr | и установите первый элемент массива как экземпляр | cla1 |.
3. Он вызывает «erase» в массиве, который сначала очистит все элементы массива, а затем освободит весь SafeArray, включая его «внутренний буфер».
4. Поскольку первый элемент массива является экземпляром | cla1 |, когда он очищается, обратный вызов| Class_Terminate | будет вызван.
5. Внутри обратного вызова мы снова получаем референс на элемент, выполняя: Set o = arr (0)
6. После возврата из обратного вызова, arr (0) будет освобожден.
7. Сейчас мы имеет референс на освобожденный объект, который дает нам баг use-after-free .

Исправление в мае 2018 года

Исправление Microsoft для этого бага был довольно простым:

В oleaut32! VariantClear, если он очищает вариант VT_DISPATCH (Object), он сначала настраивает тип варианта VT_EMPTY перед запуском | Class_Terminate |, как указано ниже:

И псевдокод патча выглядит так:

if (variant is a dispatch object) {
variant.vt = VT_EMPTY;
Call VBScriptClass::Release which will call the class Terminate function.
}

Сейчас с этим патчем исходный PoC больше не будет работать, потому что когда вы пытаетесь захватить рефренс на элемент | arr (0) | в функции обратного вызова со следующей фразой в poc:

Set o = arr(0)

Вы получите пустой вариант, потому что тип варианта уже установлен на VT_EMPTY, поэтому use after free не произойдет.

Является ли исправление идеальным?

Давайте рассмотрим причину этой уязвимости:

Когда мы стираем SafeArray, он очищает элементы в массиве один за другим. Проблема в том, что мы все равно можем получить доступ к SafeArray, когда элементы очищаются, используя | Class_Terminate |.

Здесь «Доступ» к SafeArray означает, что мы можем:
1. Извлеките элементы из массива (который используется в исходном 0-day poc)
2.Введите элементы в массив.
3. Модифицируйте внутренние данные массива, включая свободный внутренний буфер массива.

Сейчас давайте вернемся к исправлению: патч не позволяет нам прочитать элемент, который очищается в обратном вызове. Легко заметить, что этот патч (частично) предотвращает первый шаблон, но не может адресовать другие 2 шаблона.

Посмотрим, сможем ли мы найти новые проблемы с использованием этих двух шаблонов после патча CVE-2018-8174.

Покажи мне новые 0-day
Новая проблема 1 — SafeArray Double Free

Попробуем шаблон 3, чтобы увидеть, что произойдет, если мы попытаемся удалить внутренний буфер SafeArray внутри обратного вызова | Class_Terminate | с новым poc:

Здесь мы немного изменили исходный poc, попытались стереть массив снова внутри | Class_Terminate |, к нашему удивлению, новый poc разбился немедленно:

(14c8.258): Break instruction exception – code 80000003 (!!! second chance !!!)

eax=00000000 ebx=00000000 ecx=000014c8 edx=00000000 esi=65789d80 edi=00000000

eip=6578cd6f esp=0727ca14 ebp=0727ca38 iopl=0 nv up ei pl zr na pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

verifier!VerifierStopMessage+0x27f:

6578cd6f 837df800 cmp dword ptr [ebp-8],0 ss:0023:0727ca30=00000000

0:006> k

ChildEBP RetAddr

WARNING: Stack unwind information not available. Following frames may be wrong.

0727ca38 6578ab0c verifier!VerifierStopMessage+0x27f

0727ca9c 6578794d verifier!VerifierDisableFaultInjectionExclusionRange+0x414c

0727cb00 65787aa5 verifier!VerifierDisableFaultInjectionExclusionRange+0xf8d

0727cb24 65787d30 verifier!VerifierDisableFaultInjectionExclusionRange+0x10e5

0727cb40 65789e10 verifier!VerifierDisableFaultInjectionExclusionRange+0x1370

0727cb5c 779616d9 verifier!VerifierDisableFaultInjectionExclusionRange+0x3450

0727cbbc 778b8c0a ntdll!RtlpNtSetValueKey+0x3089

0727ccc0 778b64b8 ntdll!RtlFreeHeap+0x2eaa

0727cd10 7553d83b ntdll!RtlFreeHeap+0x758

0727cd24 753cc819 combase!CoCreateFreeThreadedMarshaler+0x1362b

0727cd40 753cc8a4 OLEAUT32!_SafeArrayFreeData+0x34

0727cd4c 753ccb52 OLEAUT32!_SafeArrayReleaseDescriptor+0x38

0727cd5c 5e02f348 OLEAUT32!SafeArrayReleaseDescriptor+0x12

0727cd68 5e034c1e vbscript!AutoVariantRef::ArrayPinnableData::Unpin+0x21

0727cd6c 5e034c12 vbscript!VbsErase+0xee

0727cda8 5dfec837 vbscript!VbsErase+0xe2

Вы можете видеть, что верификатор обнаружил критическую ошибку при вызове RtlFreeHeap для освобождения блока кучи, что обычно указывает на потенциальное повреждение кучи. Обратите внимание, что проблема запускается с | VbsErase | в callstack.

Давайте проверим освобожденный блок кучи, который вызвал предупреждение верификатора:

address 041b4fd8 found in

_DPH_HEAP_ROOT @ 701000

in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)

40b3f08: 41b4000 2000

65789e42 verifier!VerifierDisableFaultInjectionExclusionRange+0x00003482

779616d9 ntdll!RtlpNtSetValueKey+0x00003089

778b8c0a ntdll!RtlFreeHeap+0x00002eaa

778b64b8 ntdll!RtlFreeHeap+0x00000758

7553d83b combase!CoCreateFreeThreadedMarshaler+0x0001362b

753cc819 OLEAUT32!_SafeArrayFreeData+0x00000034

753cc8a4 OLEAUT32!_SafeArrayReleaseDescriptor+0x00000038

753ccb52 OLEAUT32!SafeArrayReleaseDescriptor+0x00000012

753741f7 OLEAUT32!SafeArrayDestroyDescriptor+0x00000087

5e034be6 vbscript!VbsErase+0x000000b6

Вы можете видеть, что блок кучи передан в | RtlFreeHeap | ,который уже был освобожден ранее в другом вывозе к VbsErase.

Сейчас совершенно ясно, что это двойная свободная уязвимость. В нашем poc, когда первый вызов VbsErase пытается освободить память дескриптора SafeArray, он уже был неожиданно освобожден вторым вызовом VbsErase в нашем обратном вывозе | Class_Terminator |.

Эта двойная свободная уязвимость довольно проста, однако для ее использования требуется некоторый метод. Я буду подробно обсуждать о этой уязвимости, включая подробный анализ и как написать poc-эксплойт для него в следующем блоге. Вы можете попытаться написать poc-эксплоит самостоятельно, если вы заинтересованы в нем.

Новая проблема 2: Объект Use After Free,вызванный рефренсом,привел к переполнению отсчета

Сейчас у нас есть новая двойная свободная уязвимость. Но мы не хотели останавливаться здесь и отдыхать, наш мозговой штурм продолжался.

О шаблонае 2? Можем ли мы сделать некоторые проблемы, написав элементы на SafeArray внутри обратного вывоза| Class_Terminate |?

Ответ — да. Проблема не так очевидна: мы просто пишем некоторые элементы на SafeArray, чья память будет освобождена, мы не напишем границу массива; мы не напишем в уже освобожденной памяти. Кажется, не имеет большого значения?

Однако рассмотрите следующую ситуацию:

1. У нас есть SafeArray с 2 элементами.

2. Мы вызываем erase на SafeArray.

3. Элементы в SafeArray будут очищаться один за другим, сначала очистят arr (0), а затем очистят arr (1) »

4. При очистке arr (1) мы можем запустить одни обратный вывоз | Class_Terminate |. И внутри обратного вызова мы сохраняем объект arr (0) : Set arr (0) = SomeObject. В VBScript сохранение объекта в SafeArray увеличит отсчет рефренсов этого объекта, поэтому мы получим SomeObject.ref_cnt + = 1. Отсчет рефенсов будет уменьшен, когда мы очистим объект от массива, в нормальном дело.

5. Сейчас, когда arr (1) очищен, все элементы в SafeArray очищены. Он не вернется к очистке arr (0), потому что arr (0) уже очищен до очистки arr (1). Затем он сразу освободит память внутреннего буфера SafeArray.

6. Таким образом, SomeObject.ref_cnt не будет уменьшаться. Это вызовет утечку рефренсов на объекте.

7. Отсчет рефренсов объекта VBScript составляет 32 бита. Путем триггера утечки отсчета рефренсов, мы можем, наконец, переполнить 32-битное отсчет рефренсов, и отсчет рефренсов станет очень маленьким. Затем мы можем освободить объект, очистив несколько рефренсов, в то время как у нас все еще есть много рефренсов, указывающих на этом объекте. Это означает, что с этой уязвимостью мы можем создать висячий указатель на уже освобожденный объект vbscript и получить доступ к свободному объекту в любое время, когда мы хотим, что довольно легко использовать.

Полный poc указан ниже:

Вышеуказанный PoC взял ~ 10 минут, чтобы потерпеть крах на моей тестовой машине. Ошибка при попытке доступа к уже освобожденному объекту в oleaut32! VariantClear: (58.1154): Нарушение прав доступа — код c0000005 (!!! второй шанс !!!)

eax=00000000 ebx=071cd010 ecx=0e9d2fc0 edx=04000000 esi=00000000 edi=00000009

eip=7537bbce esp=071ccfc0 ebp=071ccfe0 iopl=0 nv up ei pl nz na pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206

OLEAUT32!VariantClear+0xfe:

7537bbce 8b01 mov eax,dword ptr [ecx] ds:0023:0e9d2fc0=????????

address 0e9d2fc0 found in

_DPH_HEAP_ROOT @ 3fb1000

in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)

dab3fa4: e9d2000 2000

65789e42 verifier!VerifierDisableFaultInjectionExclusionRange+0x00003482

779616d9 ntdll!RtlpNtSetValueKey+0x00003089

778b8c0a ntdll!RtlFreeHeap+0x00002eaa

778b64b8 ntdll!RtlFreeHeap+0x00000758

752d77c5 msvcrt!free+0x00000065

5f430619 vbscript!VBScriptClass::`scalar deleting destructor’+0x00000019

5f476fca vbscript!VBScriptClass::CheckDelete+0x00000028

7537bbb9 OLEAUT32!VariantClear+0x000000e9

60db19e2 IEShims!IEShims_SetRedirectRegistryForThread+0x00005472

5f43224e vbscript!AssignVar+0x0000013e

Исправление для системы безопасности в июле

Мы сообщили о 2 новых уязвимостях для Microsoft, и они обратились к ним в одном CVE (CVE-2018-8242) в июльском обновлении для системы безопасности.

Быстрый анализ показывает, что на этот раз они исправили новые проблемы следующим образом:

Сейчас для операций VBScript, которые освободят внутренний буфер SafeArray (erase, redim), прежде чем очищать элементы в массиве, сначала установит внутренний дескриптор SafeArray равным null. После этого исправления вы не сможете получить доступ (читать / писать / освободить) к SafeArray внутри | Class_Terminate | снова, потому что дескриптор уже очищен при вызове обратного вызова.

Этот патч прекрасно фиксировал новые проблемы, о которых мы сообщали.

Кстати, это не проблема безопасности, заметили ли вы, что вышеупомянутый патч ввел проблему с утечкой ресурсов (памяти)? Попытайтесь понять это.

Некоторые мысли

Это дело — интересное приключение для меня. Анализ poc / патч — подумать — поиск нового пути к сбою , каждый шаг приносит мне массу удовольствия.

То, что я узнал (снова) из этого дела, что анализ исправлений безопасности очень важен для охотника за ошибками. Я видел много случаев, когда исправление безопасности не помогло устранить все возможные пути уязвимости, и мы все еще можем найти способ запуска этой уязвимости после патча. И я также увидел случаи, когда исправление безопасности может указать новые возможности атаки или новые ошибки непосредственно в программное обеспечение. И я думаю, что разработчики и исследователи безопасности должны потратить больше времени на исправления безопасности.