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

Статья Как утилита Trend Micro для борьбы с руткитами позволила устанавливать руткиты

yashechka

Генератор контента.Фанат Ильфака и Рикардо Нарвахи
Эксперт
Регистрация
24.11.2012
Сообщения
2 344
Реакции
3 563
18 мая 2020 года восемнадцатилетний исследователь Билл Демиркапи опубликовал в своем блоге объемное исследование, посвященное очень любопытной теме. Автору удалось установить в Windows собственный руткит с помощью утилиты RootkitBuster компании Trend Micro. Рассказ о том, как молодой исследователь пришел к успеху, — в сегодняшней статье.

Изучая методы обнаружения руткитов, Билл Демиркапи наткнулся на бесплатную утилиту RootkitBuster компании Trend Micro. Разработчик позиционирует ее как сканер скрытых файлов, записей реестра и master boot record (MBR), предназначенный для идентификации и удаления руткитов. Описание программы гласило, что RootkitBuster способен выявлять несколько методов проникновения руткитов и закрепления их в системе. Это заинтересовало исследователя, и он решил выяснить, что у RootkitBuster спрятано под капотом. Пристальное изучение программы позволило обнаружить примечательную дыру в коде, с помощью которой можно использовать тулзу не только для поиска прячущихся в глубинах Windows вредоносов, но и для установки в систему собственных руткитов.

Установка

Сразу после запуска инсталлятора RootkitBuster Билл обратил внимание на предупреждение Resource Hacker о том, что софтина пытается установить в его систему файл tmcomm.sys — драйвер, используемый некоторыми приложениями Trend Micro.

1.png


RootkitBuster устанавливает драйвер до того, как пользователь примет условия лицензионного соглашения (здесь и далее иллюстрации из блога Билла Демиркапи)

Примечательно, что драйвер и сам исполняемый файл сканера были распакованы на диск в папку %TEMP%\RootkitBuster еще до того, как на экране Билла появился текст лицензионного соглашения. Оно, в частности, гласило, что пользователь RootkitBuster обязуется «не пытаться перепроектировать, модифицировать, дизассемблировать, декомпилировать, исследовать исходный код или создавать производные произведения на основе этой программы».

Поэтому Билл взял и завершил процесс инсталлятора с помощью пункта «Закрыть окно» контекстного меню, так и не приняв условия лицензии. Что позволило ему с чистой совестью «дизассемблировать, декомпилировать» и делать с этим продуктом Trend Micro другие вещи, о которых в приличном обществе не принято говорить вслух. Отличный трюк для обхода юридических сложностей!


Драйвер tmcomm.sys

Этот драйвер, обозначенный как Common Module Trend Micro, способен принимать сообщения от привилегированных приложений пользовательского режима и может выполнять функции, специфичные не только для утилиты RootkitBuster. Иными словами, он используется многими другими программами Trend Micro.

В числе первых действий драйвер создает устройство по адресу \Device\TmComm для приема сообщений IOCTL из пользовательского режима. Для этого устройства создается символическая ссылка по адресу \DosDevices\TmComm (которая доступна через \\.\Global\TmComm). Точка входа инициализирует значительное количество используемых в драйвере классов и структур, однако для целей исследования — выполнения кода ядра — нет необходимости подробно рассматривать каждый из них.

2.png


Создание устройства в драйвере tmcomm.sys

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

Класс TrueApi

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

C:
struct TrueApi
{
    BYTE Initialized;
    PVOID ZwQuerySystemInformation;
    PVOID ZwCreateFile;
    PVOID unk1; // Initialized as NULL
    PVOID ZwQueryDirectoryFile;
    PVOID ZwClose;
    PVOID ZwOpenDirectoryObjectWrapper;
    PVOID ZwQueryDirectoryObject;
    PVOID ZwDuplicateObject;
    PVOID unk2; // Initialized as NULL
    PVOID ZwOpenKey;
    PVOID ZwEnumerateKey;
    PVOID ZwEnumerateValueKey;
    PVOID ZwCreateKey;
    PVOID ZwQueryValueKey;
    PVOID ZwQueryKey;
    PVOID ZwDeleteKey;
    PVOID ZwTerminateProcess;
    PVOID ZwOpenProcess;
    PVOID ZwSetValueKey;
    PVOID ZwDeleteValueKey;
    PVOID ZwCreateSection;
    PVOID ZwQueryInformationFile;
    PVOID ZwSetInformationFile;
    PVOID ZwMapViewOfSection;
    PVOID ZwUnmapViewOfSection;
    PVOID ZwReadFile;
    PVOID ZwWriteFile;
    PVOID ZwQuerySecurityObject;
    PVOID unk3; // Initialized as NULL
    PVOID unk4; // Initialized as NULL
    PVOID ZwSetSecurityObject;
};

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

Класс XrayApi

В драйвере имеется еще один важный класс под названием XrayApi. Он используется для доступа к нескольким низкоуровневым устройствам и непосредственного взаимодействия с файловой системой. Этот класс содержит структуру XrayConfig, в которой сосредоточена его основная конфигурация:

C:
struct XrayConfigData
{
    WORD Size;
    CHAR pad1[2];
    DWORD SystemBuildNumber;
    DWORD UnkOffset1;
    DWORD UnkOffset2;
    DWORD UnkOffset3;
    CHAR pad2[4];
    PVOID NotificationEntryIdentifier;
    PVOID NtoskrnlBase;
    PVOID IopRootDeviceNode;
    PVOID PpDevNodeLockTree;
    PVOID ExInitializeNPagedLookasideListInternal;
    PVOID ExDeleteNPagedLookasideList;
    CHAR unkpad3[16];
    PVOID KeAcquireInStackQueuedSpinLockAtDpcLevel;
    PVOID KeReleaseInStackQueuedSpinLockFromDpcLevel;
    ...
};

В этой структуре среди прочего есть информация о расположении таких внутренних и недокументированных переменных в ядре Windows, как ExInitializeNPagedLookasideListInternal, IopRootDeviceNode, ExDeleteNPagedLookasideList и PpDevNodeLockTree. Исследователь предположил, что предназначение этого класса — получение прямого доступа к низкоуровневым устройствам в обход документированных (а следовательно, широко известных вирусописателям) методов.

Запросы IOCTL

Перед изучением возможностей и функций драйвера Билл Демиркапи уделил внимание механизму обработки запросов IOCTL. Это специфичные для отдельных (преимущественно низкоуровневых) устройств системные вызовы ввода-вывода, которые не могут быть реализованы с использованием регулярных вызовов. В основной функции диспетчеризации драйвер Trend Micro преобразует данные вместе с запросом IRP_MJ_DEVICE_CONTROL в собственную структуру, которую исследователь назвал TmIoctlRequest:

C:
struct TmIoctlRequest
{
    DWORD InputSize;
    DWORD OutputSize;
    PVOID UserInputBuffer;
    PVOID UserOutputBuffer;
    PVOID Unused;
    DWORD_PTR* BytesWritten;
};

Таким образом, отправка запросов IOCTL в драйвере tmcomm.sys реализована с помощью своеобразных «таблиц диспетчеризации», при этом «базовая таблица» содержит код IOCTL и соответствующую вспомогательную функцию. Например, если необходимо отправить IOCTL-запрос с кодом 0xDEADBEEF, драйвер сравнивает запрос с каждой строкой базовой таблицы диспетчеризации и передает его только в том случае, если найдет совпадение. Каждая запись в такой таблице имеет структуру, подобную представленной ниже:

C:
typedef NTSTATUS (__fastcall *DispatchFunction_t)(TmIoctlRequest *IoctlRequest);

struct BaseDispatchTableEntry
{
    DWORD_PTR IOCode;
    DispatchFunction_t DispatchFunction;
};

После вызова функции DispatchFunction обычно верифицируются передаваемые данные — начиная с проверки nullptr и заканчивая проверкой входных и выходных буферов. Эти «функции вспомогательной диспетчеризации» затем выполняют другой поиск на основе кода, переданного в пользовательском буфере ввода, с целью найти соответствующую запись во вспомогательной таблице. Такие записи используют структуру следующего вида:

C:
typedef NTSTATUS (__fastcall *OperationFunction_t)(PVOID InputBuffer, PVOID OutputBuffer);

struct SubDispatchTableEntry
{
    DWORD64 OperationCode;
    OperationFunction_t PrimaryRoutine;
    OperationFunction_t ValidatorRoutine;
};

Непосредственно перед вызовом модуля PrimaryRoutine, который выполняет запрошенное действие, функция SubDispatchTableEntry вызывает ValidatorRoutine. Эта подпрограмма проверяет входной буфер, то есть проверяет данные, которые будут впоследствии использоваться PrimaryRoutine. Этот основной код выполняется только в том случае, если ValidatorRoutine успешно завершит проверку.

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

IoControlCode == 9000402Bh

Первая из рассмотренных Биллом таблиц отвечает за взаимодействие с файловой системой. Код для этой вспомогательной таблицы диспетчеризации получается путем разыменования DWORD из начала входного буфера. То есть, чтобы определить, какую запись вспомогательной таблицы следует выполнить, в начале входного буфера нужно поместить DWORD, соответствующий определенному опкоду. Для упрощения задачи исследователя разработчики из Trend Micro оставили в коде драйвера много отладочных строк, с использованием которых Билл Демиркапи составил таблицу функций PrimaryRoutine, хранящихся во вспомогательной таблице диспетчеризации, и описал их назначение.

ОпкодФункция PrimaryRoutineОписание
2713hIoControlCreateFileВызывает NtCreateFile, все параметры определяются запросом
2711hIoControlFindNextFileВозвращает STATUS_NOT_SUPPORTED
2710hIoControlFindFirstFileНичего не делает, всегда возвращает STATUS_SUCCESS
2712hIoControlFindCloseFileВызывает ZwClose, все параметры определяются запросом
2714hIoControlCreateFileIRPСоздает новый FileObject и связывает с ним DeviceObject для запрошенного диска
2715hIoControlReadFileIRPNoCacheСсылается на FileObject, используя HANDLE из запроса. Вызывает IofCallDriver и читает результат
2716hIoControlDeleteFileIRPУдаляет файл, отправляя запрос IRP_MJ_SET_INFORMATION
2717hIoControlGetFileSizeIRPЗапрашивает размер файла, отправляя запрос IRP_MJ_QUERY_INFORMATION
2718hIoControlSetFilePosIRPУстанавливает метаданные файла, отправляя запрос IRP_MJ_SET_INFORMATION
2719hIoControlFindFirstFileIRPВозвращает STATUS_NOT_SUPPORTED
271AhIoControlFindNextFileIRPВозвращает STATUS_NOT_SUPPORTED
2720hIoControlQueryFileВызывает NtQueryInformationFile, все параметры определяются запросом
2721hIoControlSetInformationFileВызывает NtSetInformationFile, все параметры определяются запросом
2722hIoControlCreateFileOplockСоздает Oplock с помощью IoCreateFileEx и другого API файловой системы
2723hIoControlGetFileSecurityВызывает NtCreateFile, а затем ZwQuerySecurityObject. Все параметры определяются запросом
2724hIoControlSetFileSecurityВызывает NtCreateFile, а затем ZwSetSecurityObject. Все параметры определяются запросом
2725hIoControlQueryExclusiveHandleПроверяет дескриптор файла и параметры его использования
2726hIoControlCloseExclusiveHandleПринудительно закрывает дескриптор файла

IoControlCode == 90004027h

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

ОпкодФункция PrimaryRoutineОписание
C350hGetProcessesAllMethodsПоиск процессов с использованием ZwQuerySystemInformation и WorkingSetExpansionLinks
C351hDeleteTaskResults *Удаляет результаты, полученные с помощью других функций, таких как GetProcessesAllMethods
C358hGetTaskBasicResults *Используется для получения результатов анализа, выполненного с помощью других функций, таких как GetProcessesAllMethods
C35DhGetTaskFullResults *Используется для получения полных результатов анализа, выполненного с помощью других функций, таких как GetProcessesAllMethods
C360hIsSupportedSystemВозвращает TRUE, если система «поддерживается» (независимо от того, имеются ли жестко заданные смещения для текущего билда)
C361hTryToStopTmCommПытается остановить драйвер
C362hGetProcessesViaMethodВыполняет поиск процессов с использованием заданного метода
C371hCheckDeviceStackIntegrityПроверяет device tampering для устройств, связанных с физическими дисками
C375hShouldRequireOplockВозвращает TRUE, если для определенных операций сканирования необходимо использовать блокировку

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

C:
struct MicroTaskVtable
{
    PVOID Constructor;
    PVOID NewNode;
    PVOID DeleteNode;
    PVOID Insert;
    PVOID InsertAfter;
    PVOID InsertBefore;
    PVOID First;
    PVOID Next;
    PVOID Remove;
    PVOID RemoveHead;
    PVOID RemoveTail;
    PVOID unk2;
    PVOID IsEmpty;
};

struct MicroTask
{
    MicroTaskVtable* vtable;
    PVOID self1; // ptr to itself
    PVOID self2; // ptr to itself
    DWORD_PTR unk1;
    PVOID MemoryAllocator;
    PVOID CurrentListItem;
    PVOID PreviousListItem;
    DWORD ListSize;
    DWORD unk4; // Initialized as NULL
    char ListName[50];
};

struct MicroScanVtable
{
    PVOID Constructor;
    PVOID GetTask;
};

struct MicroScan
{
    MicroScanVtable* vtable;
    DWORD Tag; // Always 'PANS'
    char pad1[4];
    DWORD64 TasksSize;
    MicroTask Tasks[4];
};

Для большинства запросов IOCTL в этой вспомогательной таблице структура MicroScan заполняется на стороне клиента, который вызывает драйвер. Именно эту особенность Демиркапи решил использовать для эксплуатации возможной уязвимости.

Эксплоит

Билл Демиркапи признается, что во время реверсинга функций, хранящихся в этой вспомогательной таблице диспетчеризации, он был совершенно сбит с толку. В итоге оказалось, что указатель ядра MicroScan, возвращаемый такими функциями, как GetProcessesAllMethods, напрямую передавался другим функциям, например DeleteTaskResults, на стороне клиента. Эти функции принимают такой недоверенный указатель ядра и практически без проверки вызывают функции в таблице виртуальных функций, описанной в классе.

3.png


Если внимательно посмотреть на «подпрограмму проверки» функции вспомогательной таблицы диспетчеризации DeleteTaskResults, то можно увидеть, что для экземпляра MicroScan, указанного во входном буфере + 0x10, проверка фактически одна: убедиться, что он содержит действительный адрес в памяти ядра.

4.png

Проверка экземпляра MicroScan

В DeleteTaskResults используется еще одна простая проверка.

5.png


Проверка Tag в экземпляре MicroScan

Далее функция DeleteTaskResults вызывает конструктор, указанный в таблице виртуальных функций экземпляра MicroScan. Чтобы вызвать таким образом произвольную функцию ядра, необходимо выполнить следующие условия.
  1. Выделить как минимум 10 байт памяти ядра (для vtable и tag).
  2. Управлять выделенной памятью ядра, чтобы установить указатель таблицы виртуальных функций и параметр tag.
  3. Научиться определять адрес этой памяти ядра из пользовательского режима.

Исследователь пишет, что правильное решение в поисках способа выделения и управления памятью ядра из пользовательского режима ему подсказал его наставник Алекс Ионеску. В журнале Hack In The Box от 2010 года была опубликована статья Мэтью Юрчика «Резервные объекты в Windows 7». Идея статьи заключается в том, что можно специальным образом сформировать очередь Apc для Apc Reserve Object, а затем использовать NtQuerySystemInformation для поиска нужного Apc Reserve Object в памяти ядра. Это позволяет приложению пользовательского режима аллоцировать и контролировать до 32 байт в памяти ядра (в 64-битной среде) и определять расположение этой памяти.

Такой прием все еще работает в Windows 10, а это означает, что мы можем соблюсти все требования. Используя резервный объект Apc, мы можем выделить как минимум 10 байт для структуры MicroScan и полностью обойти упомянутые выше проверки. В результате появляется возможность вызывать произвольные указатели ядра.

6.png


Вызов произвольных указателей ядра с использованием резервных объектов Apc

Билл Демиркапи пришел к выводу, что подобная уязвимость характерна не только для функции DeleteTaskResults, но и для всех функций диспетчеризации, помеченных в таблице звездочкой. Все они доверяют переданному клиентом указателю ядра и вызывают конструктор из таблицы виртуальных функций экземпляра MicroScan без дополнительных проверок.

IoControlCode == 90004033h

Эта таблица диспетчеризации управляет классом TrueApi и включает следующие функции.


ОпкодФункция PrimaryRoutineОписание
EA60hIoControlGetTrueAPIPointerПолучает указатели функций в классе TrueApi
EA61hIoControlGetUtilityAPIPointerПолучает указатели служебных функций драйвера
EA62hIoControlRegisterUnloadNotify *Регистрирует функцию, которая вызывается при выгрузке
EA63hIoControlUnRegisterUnloadNotifyВыгружает ранее зарегистрированную функцию выгрузки

Функция IoControlRegisterUnloadNotify привлекла внимание Билла Демиркапи прежде всего потому, что ее упоминание встречалось в отладочных строках. Используя эту функцию из вспомогательной таблицы диспетчеризации, «недоверенный клиент» может зарегистрировать до 16 произвольных процедур, которые вызываются при выгрузке драйвера.

Валидатор данной функции проверяет правильность этого указателя из буфера на стороне клиента. Если вызывающий объект работает в режиме пользователя, средство проверки вызывает указатель ProbeForRead. Если вызывающая сторона находится в режиме ядра, валидатор проверяет, содержит ли указатель действительный адрес памяти ядра.

Эта функция не может непосредственно использоваться в эксплоите из пользовательского режима. Проблема в том, что, если мы используем вызов из режима пользователя, мы должны предоставить указатель пользовательского режима, поскольку валидатор использует ProbeForRead. При выгрузке драйвера вызывается этот указатель пользовательского режима, но он мало что делает из-за ограничений, накладываемых SMEP — механизмом защиты страниц памяти. Позже мы вспомним про эту особенность.

IoControlCode == 900040DFh

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


ОпкодФункция PrimaryRoutineОписание
15F90hIoControlReadFileЧитает файл прямо с диска
15F91hIoControlUpdateCoreListОбновляет указатели ядра, используемые Xray API
15F92hIoControlGetDRxMapTableПолучает таблицу дисков, сопоставленных с соответствующими им устройствами

IoControlCode == 900040E7h

Последняя изученная исследователем таблица диспетчеризации предназначена для поиска в различных структурах системы хуков, которые могут использовать различные руткиты. В ней перечислены проверки, которые выполняет ПО Trend Micro для поиска перехвата вызовов функций и прочих хуков.


ОпкодФункция PrimaryRoutineОписание
186A0hTMXMSCheckSystemRoutineПроверка некоторых системных подпрограмм на наличие хуков
186A1hTMXMSCheckSystemFileIOПроверка главных функций ввода-вывода файлов на наличие хуков
186A2hTMXMSCheckSpecialSystemHookingПроверка типа объекта файла и функции Itoskrnl Io на наличие хуков
186A3hTMXMSCheckGeneralSystemHookingПроверка Io Manager на наличие хуков
186A4hTMXMSCheckSystemObjectByNameРекурсивное отслеживание системных объектов (каталог или символическая ссылка)
186A5hTMXMSCheckSystemObjectByName2 *Копирование системного объекта в память пользовательского режима

Перед подробным анализом функции TMXMSCheckSystemObjectByName2 Билл Демиркапи приводит дизассемблированный листинг нескольких структур, используемых этой функцией:

C:
struct CheckSystemObjectParams
{
    PVOID Src;
    PVOID Dst;
    DWORD Size;
    DWORD* OutSize;
};

struct TXMSParams
{
    DWORD OutStatus;
    DWORD HandlerID;
    CHAR unk[0x38];
    CheckSystemObjectParams* CheckParams;
};

Функция TMXMSCheckSystemObjectByName2 принимает на вход указатель источника, указатель назначения и размер в байтах. Вызываемый для TMXMSCheckSystemObjectByName2 валидатор проверяет следующее:

  • ProbeForRead в объекте CheckParams структуры TXMSParams;
  • ProbeForRead иProbeForWrite в объекте Dst структуры CheckSystemObjectParams.
По сути это означает, что нам нужно передать действительную структуру CheckParams, а указатель Dst, который мы передаем, находится в памяти пользовательского режима. Теперь посмотрим на саму функцию.

7.png


Функция TMXMSCheckSystemObjectByName2

Цикл for может показаться пугающим, но все, что он содержит, — оптимизированный метод проверки диапазона памяти ядра. Для каждой страницы памяти в диапазоне от Src до Src + Size функция вызывает MmIsAddressValid. По-настоящему страшны следующие операции.


8.png


Копирование Size в указатель Dst

Эти строки принимают недостоверный указатель Src и копируют байты Size в недостоверный указатель Dst. Мы можем использовать операции memmove для чтения произвольного указателя ядра, но как насчет записи в произвольный указатель ядра? Проблема заключается в том, что средство проверки TMXMSCheckSystemObjectByName2 требует, чтобы местом назначения была память пользовательского режима. К счастью, Билл Демиркапи обнаружил в коде еще одну ошибку.

Строка *params->OutSize = Size; берет объект Size из нашей структуры и помещает его в указатель, определяемый OutSize. На что указывает OutSize, не проверяется, поэтому мы можем записывать в DWORD любой вызов IOCTL. Одно предостережение: указатель Src должен указывать на действительную память ядра длиной в байтах, соответствующей значению Size байтов. Чтобы удовлетворить этому требованию, Демиркапи просто передал базу модуля ntoskrnl в качестве источника.

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

Обход сертификации Microsoft WHQL

Пока что мы рассмотрели методы чтения и записи произвольной памяти ядра, но для установки нашего собственного руткита не хватает одного шага. Хотя можно выполнить шелл-код ядра только с использованием примитива чтения/записи, Билл Демиркапи предложил пойти по пути наименьшего сопротивления. Поскольку речь идет о драйвере стороннего производителя, скорее всего, он использует выделенную память NonPagedPool, которую мы можем задействовать для размещения и выполнения нашего вредоносного шелл-кода.

Давай посмотрим, как Trend Micro распределяет память. В самом начале точки входа драйвера программа проверяет, поддерживается ли система, определяя версию и номер сборки ОС. Trend Micro делает это потому, что в программе жестко закодировано несколько смещений, которые отличаются в разных редакциях Windows.

К счастью, глобальная переменная PoolType, которая используется для выделения невыгружаемой памяти NonPagedPool, по умолчанию установлена в 0. Билл Демиркапи заметил, что, хотя изначально это значение равнялось 0, переменная все еще находилась в секции .data, то есть ее можно изменить. Когда исследователь посмотрел на то, что записано в переменной, он увидел, что функция, отвечающая за проверку версии операционной системы, также в некоторых случаях устанавливала значение переменной PoolType.

9.png


Установка значения переменной PoolType

Если на компьютере установлена Windows 10 или более новая версия Windows, драйвер предпочитает использовать NonPagedPoolNx. Хорошо с точки зрения безопасности, но плохо для нас. Чтобы использовать уязвимость, нужно найти запасной элемент ExAllocatePoolWithTag c жестко определенным аргументом NonPagedPool, иначе мы не сможем использовать выделенную память драйвера в Windows 10. Но это не так просто. Как насчет функции MysteriousCheck()?

10.png


Функция MysteriousCheck()

Функция MysteriousCheck() проверяет, был ли включен Microsoft Driver Verifier. Вместо того чтобы просто использовать функцию NonPagedPoolNx, которая работает в Windows 8 и выше, Trend Micro выполняет явную проверку, чтобы использовать только безопасное распределение памяти.

Драйвер Trend Micro сертифицирован WHQL, а для получения этого сертификата необходима проверка драйверов. В Windows 10 драйверы не выделяют исполняемую память, это гарантирует Driver Verifier. Компания Trend Micro решила не обращать внимания на требования безопасности и разработала драйвер таким образом, чтобы он работал в любой среде тестирования или отладки, которая обнаружит такие нарушения.

11.png


Драйвер Trend Micro сертифицирован WHQL
Trend Micro мог просто оставить стандартную проверку Windows 10, зачем вообще создавать явную проверку для Driver Verifier? Единственная рабочая теория, которую и предложил Билл Демиркапи, заключается в том, что по какой-то причине большинство их драйверов несовместимы с NonPagedPoolNx (совместима только их точка входа), в противном случае подобные ухищрения теряют всякий смысл.

Устанавливаем свой руткит с помощью RootkitBuster

Итак, как все-таки можно использовать RootkitBuster для установки в систему собственного руткита? Для этого нужно выполнить всего лишь три несложных шага.

  1. Найти любое используемое драйвером распределение NonPagedPool. Пока у тебя не запущен Driver Verifier, можно использовать любые нестраничные выделения памяти, указатели которых хранятся в секции .data. Желательно выбирать распределение, которое используется нечасто.
  2. Напиши свой шелл-код ядра в любом месте выделенной памяти, используя произвольный примитив записи ядра TMXMSCheckSystemObjectByName2.
  3. Выполни свой шелл-код, зарегистрировав процедуру выгрузки (непосредственно в секции .data) или используя несколько других методов, представленных в таблице диспетчеризации 90004027h.
Вот, собственно, и все. В своем исследовании Демиркапи отмечает, что драйвер tmcomm.sys, разработанный в Trend Micro, представляет собой куски кода, кое-как смотанные изолентой. Хотя разработчики и предусмотрели определенные меры защиты, значительная часть кода внутри обработчиков IOCTL использует довольно небезопасные методы, которые злоумышленники могут применять отнюдь не в благих целях. При этом драйвер tmcomm.sys используется не только в утилите RootkitBuster, но и в других продуктах Trend Micro, а значит, потенциальные уязвимости присутствуют и в них.
 
А я уже думал, что руткитам все дырки позатыкали.
 
Хорошая статья, нет непозатыкали.)))

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

Таких драйверов уязвимых много, думаю почти на каждом компе найдется хотя-бы один.)))

Поэтому руткиты будут жить.:)
 
Я к тому что с приходом UEFI руткиты пропали.
 
Я к тому что с приходом UEFI руткиты пропали.
UEFI - Это больше защита от букткитов, вот они пропали с приходом UEFI.

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

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

Вообще руткиты, это не на паблик малварь, а обычно таргетивные атаки.

Хотя уже давно, даже малварь класса рансомваре уже направлена на организации и т.д.

Вообще малварь, класса рансомваре весьма интересная также, ведь они не просто шифруют данные, а еще и крадут эти данные для возможного шантажа.

Более того такую малварь можно, да я думаю уже давно используют как оружие спец. службы стран, либо сами, либо косвенно "управляя" вымогателями.

Помните атаки в РФ, в прошлом году, когда были атакованы банки, больницы и т.д.
Также мне непонятны причины, почему СМИ раздувают из-за атак вымогателей такой шум, вот например всякие заявления вымогателей, во всех СМИ, причем нет ссылок где эти заявления можно почитать, очень много шума...)

Кто почитает этот пост, если не задумались, то повод задуматься, а не используют-ли вас кто-то для достижения своих целей.

Хотя думаю всем пофиг, короче не заморачивайтесь...:):cool:
 
Просто давно не было никаких больших расследований и исследований про руткиты буткиты
 
Пожалуйста, обратите внимание, что пользователь заблокирован


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