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

Статья Использование Windows RPC для обхода CFG: анализ образца CVE-2021-26411

Azrv3l

win32kfull
Эксперт
Регистрация
30.03.2019
Сообщения
215
Реакции
539
Общий метод эксплуатации процесса рендеринга браузера: после использования уязвимости для получения произвольного примитива чтения/записи памяти пользовательского режима, vtable объекта DOM/js подделывается, чтобы захватить поток выполнения кода. Затем VirtualProtect вызывается цепочкой ROP для изменения памяти шеллкода на PAGE_EXECUTE_READWRITE, и, наконец, поток выполнения кода переходит к шеллкоду с помощью цепочки ROP. После Windows 8.1 Microsoft представила средство CFG (Control Flow Guard) [1] для проверки косвенного вызова функции, что снижает риск взлома vtable для выполнения кода.

Однако на этом противостояние не закончилось. Появились некоторые новые методы обхода CFG. Например, в chakra/jscript9 поток выполнения кода захватывается путем подделки адреса возврата функции в стеке; в версии 8 для выполнения шелл-кода используется WebAssembly со свойством исполняемой памяти. В декабре 2020 года Microsoft представила технологию смягчения последствий CET(Control-flow Enforcement Technology) [2], основанную на процессоре Intel Tiger Lake в Windows 10 20H1, которая защищает от взлома адрес возврата функции в стеке. Таким образом, как обойти CFG в среде смягчения последствий CET стало новой проблемой для эксплуатации уязвимостей.

Анализируя образец CVE-2021-26411 в естественных условиях, мы обнаружили новый метод обхода защиты от CFG с помощью Windows RPC (Remote Procedure Call) [3]. Этот метод не полагается на цепочку ROP. Создав RPC_MESSAGE, можно выполнить произвольный код, вызвав вручнуюrpcrt4!NdrServerCall2.

Ретроспектива
В моем блоге «CVE-2021-26411: Internet Explorer mshtml use-after-free» проиллюстрирована основная причина: removeAttributeNode() запускает обратный вызов valueOf объекта атрибута nodeValue. Во время обратного вызова clearAttributes() вызывается вручную, что приводит к предварительному освобождению BSTR, сохраненного в nodeValue. После возврата обратного вызова valueOf объект nodeValue не проверяется, что приводит к UAF.

Исправление этой уязвимости в патче Windows March заключается в добавлении проверки индекса перед удалением объекта в функции CAttrArray::Destroy

1.png


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

2.png


Введение и эксплуатация Windows RPC
Windows RPC используется для поддержки сценария распределенных вызовов функций клиент/сервер. На основе Windows RPC клиент может вызывать функции сервера так же, как вызов локальной функции. Базовая архитектура Windows RPC показана следующим образом:

3.png


Программа клиент / сервер передает параметры вызова или возвращаемые значения функции заглушки нижнего уровня. Функция заглушки отвечает за инкапсуляцию данных в формат NDR(Network Data Representation). Связь через библиотеку времени выполнения обеспечивается через rpcrt4.dll.

Ниже приведен пример idl:
C++:
[
    uuid("1BC6D261-B697-47C2-AF83-8AE25922C0FF"),
    version(1.0)
]

interface HelloRPC
{
    int add(int x, int y);
}

Когда клиент вызывает функцию добавления, сервер получает запрос на обработку от rpcrt4.dll и вызывает rpcrt4!NdrServerCall2:

4.png


rpcrt4!NdrServerCall2 имеет только один параметр PRPC_MESSAGE, который содержит важные данные, такие как индекс функции и параметры. Структура RPC_MESSAGE сервера и основная структура вспомогательных данных показаны следующим образом (32 bits):

5.png


Как показано на вышеупомянутом рисунке, в структуре RPC_MESSAGE двумя важными переменными вызова функции являются Buffer и RpcInterfaceInformation. Buffer хранит параметры функции, а RpcInterfaceInformation указывает на структуру RPC_SERVER_INTERFACE. Структура RPC_SERVER_INTERFACE сохраняет информацию об интерфейсе программы сервера, в которой DispatchTable (+ 0x2c) сохраняет указатели интерфейсных функций библиотеки времени выполнения и функции-заглушки, а InterpreterInfo (+ 0x3c) указывает на структуру MIDL_SERVER_INFO. Структура MIDL_SERVER_INFO сохраняет информацию интерфейса IDL сервера, а DispatchTable (+ 0x4) сохраняет массив указателей функций подпрограммы сервера.

Вот пример, представляющий структуру RPC_MESSAGE

Согласно приведенному выше idl, когда клиент вызывает add (0x111, 0x222), серверная программа прерывается на rpcrt4!NdrServerCall2:

6.png


Можно видеть, что дамп памяти динамической отладки согласуется с анализом структуры RPC_MESSAGE, а функция добавления хранится в MIDL_SERVER_INFO.DispatchTable.

Затем мы анализируем, как rpcrt4!NdrServerCall2 вызывает функцию добавления в соответствии с RPC_MESSAGE:

Rpcrt4!NdrServerCall2 вызывает rpcrt4!NdrStubCall2. Rpcrt4!NdrStubCall2 вычисляет адрес указателя функции на основе MIDL_SERVER_INFO.DispatchTable и RPC_MESSAGE.ProcNum и передает указатель функции, параметры функции и длину параметра в rpcrt4!Invoke

7.png


Команда rpcrt4!Invoke, наконец, вызывает стандартную функцию, предоставленную сервером:

8.png


Основываясь на приведенном выше анализе, после достижения произвольного примитива чтения/записи в память мы можем создать поддельный RPC_MESSAGE, установить указатель функции и параметры функции, которые нужно вызвать, и вызвать rpcrt4!NdrServerCall2 вручную, чтобы реализовать выполнение любой функции.

Далее необходимо решить две проблемы:
  1. Как вызвать rpcrt4!NdrServerCall2 в javascript
  2. При наблюдении за вызовом функции серверной подпрограммы в rpcrt4!Invoke:
9.png


Мы видим, что это косвенный вызов функции и есть проверка CFG. Следовательно, нам нужно подумать, как обойти здесь защиту CFG после подделки указателя функции MIDL_SERVER_INFO.DispatchTable.

Давайте сначала решим Проблему 1: как вызвать rpcrt4!NdrServerCall2 в javascript?:
Мы можем заменить указатель функции vtable объекта DOM на rpcrt4!NdrServerCall2. Поскольку rpcrt4!NdrServerCall2 является допустимым указателем, записанным в CFGBitmap, он может пройти проверку CFG. Образец заменяет MSHTML!CAttribute :: normalize на rpcrt4!NdrServerCall2 и вызывает «xyz.normalize ()» в javascript для вызова rpcrt4!NdrServerCall2.

Затем решаем Вопрос 2: Как обойти защиту CFG в rpcrt4! NdrServerCall2?
Метод в примере:
  1. Используйте поддельные RPC_MESSAGE и rpcrt4!NdrServerCall2 для вызова VirtualProtect и измените атрибут памяти RPCRT4!__ guard_check_icall_fptr на PAGE_EXECUTE_READWRITE
  2. Замените указатель ntdll!LdrpValidateUserCallTarget, сохраненный в rpcrt4!__guard_check_icall_fptr, на ntdll!KiFastSystemCallRet, чтобы отключить проверку CFG в rpcrt4.dll
  3. Восстановить атрибут памяти RPCRT4!__guard_check_icall_fptr
JavaScript:
function killCfg(addr) {
  var cfgobj = new CFGObject(addr)
  if (!cfgobj.getCFGValue())
    return
  var guard_check_icall_fptr_address = cfgobj.getCFGAddress()
  var KiFastSystemCallRet = getProcAddr(ntdll, 'KiFastSystemCallRet')
  var tmpBuffer = createArrayBuffer(4)
  call2(VirtualProtect, [guard_check_icall_fptr_address, 0x1000, 0x40, tmpBuffer])
  write(guard_check_icall_fptr_address, KiFastSystemCallRet, 32)
  call2(VirtualProtect, [guard_check_icall_fptr_address, 0x1000, read(tmpBuffer, 32), tmpBuffer])
  map.delete(tmpBuffer)
}

После решения двух проблем поддельный RPC_MESSAGE можно использовать для вызова любого указателя функции, включая буфер, хранящий шелл-код, поскольку проверка CFG в rpcrt4.dll была прервана. Наконец, образец записывает шелл-код в расположение msi.dll+0x5000 и, наконец, вызывает шелл-код через rpcrt4!NdrServerCall2

JavaScript:
var shellcode = new Uint8Array([0xcc])
var msi = call2(LoadLibraryExA, [newStr('msi.dll'), 0, 1]) + 0x5000
var tmpBuffer = createArrayBuffer(4)
call2(VirtualProtect, [msi, shellcode.length, 0x4, tmpBuffer])
writeData(msi, shellcode)
call2(VirtualProtect, [msi, shellcode.length, read(tmpBuffer, 32), tmpBuffer])
call2(msi, [])

Скриншот эксплуатации:

10.png


Некоторые мысли
Новый метод обхода защиты от CFG за счет использования Windows RPC, представленного в CVE-2021-26411 в исходном образце. Эта технология эксплуатации не требует построения цепочки ROP и выполнения произвольного кода напрямую с помощью поддельного RPC_MESSAGE. Эта технология эксплуатации проста и стабильна. Разумно полагать, что это станет новой и эффективной технологией эксплуатации, позволяющей обойти защиту от CFG.

Ссылки
[1] https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard
[2] https://windows-internals.com/cet-on-windows/
[3] https://docs.microsoft.com/en-us/windows/win32/rpc/rpc-start-page

От ТС
За оригиналом сюда
Спасибо weaver за материал

Интересный способ обхода CFG, а главное простой и действенный.

Перевод:
Azrv3l cпециально для xss.pro
BTC: bc1qs2fk7zftnwwhhdrw9ge6ncxrspeyta7dymjwkj
ETH: 0xEb8CdE54aBaA7186E9dB8A27f6898C9F02397bab
 


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