• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья CVE-2020-1380: анализ недавно исправленного 0day в IE

yashechka

Генератор контента.Фанат Ильфака и Рикардо Нарвахи
Эксперт
Регистрация
24.11.2012
Сообщения
2 344
Реакции
3 563
В рамках августовского вторника исправлений Microsoft пропатчила одну уязвимость нулевого дня, нацеленную на Internet Explorer 11, а именно CVE-2020-1380. Это ошибка UAF в движке JavaScript Internet Explorer, jscript9.dll. За последние несколько лет мы заметили, что атаки нулевого дня на Internet Explorer обычно эксплуатируют vbscript.dll и jscript.dll для запуска шелл-кода. На этот раз цель сменилась на jscript9.dll и использовала движок Just-In-Time (JIT) современного движка JavaScript, чтобы вызвать ошибку, поэтому я решил погрузиться в JIT-движок jscrtip9.dll, чтобы попытаться выяснить основную причину ошибки CVE-2020-1380.

Обзор конвейера выполнения Jscirpt9.dll

Jscript9.dll - это движок JavaScript по умолчанию, который используется для замены jscript.dll, начиная с Internet Explorer 9. На рисунке 1 показан конвейер выполнения jscript9.dll.

1.png



Как правило, выполнение исходного кода JavaScript в jscript9.dll состоит из пяти основных шагов:

- Парсер анализирует исходный код JavaScript, чтобы получить Абстрактное Синтаксическое Дерево (AST).
- ByteCodeGenerator проходит через AST и генерирует ByteCode.
- Интерпретатор - это виртуальная машина, выполняющая ByteCode. Данные профиля, такие как информация о типе, собираются при выполнении ByteCode.
- Когда некоторые фрагменты кода вызываются несколько раз, например, в цикле for, интерпретатор отправляет данные ByteCode и профиля на движок бэкэнда Just-In-Time (JIT) для генерации машинного кода, а затем заменяет точку входа ByteCode на сгенерированный машинный код.
- При выполнении машинного кода, если какой-либо статус нарушает предположение о профиле, машинный код отправляет интерпретатору запрос о помощи для повторного выполнения ByteCode, чтобы избежать каких-либо проблем с безопасностью.

Расположение машинного кода

Прежде чем говорить об уязвимости, нам сначала нужно найти машинный код, сгенерированный JIT-движком. Цикл for обычно вызывает механизм JIT в интерпретаторе. На рисунке 2 показан простой код JavaScript, который может запускать JIT:

2.png


Когда количество циклов превышает некоторые пороговые значения (0x32 во фрагменте кода на рисунке 3), тело цикла и функция внутреннего вызова opt будут отправлены в очередь бэкэнда движка JIT для генерации оптимизированного машинного кода:

3.png


Поток внутреннего механизма JIT получает задание из очереди и, наконец, вызывает jscript9! Func :: Codegen для генерации оптимизированного машинного кода:

4.png


Бэкэнд-движок JIT выполняет несколько шагов для создания оптимизированного машинного кода, таких как: построение промежуточного представления (IR), встраивание, построение графа потока управления (CFG), анализ потока данных, уменьшение, выделение регистров, компоновка, кодирование и так далее:

5.png



Когда сгенерирован оптимизированный машинный код, он будет использоваться для замены тела цикла ByteCode. Когда цикл for вызывается следующим, машинный код будет вызываться вместо этого в функции Js::InterpreterStackFrame:: CallLoopBody:


6.png


Наконец, машинный код тела цикла вызовет машинный код функции внутреннего вызова opt, который мы можем видеть здесь:

7.png


Анализ первопричин CVE-2020-1380

PoC CVE-2020-1380 показан на рисунке 8:

8.png

Следующие шаги могут вызвать ошибку:

- Цикл for отправляет функцию opt механизму JIT.
- В функции opt три строки "операции аргументов" могут установить value2 в value1, тогда Float32Array первый элемент arr[0] Float32Array устанавливается value1.
- После отправки функции opt механизму JIT она изменяет аргумент value2 с целого числа 0x1337 на объект, который имеет функцию обратного вызова valueOf.
- В последнем вызове функции opt, поскольку аргумент "flag" установлен в 0, базовый блок "if (flag == 1)" не выполняется, что устанавливает value2 в arr[0]. Поскольку объект заменяет value2, происходит неявное преобразование типа, тогда функция обратного вызова valueOf может быть вызвана в машинном коде.

JavaScript - это динамический язык, в котором тип или свойство могут быть неявно преобразованы. Сгенерированный машинный код, который выполняет неявные вызовы JavaScript напрямую без какой-либо проверки, не заслуживает доверия. Jscript9.dll использует функцию ExecuteImplicitCall, чтобы сделать неявный вызов JavaScript безопасным.

Во-первых, мы меняем три строки "arguments operation" на "arguments [0] = value2", что имеет тот же эффект. На рисунке 9 показан сгенерированный фрагмент кода JIT.

9.png


Перед вызовом функции преобразования типов jscript9!Js::JavascriptConversion::toFloat_Helper некоторые значения устанавливаются на флаги, хранящиеся в адресах 0x140F3F68 и 0x140F3E86 отдельно. Флаг, хранящийся по адресу 0x140F3F68 - это ImplicitCallFlags, а другой флаг, хранящийся в 0x140F3E86 - это DisableImplicitFlags. DisableImplicitFlags имеет значение 3 (DisableImplicitCallFlag | DisableImplicitExceptionFlag), что означает, что преобразование типа в float не допускается при неявном вызове из кода JavaScript, который должен быть вызван, например valueOf.

Функция Js::JavascriptConversion::toFloat_Helper проверяет тип ввода, чтобы решить, какой путь преобразования типа выбрать. Поскольку value2 является объектом, вызывается Js::DynamicObject::toPrimitive, а затем, наконец, вызывается ExecuteImplicitCall:


10.png


ExecuteImplicitCall проверяет DisableImplicitFlags; если значение не равно 0, неявный вызов JavaScript не будет вызываться и вернет undefined напрямую. Наконец, машинный код перейдет к интерпретатору и безопасно вызовет неявный вызов в интерпретаторе:

11.png


Однако, когда три строки "операции аргументов" используются для замены "arguments[0] = value2", мы видим, что сгенерированный машинный код вызывает Js::JavascriptConversion::toFloat_Helper напрямую, без установки DisableImplicitFlags:


12.png


Наконец, неявный вызов valueOf будет вызываться непосредственно из машинного кода. Злоумышленник может использовать эту возможность обратного вызова без проверки, чтобы вызвать уязвимость UAF, например стерилизовать память ArrayBuffer TypedArray рабочим потоком.

13.png


Меня заинтересовало, почему три строки "операции с аргументами" могут устранить машинный код установки DisableImplicitFlags. Я думаю, что основной причиной является ошибка вывода типа arguments[0] в бэкэнде на этапе JIT GlobOpt. Механизм JIT не знает побочного эффекта Array.prototype.push, который можно использовать для изменения типа аргументов [0]. Тип arguments[0] должен быть уничтожен после операции Array.prototype.push, чтобы избежать проблемы с ошибкой вывода этого типа.

Эту ошибку могут вызвать другие методы, например, использование Array.prototype.splice или использование Float64Array для вызова пути преобразования Js::JavascriptConversion::toNumber_Helper. Эта ошибка также была исправлена в августовском патче.

14.png


Заключение

В последние несколько лет атаки нулевого дня, нацеленные на Internet Explorer, обычно используют уязвимости vbscrpt.dll и jscript.dll. CVE-2020-1380 является особенным, поскольку он нацелен на JIT-движок jscript9.dll. Уязвимости JIT - распространенная проблема в современных движках JavaScript, таких как V8, JavascriptCore, Spidermonkey и Chakra.

Возможно, злоумышленники теперь выбирают целью JIT-движок Internet Explorer.

Источник https://www.trendmicro.com/en_us/re...0-analysis-of-recently-fixed-ie-zero-day.html
Автор перевода: yashechka
Переведено специально для https://xss.pro
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх