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

Статья CVE-2021-26411: Internet Explorer mshtml use-after-free

Azrv3l

win32kfull
Эксперт
Регистрация
30.03.2019
Сообщения
215
Реакции
539
Вступление
В январе этого года Google и Microsoft, опубликовали блоги, раскрывающие атаки на исследователей безопасности со стороны APT-группы из NK [1] [2]. Уязвимость в Internet Explorer, использованная в этой атаке, была исправлена как CVE-2021-26411 в патче Microsoft во вторник в этом месяце [3]. Уязвимость срабатывает, когда пользователи уязвимой версии Internet Explorer получают доступ к вредоносной ссылке, созданной злоумышленниками, что приводит к удаленному выполнению кода.

Анализ первопричин
POC, который может вызвать уязвимость, приведён ниже:
JavaScript:
<script>
var elem = document.createElement('xxx');
var attr1 = document.createAttribute('yyy');
var attr2 = document.createAttribute('zzz');

var obj = {};
obj.valueOf = function() {
    elem.clearAttributes();
    return 0x1337;
};

attr1.nodeValue = obj;
attr2.nodeValue = 123;
elem.setAttributeNode(attr1);
elem.setAttributeNode(attr2);
elem.removeAttributeNode(attr1);
</script>

Процесс выполнения PoC:
  1. Создаётся 1 объект элемента HTML (elem) и 2 объекта атрибута HTML (attr1 и attr2)
  2. Присваиватся значения двум объектам атрибута nodeValue, где nodeValue attr1 указывает на объект, функция valueOf которого перегружена.
  3. Устанавливается значение Element object elem для двух объектов Attribute attr1 и attr2.
  4. Вызывается elem.removeAttributeNode (attr1), чтобы удалить attr1 из elem.
  5. Метод removeAttributeNode запускает обратный вызов функции valueOf, во время которой вызывается clearAttributes() для очистки всех объектов атрибутов (attr1 и attr2) объекта elem.
  6. Когда обратный вызов valueOf возвращается, процесс IE Tab вылетает при разыменовании нулевого указателя:
1.png


Основываясь на приведенном выше анализе потока PoC, можно сделать вывод, что причиной сбоя является проблема двойного освобождения, вызванная функцией clearAttributes() в обратном вызове valueOf. После очистки всех объектов атрибута объекта элемента он возвращается к интерпретатору (прерывая операцию атомарного удаления на уровне интерпретатора) и снова освобождает объект атрибута, что приводит к двойному освобождению. Но есть еще некоторые детали, которые нужно проработать:
  1. Почему removeAttributeNode () запускает обратный вызов valueOf на уровне скрипта?
  2. Почему исключение разыменования нулевого указателя все еще возникает на объектах DOM, которые защищены изолированной кучей и механизмом отложенного освобождения?
  3. Как эксплуатировать это исключение разыменования нулевого указателя?
Ответ на вопрос 1, начинается со статического анализа
1. Функция removeAttributeNode() в mshtml.dll обрабатывается функцией MSHTML!CElement::ie9_removeAttributeNode. Он вызывает внутри себя MSHTML!CElement::ie9_removeAttributeNodeInternal, который является основной реализацией removeAttributeNode():

2.png


2. MSHTML!CElement::ie9_removeAttributeNodeInternal дважды вызывает CBase::FindAAIndexNS для поиска индексов объекта атрибута и объекта атрибута nodeValue в массиве VARINAT CAttrArray (+0x8)

3.png


3. Когда индекс объекта атрибута найден, объект атрибута извлекается через CBase::GetObjectAt:

4.png


4. Когда индекс nodeValue объекта атрибута найден, он вызывает функцию CBase::GetIntoBSTRAt для преобразования nodeValue в BSTR и сохраняет значение BSTR в CAttribute.nodeValue(+0x30). В это время будет запущен обратный вызов valueOf!

5.png


5. Затем он дважды вызывает CBase::DeleteAt, чтобы удалить объект атрибута и объект атрибута nodeValuev(здесь необходимо обратить внимание на существование одного вызова CBase::FindAAIndexNS между двумя вызовами DeleteAt, чтобы снова найти индекс attr1.nodeValue):

6.png


6. CBase::DeleteAt проверяет индекс объекта, который необходимо удалить. Если он не равен -1, он вызывает CAttrArray::Destroy для выполнения работы по очистке:

7.png


7. CAttrArray::Destroy вызывает CImplAry::Delete, чтобы изменить счетчик CAttrArray(+0x4) и изменить порядок соответствующего массива VARIANT (+0x8), затем вызывает CAttrValue::Free, чтобы освободить объект атрибута в конце:

8.png


Затем мы наблюдаем за процессом обратного вызова вопроса 1 и анализируем вопрос 2 с помощью динамической отладки
1. Макет памяти объекта элемента до входа в функцию узла removeAttribute:

9.png


2. Результат двух вызовов функции CBase::FindAAIndexNS после ввода MSHTML!CElement::ie9_removeAttributeNodeInternal:

11.png


3. CHase::GetIntoBSTRAt запускает значение Callback в скрипте:

12.png


4. Структура памяти объекта elem после вызова clearAttributes() в обратном вызове valueOf:

13.png


Сравнивая c шагом 1, вы можете увидеть, что после clearAttributes() значение elem.CAttrArray.count(+ 0x4) уменьшается до 1, и происходит операция копирования памяти в массиве CAttrArray VARIANT (+ 0x8): attr2 индекса 4 копируется в предыдущий индекс в порядке (сдвиг), который соответствует логике CImplAry::Delete:

14.png


5.Когда обратный вызов возвращается, в первом вызове CBase::DeleteAt(): Он проверяет индекс объекта VARIANT, который сначала ожидал удаления. Вот значение индекса attr1 2, которое найдено в первом поиске CBase::FindAAIndexNS:

15.png


После прохождения проверки индекса вызывается CAttrArray::Destroy для начала очистки.

6. Когда обратный вызов вернется, во втором вызове CBase::DeleteAt(): Вспоминая часть статического анализа, выполняется один CBase::FindAAIndexNS между двумя CBase::DeleteAt, чтобы снова найти индекс attr1.nodeValue. При нормальных обстоятельствах ожидается, что CBase::FindAAIndexNS вернет 1. Однако, поскольку обратный вызов прерывает атомарную операцию на уровне интерпретатора и заранее освобождает все атрибуты elem, здесь возвращается неожиданный -1:

16.png


Согласно статическому анализу функции CBase::DeleteAt, для случая, когда индекс равен -1, будет выдано исключение:

17.png


После возврата указатель объекта CAttrArray указывает на память, установленную как NULL, что, наконец, вызывает исключение разыменования нулевого указателя:

18.png


Вот картинка, объясняющая весь процесс:

19.png


От разыменования нулевого указателя до примитива чтения/записи
Наконец, давайте обратимся к вопросу 3:
Как эксплуатировать это исключение разыменования нулевого указателя?

Как известно, исключение разыменования нулевого указателя в пользовательском режиме сложно эксплуатировать, но эта уязвимость имеет свою особенность. Из предыдущего анализа мы знаем, что исключение разыменования нулевого указателя возникает во второй операции DeleteAt, но первая операция DeleteAt уже имеет неверное предположение: массив VARIANT (+ 0x8), сохраненный в CAttrArray, был переназначен в обратном вызове:

20.png


В это время первая операция DeleteAt с index = 2 по ошибке освободит объект attr2:

21.png


Итак, на самом деле проблема UAF скрыта исключением разыменования нулевого указателя.

Следующим шагом нужно подумать о том, как использовать этот UAF. Учитывая, что объект элемента DOM защищен изолированной кучей и механизмом отложенного освобождения, он должен выбрать объект, который может напрямую выделять память системным распределителем кучи, например чрезвычайно длинный BSTR.

Пересмотренный PoC:
JavaScript:
<script>
var elem = document.createElement('xxx');
var attr1 = document.createAttribute('yyy');
//var attr2 = document.createAttribute('zzz');

var obj = {};
obj.valueOf = function() {
    elem.clearAttributes();
    return 0x1337;
};

attr1.nodeValue = obj;
//attr2.nodeValue = 123;

elem.setAttributeNode(attr1);
//elem.setAttributeNode(attr2);
elem.setAttribute('zzz', Array(0x10000).join('A'));

elem.removeAttributeNode(attr1);
</script>

Схема памяти элемента перед вызовом removeAttributeNode():

22.png


После clearAttributes() массив VARIANT CAttryArray копируется вперед, а BSTR немедленно освобождается:

23.png


В первой операции DeleteAt снова осуществляется доступ к памяти BSTR атрибута «zzz» с индексом = 2, что приводит к UAF:

24.png


Здесь мы можем получить дыру в памяти размером 0x20010 байтов, после обратного вызова valueOf clearAttributes():

Тогда есть два вопроса, на которые нужно ответить:
  1. Какой объект можно выбрать, чтобы он занимал пустую память?
  2. Как обойти исключение разыменования нулевого указателя, вызванное проверкой «index = -1» во втором DeleteAt после успешного заполнения пустой памяти?
Ссылаясь на коды exp, опубликованные ENKI [4], мы можем использовать объект ArrayBuffer с размером 0x20010 байт, чтобы занять пустую память, и повторно установить атрибут 'yyy' в элементе перед возвратом обратного вызова для обхода исключениея разыменования нулевого указателя:

25.png


Наконец, после того, как возвращается элемент elem.removeAttributeNode(attr1) , получается висящий указатель hd2.nodeValue размером 0x20010 байт.
Есть много путей для последующей эксплуатации. Основные идеи:
  1. Используйте Scripting.Dictionary.items(), чтобы занять дыру в памяти, и используйте висячий указатель hd2.nodeValue для утечки поддельного адреса ArrayBuffer
  2. Воспользуйтесь утечкой метаданных поддельного ArrayBuffer, изменените ArrayBuffer buffer = 0, length = 0xffffffff и получите новый поддельного ArrayBuffer
  3. Создайте DataView, который ссылается на новый поддельный ArrayBuffer для достижения произвольного примитива чтения и записи в память
26.png


Вместо заключения
Microsoft удалила уязвимость браузера IE из программы вознаграждений за уязвимости и начала выпускать новый браузер Edge на основе Chromium. Однако в последние годы APT, использующие уязвимости браузера IE, все еще активны. От движка vbscript в 2018 году, движка jscript в 2019 году до движка jscript9 в 2020 году злоумышленники постоянно ищут новые поверхности для атак. Раскрытие CVE-2021-26411 снова привлекло внимание общественности к проблемам безопасности механизма mshtml, которые были обнаружены с большим количеством проблем UAF. Мы считаем, что атаки на Internet Explorer не остановлены.

Ссылки
[1] https://blog.google/threat-analysis-group/new-campaign-targeting-security-researchers/
[2] https://www.microsoft.com/security/blog/2021/01/28/zinc-attacks-against-security-researchers/
[3] https://msrc.microsoft.com/update-guide/en-us/vulnerability/CVE-2021-26411
[4] https://enki.co.kr/blog/2021/02/04/ie_0day.html

От ТС
Оригинал тут.
Давно пытаюсь влится в тему экплуатации браузеров, эта статья мне безусловно помогла)

Перевод:
Azrv3l cпециально для xss.pro
 
Последнее редактирование:


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