Уязвимый драйвер. IDA Pro «с нуля» ч.56

Обсудить статью на форуме


Очень удобно, когда все крэкерские инструменты, книги и статьи в одном месте. Используйте сборник 2020 года от EXELAB - вот тут.
Давайте теперь рассмотрим драйвер, который запрограммирован с различными уязвимостями, чтобы понять, как их эксплуатировать. Как и всегда мы будем использовать WINDOWS 7 SP1 без какого-либо патча безопасности. Мы знаем, что здесь всё будет работать. Затем мы увидим, какие изменения есть ниже и какие другие возможности существуют в новых системах. Но мы будем идти потихоньку и никуда не будем спешить.

У нас есть драйвер, скомпилированный с символами. У вас есть возможность эксплуатировать его практически всеми возможными способами. Он сделан специально для практики.

https://github.com/hacksysteam/HackSysExtremeVulnerableDriver

Реализованные уязвимости


Мы будем начинать потихоньку. Сначала сделаем анализ переполнения стека.

Конечно, Вам нужно скопировать драйвер на целевую машину и загрузить его с помощью OSR DRIVER LOADER.

Мы копируем его с его IDB в локальную папку и открываем его в IDA чтобы начать анализ.

Хорошо. Как мы уже знаем здесь, у нас есть символы, которые облегчают нам много вещи. Но первое, что мы должны найти и что почти всегда распознаётся с символами или без, это структура _DRIVER_OBJECT, которая передается как аргумент в функцию DRIVERENTRY.

В этом случае у нас не так много проблем.

IDA Pro взлом и реверсинг программ

Здесь хорошо видна точка входа, и ее аргументы хорошо обнаружились.

Мы видим, что драйвер использует, как и в предыдущих примерах, API функцию RTLINITUNICODESTRING для инициализации структур.

IDA Pro взлом и реверсинг программ

Напомним, что первым аргументом был указатель на структуру UNICODE_STRING. Здесь мы видим PUNICODE_STRING, т.е. указатель на структуру UNICODE_STRING.

IDA Pro взлом и реверсинг программ

Т.е. это буфер, куда драйвер сохранит длину строки в переменную типа WORD, Максимальная длина в следующем поле также имеет тип WORD, а указатель будет скопирован в строку юникода, которую мы передали как источник в третьем поле.

IDA Pro взлом и реверсинг программ

Сначала инициализируется в нуль структура DOSDEVICENAME, которая также имеет тип UNICODE_STRING.

IDA Pro взлом и реверсинг программ

Драйвер помещает в поле LENGTH нуль, помещая в него значение регистра AX, которое равно здесь 0. А затем идет инструкция STOSD, которая копирует значение из регистра EAX, т.е. помещает нуль в адрес, куда указывает регистр EDI, т.е. в поле MAXIMUMLENGHT. А затем следующая инструкция STOSW копирует из регистра AX значение, т.е. нуль в два следующих байтах, т.е. помещая нуль в 6 байтов, т. е. инициализирует два оставшихся поля структуры, которые занимают 6 байтов (1 WORD и DWORD).

Компилятор только инициализирует структуру DOSDEVICENAME. Другая переменная, которая называется DEVICENAME, не равна нулю. Драйвер использует ее напрямую.

IDA Pro взлом и реверсинг программ

Другими словами, DEVICENAME - это строка, которая преобразуется в тип структуры UNICODE_STRING. Другими словами, в её трех полях будет длина, максимальная длина и указатель, который мы передаем в строку источник. Он будет скопирован в третье поле.

IDA Pro взлом и реверсинг программ

В моей машине указатель находится по адресу 0x00016938. Это смещение скопирует его в третье поле структуры.

IDA Pro взлом и реверсинг программ

В DOSDEVICENAME вы будете создавать другой указатель UNICODE_STRING. Я использую как источник эту другую строку.

Затем идёт вызов функции IOCREATEDEVICE. Мы помним, что вам нужно было создать DEVICE OBJECT, чтобы иметь возможность общаться с программами из пользовательского режима.

IDA Pro взлом и реверсинг программ

IDA Pro взлом и реверсинг программ

Это будет почти всегда около точки входа в большинстве драйверов, которые взаимодействуют с программами в пользовательском режиме.

IDA Pro взлом и реверсинг программ

Последний аргумент, это указатель на вновь созданную структуру DEVICE_OBJECT.

IDA Pro взлом и реверсинг программ

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

IDA Pro взлом и реверсинг программ

Затем структура DRIVEROBJECT будет инициализироваться из регистра ESI + 38. Поскольку ESI указывает на DRIVEROBJECT, я нажимаю T. Я могу посмотреть, какое это поле (но это DRIVER_OBJECT идём в LOCAL TYPES и синхронизируем типы)

IDA Pro взлом и реверсинг программ

IDA Pro взлом и реверсинг программ

Т.е. это указатель на структуру MAJORFUNCTION, про которую мы помним, что он представляет собой таблицу указателей, которая, согласно позиции, приведет меня к разным функциям в зависимости от случая. Вспомните это, например.

IDA Pro взлом и реверсинг программ

Первый указатель, т.е. тот, который находится в позиции 0, является IRP_MJ_CREATE и драйвер будет переходить на него, когда вы вызываете функцию CREATEFILE, чтобы открыть дескриптор устройства. Второй указатель, т.е. со значением 0x1 находится в положении 4, так как он являются DWORD и т.д. Это означает, что обратно пропорционально, если у меня есть поле этой структуры по её смещению, чтобы знать, какой указатель нам нужен надо делить его на четыре. Из примера, который мы использовали в предыдущих драйверах давайте вспомним.

IDA Pro взлом и реверсинг программ

Это соответствует значению 0x38/4, т.е.

Python>hex(0x38/4)
0xE

IDA Pro взлом и реверсинг программ

Т.е. 0xE соответствует IRP_MJ_DEVICE_CONTROL, когда мы передали код IOCTL из режима пользователя. Этот указатель мы перезаписали с помощью обработчика, так что в соответствии с тем, какой код IOCTL, различные действия будут выполняться с помощью конструкции SWITCH. Например так.

В текущем случае, мы видим, что драйвер инициализирует значения начиная с указателя на начало таблицы MAJORFUNCTION. Он копирует значение регистра EAX, в которое помещается смещение функции, которая называется _IRP_NOTIMPLEMENTEDHANDLERS. Копирование происходит 0x1C раз. Это значение передаётся в регистре ECX. Оно равно количеству указателей, которые нужно инициализировать.

IDA Pro взлом и реверсинг программ

Другими словами, в начале вся таблица инициализирует этим указателем, который, по-видимому, не будет делать ничего. Это похоже на случай по умолчанию.

IDA Pro взлом и реверсинг программ

Поскольку регистр EDX хранит указатель на начало таблицы MAJORFUNCTION, его содержимым является позиция 0, т.е.

+#define IRP_MJ_CREATE 0x00
+#define IRP_MJ_CREATE_NAMED_PIPE 0x01
+#define IRP_MJ_CLOSE 0x02
+#define IRP_MJ_READ 0x03
+#define IRP_MJ_WRITE 0x04
+#define IRP_MJ_QUERY_INFORMATION 0x05
+#define IRP_MJ_SET_INFORMATION 0x06
+#define IRP_MJ_QUERY_EA 0x07
+#define IRP_MJ_SET_EA 0x08
+#define IRP_MJ_FLUSH_BUFFERS 0x09
+#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a
+#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b
+#define IRP_MJ_DIRECTORY_CONTROL 0x0c
+#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d
+#define IRP_MJ_DEVICE_CONTROL 0x0e
+#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f
+#define IRP_MJ_SCSI 0x0f
+#define IRP_MJ_SHUTDOWN 0x10
+#define IRP_MJ_LOCK_CONTROL 0x11
+#define IRP_MJ_CLEANUP 0x12
+#define IRP_MJ_CREATE_MAILSLOT 0x13
+#define IRP_MJ_QUERY_SECURITY 0x14
+#define IRP_MJ_SET_SECURITY 0x15
+#define IRP_MJ_POWER 0x16
+#define IRP_MJ_SYSTEM_CONTROL 0x17
+#define IRP_MJ_DEVICE_CHANGE 0x18
+#define IRP_MJ_QUERY_QUOTA 0x19
+#define IRP_MJ_SET_QUOTA 0x1a
+#define IRP_MJ_PNP 0x1b
+#define IRP_MJ_PNP_POWER 0x1b
+#define IRP_MJ_MAXIMUM_FUNCTION 0x1b


Мы будем создавать структуру MAJORFUNCTION.

struct __MajorFunction{
unsigned int _MJ_CREATE;
unsigned int _MJ_CREATE_NAMED_PIPE;
unsigned int _MJ_CLOSE;
unsigned int _MJ_READ;
unsigned int _MJ_WRITE;
unsigned int _MJ_QUERY_INFORMATION;
unsigned int _MJ_SET_INFORMATION;
unsigned int _MJ_QUERY_EA;
unsigned int _MJ_SET_EA;
unsigned int _MJ_FLUSH_BUFFERS;
unsigned int _MJ_QUERY_VOLUME_INFORMATION;
unsigned int _MJ_SET_VOLUME_INFORMATION;
unsigned int _MJ_DIRECTORY_CONTROL;
unsigned int _MJ_FILE_SYSTEM_CONTROL;
unsigned int _MJ_DEVICE_CONTROL;
unsigned int _MJ_INTERNAL_DEVICE_CONTROL;
unsigned int _MJ_SCSI;
unsigned int _MJ_SHUTDOWN;
unsigned int _MJ_LOCK_CONTROL;
unsigned int _MJ_CLEANUP;
unsigned int _MJ_CREATE_MAILSLOT;
unsigned int _MJ_QUERY_SECURITY;
unsigned int _MJ_SET_SECURITY;
unsigned int _MJ_POWER;
unsigned int _MJ_SYSTEM_CONTROL;
unsigned int _MJ_DEVICE_CHANGE;
unsigned int _MJ_QUERY_QUOTA;
unsigned int _MJ_SET_QUOTA;
unsigned int _MJ_PNP;
unsigned int _MJ_PNP_POWER;
unsigned int _MJ_MAXIMUM_FUNCTION;
};

Я знаю, что это указатели, но для нашего случая я буду использовать тип UNSIGNED INT и это будет работать. Проблема состоит в том, что это локальные типы. При использовании операции INSERT, IDA не принимают структуру. Поэтому я буду экспортировать её. Я добавляю структуру и перезагружаю ее с помощью FILE→ LOAD FILE→ PARSE C HEADER FILE

IDA Pro взлом и реверсинг программ

Я добавил структуру в ЗАГОЛОВОЧНЫЙ ФАЙЛ.

IDA Pro взлом и реверсинг программ

Теперь возникает вопрос

IDA Pro взлом и реверсинг программ

Я позволял себе редактировать внутри структуры DRIVER_OBJECT, тип MAJORFUNCTION в LOCAL TIPES ?

IDA Pro взлом и реверсинг программ

Мы видим, что я изменил структуре определение поля MAJORFUNCTION. Внутри структуры DRIVER_OBJECT для того, чтобы она была типа __MAJORFUNCTION, который я определил.

IDA Pro взлом и реверсинг программ

IDA Pro взлом и реверсинг программ

Мы видим, что сейчас, если поля определены с их именами каждого указателя.

Когда мы нажимаем T, мы не можем выбрать структуру DRIVER_OBJECT, потому что регистр EDX указывает на таблицу MAJORFUNCTION, поэтому я выбираю последнюю.

IDA Pro взлом и реверсинг программ

IDA Pro взлом и реверсинг программ

Сейчас стало намного лучше. Все в порядке. Я определил функции, которые будут использоваться, т. е. _MJ_CREATE, _MJ_CLOSE, _MJ_DEVICE_CONTROL и те, которые будет вызываться, когда драйвер останавливается через функцию DRIVERUNLOAD.

Очевидно, когда из режима пользователя мы вызвали функцию CREATEFILE, вызывается функция, которая перезаписывает поле _MJ_CREATE. Когда мы передаем IOCTL код в функцию DEVICEIOCONTROL, вызывается _MJ_DEVICE_CONTROL. Когда вызывается функция CLOSEHANDLE, драйвер вызывает ту, которая перезаписывает поле _MJ_CLOSE. И когда драйвер останавливается, вызывается та, которая перезаписывает функцию DRIVERUNLOAD.

Мы будем смотреть на функцию, которая будет вызываться при передаче IOCTL.

Мы синхронизируем структуру IRP из вкладки LOCAL TYPES.

Как мы видели в части 53, поле 60 из IRP указывает на структуру _IO_STACK_LOCATION.

IDA Pro взлом и реверсинг программ

Регистр ESI указывает на _IO_STACK_LOCATION, поэтому все, что равно ESI + XXX, будет полем вышеупомянутой структуры. После синхронизации из вкладки LOCAL TYPES.

Напомним, что у структуры _IO_STACK_LOCATION есть несколько опций. Я выберу ту, которая соответствует IOCONTROLCODE.

IDA Pro взлом и реверсинг программ

Мы видим, что в соответствии с кодом IOCTL, SWITCH отправляет нас в разные блоки и что они помечены типом уязвимости, который имеет каждый путь.

IDA Pro взлом и реверсинг программ

IDA Pro взлом и реверсинг программ

Здесь есть один блок, который говорит нам, что он имеет STACKOVERFLOW. Поэтому вам не нужно слишком сильно себя утруждать и искать переполнение.

IDA Pro взлом и реверсинг программ

Мы видим, что есть два аргумента, которые передают в регистре EDI структуру IRP а в регистре ESI IRPSP - это имя переменной типа _IO_STACK_LOCATION, которая была в регистре ESI.

IDA Pro взлом и реверсинг программ

IDA Pro взлом и реверсинг программ

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

IDA Pro взлом и реверсинг программ

Мы видим, что этот SIZE и этот буфер передаются в функцию _TRIGGERSTACKOVERFLOW.

IDA Pro взлом и реверсинг программ

Мы видим, что драйвер помещает нуль с помощью регистра ESI в первый DWORD буфера KERNELBUFFER и затем с помощью функции MEMSET помещает нуль в следующий DWORD, так как происходит сложение KERNELBUFFER + 4, и получается размер 0x7FC.

IDA Pro взлом и реверсинг программ

Вышеупомянутый буфер имеет длину 512 * 4, так как это массив DWORD (DD), поэтому общая длина в десятичной системе равна:

512 * 4
Out[64]: 2048


В HEX это

hex(2048)
Out[65]: '0x800'


Поэтому, поместив в первый DWORD нуль, а затем в оставшиеся 0x7FC байт. Действительно, драйвер заполнит весь буфер размером 0x800 нулями. (0x7FC + 4 = 0x800)

Затем драйвер вызовет функцию PROBEFORREAD, которая проверит, выровнен ли входной буфер в пользовательском режиме и находится ли он в пользовательском пространстве.

IDA Pro взлом и реверсинг программ

IDA Pro взлом и реверсинг программ

Затем драйвер печатает указатели буферов и их размеры.

IDA Pro взлом и реверсинг программ

Здесь мы ясно видим переполнение стека, поскольку драйвер использует размер, который я передаю ему как данные для копирования из входного буфера в пользовательский, в буфер в ядре, который является назначением.

IDA Pro взлом и реверсинг программ

IDA Pro взлом и реверсинг программ

Здесь мы видим, что при печати размера буфера ядра, драйвер используйте тот, который находится в регистре ESI, который является константой 0x800, но при выполнении функции MEMCPY он используйте аргумент SIZE, который я передал ему, без каких-либо проверок, которые будут приводить к переполнению стека и поскольку здесь нет COOKIE, он будет легко переполняться.

В следующей части мы будем делать скрипт с эксплуатацией. На этом здесь, мы закончим анализ.

=======================================================
Автор текста: Рикардо Нарваха - Ricardo Narvaja (@ricnar456)
Перевод на русский с испанского: Яша_Добрый_Хакер(Ростовский фанат Нарвахи).
06.11.2018
Версия 1.0


Обсуждение статьи: Уязвимый драйвер. IDA Pro «с нуля» ч.56 >>>


При перепечатке ссылка на https://exelab.ru обязательна.



Видеокурс ВЗЛОМ