Эксплуатация уязвимостей уровня ядра в ОС Windows. Часть 1 – Настройка рабочей среды
В данном цикле статей мы рассмотрим написание эксплоитов уровня ядра в ОС Windows.
Автор: Mohamed Shahat
Эта серия статей появилась по двум причинам. Во-первых, мне нравится работать с проектом HackSysExtremeVulnerableDriver. Во-вторых, я получил массу пожеланий, чтобы осветить эту тему.
Весь код, используемый при написании этой серии, находится в моем репозитории.
В данном цикле статей мы рассмотрим написание эксплоитов уровня ядра в ОС Windows. Важно отметить, что мы будем иметь дело с известными уязвимостями, и в реверс-инжиниринге нет необходимости (по крайней мере, для драйвера).
Предполагается, что после ознакомления со всеми статьями вы будете знать все наиболее распространенные классы брешей и методы эксплуатации, а также сможете портировать эксплоиты с архитектуры x86 на архитектуру x64 (если возможно) и ознакомитесь с новыми методами защиты в Windows 10.
Схема отладки ядра
В отличие от отладки на уровне пользователя, когда приостанавливается выполнение отдельного процесса, на уровне ядра задействуется вся система, и мы не сможем воспользоваться этим методом. Соответственно, нужна отдельная отладочная машина, которая сможет осуществлять коммуникацию с системой, где отлаживается ядро, просматривать память и структуры ядра, а также отлавливать крахи системы.
Дополнительный материал для изучения:
Эксплуатация уязвимостей ядра
Этот процесс проходит намного веселее, чем эксплуатация на уровне пользователя J.
Главная цель – добиться привилегированного выполнения в контексте ядра. А дальше уже все зависит от нашего воображения, начиная от застолья с домашним пивом и заканчивая внедрением вредоносов, спонсируемых государством.
В целом, наша задача заключается в том, чтобы получить шелл с системными привилегиями.
Темы статей этого цикла
- Часть 1: Настройка рабочей среды
- Конфигурирование трех виртуальных машин и системы, которая будет выступать в роли отладчика.
- Конфигурирование отладчика WinDBG.
- Часть 2: Полезные нагрузки
- Изучение наиболее распространенных полезных нагрузок. В последующих частях будут рассматриваться конкретные уязвимости и, при необходимости, указываться ссылки на эту статью.
- Остальные части.
- Рассмотрение уязвимостей.
Жизненный цикл разработки эксплоита уровня ядра
- Нахождение уязвимости. Эта тема не будет рассматриваться в данном цикле, поскольку мы уже точно знаем, где находятся бреши.
- Перехват потока выполнения. Некоторые уязвимости предусматривают выполнение кода, для некоторых есть дополнительные требования.
- Расширение привилегий. Главная цель – получить шелл с системными привилегиями.
- Восстановление потока выполнения. Неучтенные исключения на уровне ядра приводят к краху системы. Если вы не собираетесь писать эксплоит для DoS-атаки, следует учитывать этот факт.
Типы целевых систем
Мы будем работать с уязвимостями в следующих системах (конкретная версия не принципиальна):
- Win7 x86 VM
- Win7 x64 VM
- Win10 x64 VM
Начнем с архитектуры x86, и далее будем портировать эксплоит для системы Win7 x64. Некоторые эксплоиты не будут запускать на машинах с Win10 из-за присутствия новых защит. В этом случае мы либо будем изменять логику работы эксплоита, либо будем использовать полностью другой подход.
Используемое программное обеспечение:
Настройка систем для отладки
Отладочные системы, с которыми мы будем взаимодействовать, предназначены для загрузки уязвимого драйвера. На этих машинах часто будут возникать крахи, поскольку большинство исключений в ядре способствуют явлениям подобного рода. Необходимо выделить достаточно оперативной памяти для этих систем.
На каждой машине, которая будет отлаживаться, нужно сделать следующее:
- Внутри директории VirtualKD запустите файл target\vminstall.exe. Добавится новая загрузочная запись и будут доступны функции отладки и автоматическое подключение к серверу VirtualKD, установленному в системе, которая выступает в роли отладчика.
В случае с Windows 10 VM необходимо включить режим test signing, который позволяет загружать неподписанные драйвера в ядро.
После выполнения команды bcdedit /set testsinging on и перезагрузки на рабочем столе появится надпись «Test Mode».
- Запустите OSR Driver Loader. Зарегистрируйте и запустите службу. Возможно, потребуется перезагрузка.
- Установите дополнения на гостевой виртуальной машине (необязательное условие).
- Добавьте учетную запись с низкими привилегиями, которая понадобится во время эксплуатации.
C:\Windows\system32>net user low low /add
The command completed successfully.
Настройка отладчика
В системе, которая будет выступать в роли отладчика, будет использоваться WinDBG. Вы сможете инспектировать память, структуры данных и при необходимости выполнять манипуляции. Наличие удаленной отладочной сессии во время падения целевой системы позволит нам подключаться к виртуальной машине и анализировать крахи.
Хост VirtualKD будет выполнять коммуникацию автоматически через именованный канал, вместо установки соединения вручную. Если вы отлаживаете через сеть в Win10 VM, потребуется протестировать соединение вручную.
- Установите Windows SDK. Вы можете выбрать только «Debugging Tools for Windows».
- Проверьте, что установлен отладчик WinDBG. По умолчанию используется папка C:\Program Files (x86)\Windows Kits\10\Debuggers.
Добавьте этот путь в качестве системного и установите путь к отладчику в VirtualKD
Перезапустите гостевые виртуальные машины. Система с VirtualKD, используемая в качестве отладчика, должна быть запущена. После перезагрузки вы сможете начать сессию в WinDBG.
Настройка WinDBG
Если все настроено корректно, WinDBG поставит выполнение на паузу и отобразит некоторую информацию, касающуюся целевой системы.
Рисунок 1: Остановка выполнения кода ядра
Символы содержат отладочную информацию для множества бинарных файлов в ОС Window. Загрузить символы можно при помощи следующей команды:
Включаем режим подробного информирования процесса отладки.
ed nt!Kd_Default_Mask 0xf
Должен загрузиться модуль HEVD:
kd> lm m HEVD
Browse full module list
start end module name
fffff80b`92b50000 fffff80b`92b59000 HEVD (deferred)
Сохраняем настройки профиля и любые изменения рабочей среды:
File -> Save Workspace to File
Введите команду g или нажмите клавишу F5 для продолжения выполнения (перечень других команд, которые вам могут пригодиться, хорошо описан в этом документе).
Краткое описание модуля HEVD
Процедура DriverEntry является стартовой для каждого драйвера:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) <
UINT32 i = 0;
PDEVICE_OBJECT DeviceObject = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
UNICODE_STRING DeviceName, DosDeviceName = <0>;
- Эта процедура содержит вызов функции IoCreateDevice, содержащей имя драйвера, которое мы будем использовать во время коммуникации.
- В объект DriverObject будут добавлены нужные структуры и указатели на функции.
- Для нас важен указатель функции, связанный с процедурой DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL], отвечающей за обработку IOCTL (I/O Control; управление вводом/выводом);
- В HEVD эта функция называется IrpDeviceIoCtlHandler, которая представляет собой большое условное выражение со множеством ответвлений для каждого IOCTL. Каждая уязвимость имеет уникальный IOCTL.
Пример: HACKSYS_EVD_IOCTL_STACK_OVERFLOW представляет собой IOCTL, используемый для активации бреши, связанной с переполнением стека.
На этом первая часть завершается. В следующей статье мы поговорим о полезных нагрузках. На данный момент доступна только полезная нагрузка, предназначенная для кражи токенов, которая будет использоваться в третьей части.
P.S. Я понимаю, что существует масса тонкостей и проблем, с которыми вы можете столкнуться. Поскольку в этом цикле основное внимание уделяется разработке эксплоитов, вам придется решать все попутные проблемы самостоятельно. Однако все возникающие вопросы вы можете задавать в комментариях.
Подписывайтесь на каналы «SecurityLab» в Telegram и
Twitter, чтобы первыми узнавать о новостях и эксклюзивных материалах по информационной безопасности.
Отладка, debug, ядра ОС Windows
Некоторые устройства, которые мы разрабатываем, требуют написания драйвера устройства для ОС Windows или Linux. Написание драйвера устройства – это не совсем формат нашего сайта, но возможно эта статья будет кому-то полезна. Речь пойдет даже не о написании драйвера, а об отладке драйвера в ОС Windows. Я вот уже две недели как погрузился в отладку драйвера одного устройства.. Не очень простое дело..
Итак, предположим, нам захотелось заглянуть внутрь ядра Windows, посмотреть, как оно работает. Ну или допустим мы написали драйвер нашего устройства, а он не работает или работает неправильно. Нужно посмотреть отладчиком ядра, что происходит.
Прежде всего нужно подготовить «среду» для отладки.
Понадобится 2 компьютера с ОС Windows: первый компьютер – это тот который будет подвержен отладке, второй компьютер – это тот, с помощью которого будет вестись отладка. В терминологии Microsoft первый компьютер называется Target, а второй – это Host.
Эти два компьютера нужно соединить между собой для передачи отладочной информации. Есть несколько способов:
- использовать специальный майкрософтовский отладочный USB кабель. Не думаю, что вы его легко найдете.
- использовать кабель FireWire. Это уже проще. Некоторые компьютеры имеют такой разъем, ну или можно найти PCI плату с разъемом FireWire. У меня сейчас в ПК есть такой разъем на материнской плате, а вот в ноутбуке – нет. Так что этот способ так же отпадает.
- самый простой способ – последовательный порт. Не очень хорошо в смысле скорости передачи, зато просто организовать.
Вот мое рабочее место для отладки:
Слева – Target, инспектируемый компьютер.
Справа – Host, ноутбук отладчика.
В ноутбук подключен кабель USB и программатор MBFTDI. В этом случае мы его будем использовать просто как переходник USB2COM. То есть для ноутбука это как последовательный порт. Правда есть ньюанс – выходные уровни программатора MBFTDI не соответствуют стандартным в последовательном порту. Поэтому я еще подключил преобразователь уровней, на микросхеме MAX232 (нашел его среди старых железок, у нас для них целый ящик в офисе).
Теперь нужно настроить Target. У меня здесь Windows 7 64х битная.
Запускаем окно командной строки CMD от имени администратора и в нем выполняем команды:
>bcdedit /debug on
>bcdedit /dbgsettings serial debugport:n baudrate:rate
У меня debugport:COM1 и baudrate:115200
Это в общем и есть вся настройка инспектируемого компьютера.
Теперь на нем нужно просто выполнить перезагрузку.
Далее настроим хост – у меня это ноутбук.
Здесь нужно установить программу отладчика WinDbg.
Программа отладчика есть в составе Windows Driver Kit (WDK) или в составе Microsoft Software Developer Kit. Все это можно взять с сайта Microsoft https://msdn.microsoft.com/en-us/windows/hardware/hh852365 вполне легально и бесплатно.
У меня на ноутбуке так же Windows 7 x64. Я установил WDK и там в составе есть нужный мне отладчик.
Запускаю WindDbg. Выбираем пункт меню File -> Kernel Debug и появляется окошко:
Выбираю скорость передачи 115200 и имя последовательного порта. В принципе все готово.
На ноутбуке Host в программе WinDbg есть командная консоль, достапная через меню View -> Command . Появляется командная строка отладчика.
Любая высокая технология для наблюдателя со стороны мало отличима от магии..
На хосте в программе WinDbg нажимаю Ctrl+Break и компьютер Target останавливается! То есть полностью стоят все процессы и потоки Windows. Можно попить чайку.
В консоли отладчика можно выполнять различные команды. Команд много, у них много параметров, конечно я не смогу их все описать. В конце концов для этого есть вполне вменяемая инструкция-help самой программы WinDbg.
Самые простые команды:
>u – показать дизассемблированный код в месте, где произошла остановка процессора. Ну или “u ” – посмотреть код по адресу.
>d – показать дамп памяти по адресу или по регистру.
>r — показать содержимое регистра процессора.
>t – выполнить одну инструкцию процессора.
>p – выполнить инструкцию процессора или целую процедуру, если инструкция call.
Более того, в отладчике конечно можно установить точки останова различного типа.
Самый простой пример:
>bp — остановка ядра, когда процессор достигнет указанного адреса.
Еще можно установить точку останова по записи или чтению заданной ячейки памяти или порта ввода вывода.
Отмена всех точек останова – команда « bc * »
Тут нужно еще сказать про символическую информацию.
Конечно, рассматривать голый ассемблерный код удовольствие не очень приятное. Нужно подключить еще отладочную информацию.
Например, мы написали драйвер для ОС Windows. Скомпилировали его debug версию. Вместе с файлом драйвера mydrv.sys компилятор генерирует еще файл с соответствующей символьной информацией mydrv.pdb .
Зайдем в меню отладчика File -> Symbol File Path.. и в диалоговом окне добавим путь к нашему файлу PDB.
Теперь, когда драйвер загружен в память ядра ОС Windows уже проще производить отладку с символьной информацией. Нам теперь не нужно знать абсолютные адреса в нашем драйвере. Установить точку останова можно по имени функции:
>bp mydrv!DriverEntry – остановить ядро, когда произойдет вызов функции DriverEntry нашего драйвера mydrv .
Кроме этого, очень полезно подключить к отладчику еще и символьную информацию самого ядра Windows. Конечно, версий виндовсов много, есть разные сборки и где найти символьную информацию именно соответствующую вашей Target ОС Windows?
Проще всего, в командной строке отладчика выполнить команду
При этом, нужные файлы отладки (именно нужной версии) будут выкачаны через интернет к вам на диск в папку c:\localsymbols прямо с сервера Microsoft.
Теперь, можно уже более осмысленно дебажить и само ядро.
Хотите посмотреть, как выглядит, например, функция USBPORTSVC_CompleteIsoTransfer драйвера usbport.sys ? Нет проблем:
Можно поставить точку останова и далее по шагам исполнить все инструкции. Символьная информация помогает понять если не детали, то хотя бы общий смысл исполняемого кода чужого драйвера.
Например, очень многие системные структуры данных в ядре Windows снабжаются «сигнатурой» — специальной строкой, обычно из 4 символов. Таким образом, функции ядра имеют возможность легко проверить переданные им указатели на структуры являются верными или нет. Вот в коде на картике выше есть вызов функции USBPORT_AssertSig . Уже по названию функции становится примерно понятно, что она делает – проверяет указатель, действительно ли он указывает на структуру с нужной сигнатурой.
Вот еще что. При вызовах функций ядра Windows обычно первые четыре параметра функций передаются в регистрах RCX , RDX , R8 и R9 . Похоже остальные параметры, если их больше четырех, передаются функциям в стеке.
Отладка собственного драйвера может быть еще проще, так как имеются исходные тексты самого драйвера. Укажите к ним путь в диалоговом окне отладчика Source Search Path и можно будет выполнять по шагам не отдельные команды процессора, а целые строки программы C/C++. Так же становятся доступны для просмотра локальные переменные функций и прочая отладочная информация.
Вообще отладчик WinDbg дает широкие возможности для отладки своих драйверов, а так же возможность для изучения вообще ядра ОС Windows.