Знай своего Врага 
В предыдущей части (часть 1) мы рассмотрели несколько вариантов реализации руткитов. Теперь мы сосредоточимся на анализе руткитов ядра, рассмотрев два примера руткитов, найденных в природе: Husky Rootkit и Mingloa/CopperStealer Rootkit. На этих примерах мы поделимся нашими знаниями о методах и методологии анализа руткитов.
Прежде чем мы погрузимся в анализ, приведем несколько рекомендаций о том, как мы подходили к этому драйверу ядра Windows, а также некоторые предварительные знания, которые помогут понять назначение ключевых функций в двоичном файле.
DriverEntry
Давайте начнем с точки входа двоичного файла. В случае драйвера ядра Windows это DriverEntry.
Обычно DriverEntry включает следующие блоки кода:
* Фрагмент 1: Пример реализации DriverEntry на языке программирования C.
Следующий фрагмент кода (Фрагмент 2) демонстрирует, как выглядит дизассемблирование той же функции DriverEntry.
* Фрагмент 2: Дизассемблирование DriverEntry.
DriverUnload
DriverUnload - это функция, которая вызывается при выгрузке драйвера.
Цель этой функции-обработчика - очистить все ресурсы, созданные драйвером во время его инициализации и выполнения — например, удалить созданные в DriverEntry устройство и символическую ссылку.
Также будет стратегически правильно вызвать функцию ExFreePoolWithTag для освобождения памяти пула, выделенной в функции DriverEntry.
*Фрагмент 3: Пример реализации функции DriverUnload на языке C.
Структуры Ядра Windows
Для полного понимания дизассемблирования драйвера ядра Windows, мы также должны быть знакомы с некоторыми структурами ядра, используемыми диспетчером объектов и другими компонентами в ядре.
Например, следующая структура - это DRIVER_OBJECT (Фрагмент 4).
*Фрагмент 4: Описание структуры DRIVER_OBJECT.
Важно отследить основные функции IRP, используемые драйвером, при его реверс-инжиниринге.
Например, анализируя смещения структуры (Фрагмент 4) и дизассемблирование (Фрагмент 2), мы можем определить, что sub_1400014B0 - это DriverUnload.
Мы также можем использовать значения кодов основных функций IRP, описанные в wdm.h/ntddk.h, чтобы заключить, что sub_140001280 (в Фрагменте 2) - это обработчик функции для IRP_MJ_CREATE, проверив основную функцию кода, которая даст нам результат 0x70 из смещения MajorFunction (0x70) в структуре DRIVER_OBJECT. Это очевидно 0x00*РазмерУказателя (8 в архитектуре x64); следовательно, мы имеем дело с IRP_MJ_CREATE.
Таким же образом мы можем определить, что являются обработчиками функций для IRP_MJ_CLOSE, IRP_MJ_READ, IRP_MJ_WRITE и IRP_MJ_DEVICE_CONTROL.
*Фрагмент 5: Выдержка из wdm.h, определяющая константные значения для всех основных функций IRP.
Другие структуры ядра, с которыми мы должны быть знакомы при нашем анализе, - это структуры IRP и IO_STACK_LOCATION.
IRP, также известный как пакет запроса ввода-вывода, представляет собой структуру, представляющую запрос ввода-вывода во время его создания, перемещения между различными драйверами в стеке устройств и до момента завершения запроса.
IRP создается, когда из пользовательского режима вызывается DeviceIoControl с определенной операцией IOCTL на дескрипторе объекта устройства, полученном пользователем.
*Фрагмент 6: Описание структуры IRP.
Кроме того, IO_STACK_LOCATION представляет текущее положение IRP в стеке устройств (и, следовательно, поле CurrentLocation в структуре IRP является указателем на IO_STACK_LOCATION).
Структура IO_STACK_LOCATION содержит поле Parameters типа union, которое указывает различные параметры, используемые различными основными функциями в драйвере.
Например, если текущая операция - IRP_MJ_DEVICE_CONTROL, будут использоваться параметры типа DeviceIoControl, содержащие OutputBufferLength, InputBufferLength, IoControlCode и Type3InputBuffer.
*Фрагмент 7: Описание структуры IO_STACK_LOCATION.
Вооружившись новыми знаниями о драйверах ядра Windows и способах поиска ключевых функций в драйверах Windows, давайте рассмотрим некоторые примеры из реального мира, встречающиеся на практике.
Кейс-стади №1: Кампания APT29 с использованием Brute Ratel C4 и распространением руткита 'Husky'
Это исследование возникло в результате анализа образцов, связанных с кампанией, упомянутой также в блоге Palo Alto Networks Unit 42 о Brute Ratel C4. К сожалению, они не предоставили технического анализа этого образца, поэтому мы решили более глубоко исследовать его самостоятельно.
Детали образца
MD5 9b664450b36154b74d610f0e22e27814
SHA-1 af26cd435ff3858af6ad2d44c24e887e7dd0ca88
SHA-256 31acf37d180ab9afbcf6a4ec5d29c3e19c947641a2d9ce3ce56d71c1f576c069
Imphash 5b3ab951f23e44df83ede26ae92f6bee
SSDEEP 6144:+K2v/VfyLez5cjWNYXBtIhMDXdiq+o5IDvCzwg:Wv/VfyLU5cjC0QUXddgvC1
Размер файла 284,92 КБ (291760 байт)
Обзор образца
Образец представляет собой драйвер ядра, подписанный утекшим сертификатом NVIDIA от группы LAP$US. Он использует метод Heresy's Gate, найденный zerosum0x0 (Рисунок 1), который является техникой инжекции кода в режиме пользователя из драйвера ядра, обходя SMEP.
*Рисунок 1: Дизассемблирование подписанного драйвера, использующего метод Heresy's Gate от zerosum0x0.
Внедренный шеллкод использует классические техники, такие как обход списка InLoadOrderModuleList для поиска дескрипторов библиотек и разрешение API-функций, таких как LoadLibraryA и GetProcAddress, которые могут быть использованы для разрешения любых других API.
Также шеллкод довольно длинный для анализа (Рисунок 2) и очень похож на шеллкод, описанный в упомянутом блоге Unit 42, поскольку он использует множественные инструкции push для хранения данных в стеке. Данные, хранящиеся в стеке, включают:
*Рисунок 2: Фрагмент шеллкода, помещающего множество значений в стек и формирующего Base64 блоб.
Конфигурацию Brute Ratel C4 можно расшифровать с помощью следующего короткого скрипта (Фрагмент 8):
Фрагмент 8: Код, используемый для декодирования и расшифровки конфигурации из Base64 блоба, извлеченного из стека.
После расшифровки конфигурации мы получаем следующий результат:
*Фрагмент 9: Пример расшифрованной конфигурации.
Расшифрованные данные конфигурации (Фрагмент 9) включают в себя базовые настройки для полезной нагрузки Brute Ratel C4, включая адрес и порт сервера управления (C2) для начала общения, Base64-кодированный шаблон того, как должен выглядеть запрос к серверу C2, и различные пути на сервере C2 для разнообразных функций и опций.
*Рисунок 3: Разбор сценария атаки.
Мы обнаружили, что x64 руткит, установленный вместе с образцом Brute Ratel C4 на зараженной машине, был более интересным, так как он был полностью проигнорирован другими производителями, анализирующими этот же образец.
Руткит "Husky"
Как мы уже упоминали, x64 rootkit, который мы окрестили как руткит "Husky", был распространён вместе с полезной нагрузкой Brute Ratel.
Драйвер ядра был защищён с помощью VMProtect и подписан сертификатом, выданным на имя "SHANGMAO CHEN" (Рисунок 4).
*Рисунок 4: Сертификат, использованный руткитом.
*Рисунок 5: VMProtect-защищенный DriverEntry, показывающий безусловную инструкцию перехода в качестве первой инструкции.
Однако после распаковки мы нашли функции, такие как GsDriverEntry, которые содержат гораздо больше информации, а также важные строки (Рисунок 6), которые мы можем использовать в нашем анализе.
*Рисунок 6: Дизассемблирование ветвления из GsDriverEntry, содержащее строки URL-адресов с thpt (перемешанная версия HTTP) в качестве протокола URL.
C2 Коммуникация
Руткит напрямую взаимодействует с \Device\Tcp для осуществления связи. По этой причине подключения остаются скрытыми от инструментов пользовательского режима, таких как netstat и tcpview, работающих на зараженной машине.
Альтернативой может служить использование Wireshark на хост-машине виртуальной машины для перехвата совместно используемого сетевого интерфейса гостевой машины с целью мониторинга всего трафика коммуникации зараженной ВМ (Рисунки 7 и 8).
*Рисунок 7: Захват сетевого трафика в Wireshark, инициированного руткитом.
Вредоносное ПО общается с несколькими доменами и относительными путями для каждого домена.
*Рисунок 8: Веб-запрос и ответ от сервера по пути /xccdd в URL, показывающие полезную нагрузку ответа.
Стеганография
Внимание привлек определенный HTTP-трафик с изображениями (JPEG – заголовок JFIF), которые были загружены с следующего URL: http://pic.rmb.bdstatic.com//bjh/.jpeg.
JPEG-файлы (Рисунок 9) содержали изображения собак, которые на первый взгляд казались совершенно невинными, поэтому я назвал руткит "Husky" в честь этих изображений. Следует добавить, что это доказательство того, что я плохо разбираюсь в породах собак, так как мне позже сказали, что на этих изображениях на самом деле не хаски.
*Pисунок 9: Фотография собаки, которая мне показалась хаски и содержала скрытую полезную нагрузку.
Каждый JPEG также содержал стеганографическую полезную нагрузку в виде данных, добавленных в конец изображения по смещению 0x1769 после разделителя из нескольких нулей (Рисунок 10).
*Рисунок 10: Представление в Hexview разделителя между концом изображения и началом скрытой полезной нагрузки в .jpg с изображением собаки.
Анализируя данные, мы видим, что первые 32 байта совпадают с ответом сервера на предыдущий запрос по адресу hxxp://rxeva6w.com:10100/xccdd в формате hexlified (Фрагмент 10).
*Snippet 10: First 32 bytes of the payload similar across different payloads.
Иронично, но домен rxeva6w.com имеет 0/88 обнаружений (Рисунок 11).
*Рисунок 11: VirusTotal показывает нулевой уровень обнаружения (0/88) для домена rveva6w.com.
Шифрование
Алгоритм шифрования/дешифрования, используемый полезными нагрузками HTTP, представляет собой слегка модифицированный алгоритм DES с ключом “j_k*a-vb” (Рисунок 12).
*Рисунок 12: Ключ шифрования передается функции расшифровки DES.
Дополнительные Функции
Помимо коммуникации по HTTP и скрытия подключений, этот руткит также способен загружать новые модули, скачанные с различных URL-адресов.
Очевидно, этот руткит обладает дополнительными функциями, которые мы не рассматриваем в этом блоге, поэтому мы можем опубликовать продолжение в следующем посте или дать дополнительное обновление в будущем, поскольку мы продолжаем наш анализ.
Кейс-стади №2: Руткит Mingloa (CopperStealer)
Вредоносное ПО Mingloa было впервые обнаружено и названо ESET в 2019 году.
Позже о нем написала компания Proofpoint в этом блогпосте, где оно также получило название CopperStealer.
Считается, что у Mingloa китайское происхождение, что отражено в его названии. Это связано с короткой процедурой в компоненте пользователя, которая проверяет, не установлен ли язык упрощенного китайского (Рисунок 13), иначе происходит выход из программы.
*Рисунок 13: Проверка локали на упрощенный китайский язык.
В оригинальном блогпосте Proofpoint говорится следующее: “Анализируемый образец также может загрузить и активировать драйвер ядра. На данный момент цель этого драйвера неизвестна.“ Конечно, это утверждение побудило нас к дальнейшему исследованию.
Как отмечено в исследовании Proofpoint, вредоносное ПО содержит функции для поиска и кражи сохраненных паролей браузера. Помимо сохраненных паролей браузера, вредоносное ПО использует сохраненные куки-файлы для получения токена доступа пользователя от Facebook.
Это один из многих случаев, когда общие методы защиты учетных данных и токенов безопасности, такие как включенные в CyberArk Endpoint Privilege Manager, могут значительно ограничить воздействие программ-воров учетных данных, таких как CopperStealer. Если использовать эти методы, CopperStealer не сможет извлечь данные с зараженного компьютера (для получения дополнительной информации о методах скрапинга паролей из браузеров см. предыдущий блог CyberArk Labs).
Детали образца
MD5 6f38ca637f7978cefe7bf4dfcfeb9ad6
SHA-1 eb301689bb5154b90c0724cba47a3c8574120b42
SHA-256 d4d3127047979a1b9610bc18fd6a4d2f8ac0389b893bcb36506759ce2f20e7e4
Imphash 9192d1abce0f933180e0e907444e8bec
SSDEEP 384:ySAZEVur6CDbw+ynZDvZZvHnQZvZyEPJvHwr:yzZ0utw+yJOQr
Размер файла 21,27 КБ (21784 байта)
Обзор образца
Этот вредоносный модуль ядра был скомпилирован как для архитектур x86, так и x64.
*Рисунок 14: Анализ сценария атаки вредоносного ПО.
Драйвер подписан сертификатом, выданным на 大连纵梦网络科技有限公司 (Рисунок 15), что переводится как "Dalian Longmeng Network Technology Co. Ltd" или "Dalian Morningstar Network Technology". Возможно, этот сертификат был украден с зараженного компьютера или утек через сотрудника.
*Рисунок 15: Сертификат, выданный на "Dalian Longmeng Network Technology", использованный для подписи драйвера.
Установка Из Режима Пользователя
Давайте сначала рассмотрим рутину заражения вредоносным ПО в пользовательском режиме, предназначенную для развертывания драйвера (Рисунок 16).
*Рисунок 16: Дизассемблирование потока выполнения компонента в пользовательском режиме для установки драйвера.
Изучая этот фрагмент, мы видим, что функция InstallDriver получает один аргумент и сначала вызывается с значением аргумента 0. Во второй раз она вызывается со значением аргумента 1.
Если присмотреться к InstallDriver, видно, что она сначала пытается создать семафор (Рисунки 17 и 18), затем проверяет версию Windows. Если любой из этих вызовов не удастся, она завершится, не сделав ничего.
*Рисунок 17: Дизассемблирование начала функции InstallDriver в бинарном файле, где вызывается CreateSemaphoreWrapper.
Если предыдущие проверки прошли успешно, то вредоносное ПО продолжит работу, останавливая и удаляя любые службы с тем же именем и, в конце концов, сравнивая аргумент shouldInstallDriver со значением 0.
*Рисунок 18: Дизассемблирование функции CreateSemaphoreWrapper.
Если значение shouldInstallDriver равно 0, функция завершится без выполнения дополнительных инструкций. В противном случае она продолжит процесс установки соответствующего драйвера (Рисунок 19), встроенного в бинарный файл, в соответствии с архитектурой системы.
*Рисунок 19: Дизассемблирование функции InstallDriver, описывающее процесс установки драйвера в системе.
Эта часть кода также содержит логическую ошибку, которая не позволяет этому драйверу когда-либо быть загруженным.
Первый вызов InstallDriver, который предполагается использовать только для удаления существующего драйвера, также создаст семафор.
Второй вызов, который должен также установить драйвер, завершится преждевременно до того, как драйвер будет установлен, поскольку семафор уже существует.
Эта логическая ошибка является несколько загадочной, так как вредоносное ПО обычно тестируется на наличие таких ошибок. В данном случае оно либо было развернуто спешно без тестирования, либо еще не предназначалось для развертывания на зараженных машинах.
DriverEntry
Компонент ядра этого вредоносного ПО является устаревшим фильтровым драйвером файловой системы, который, в отличие от более современного мини-фильтра, может изменять поведение системы без использования функций обратного вызова фильтрации, таких как процедуры обратного вызова перед операцией или после операции.
Устаревшие фильтровые драйверы файловой системы могут напрямую изменять поведение файловой системы и вызываются для каждой операции ввода-вывода, такой как CREATE, READ и WRITE.
Изучая DriverEntry (Рисунок 20), мы видим, что две основные функции назначены для IRP_MJ_READ и IRP_MJ_SET_INFORMATION. Кроме того, регистрируются две функции обратного вызова — одна с использованием CmRegisterCallback, а другая — с использованием IoRegisterFsRegistrationChange.
*Рисунок 20: Дизассемблирование DriverEntry драйвера руткита Mingloa.
Когда вызывается IoRegisterFsRegistrationChange, в неё передаётся указатель на функцию DriverNotificationRoutine, цель которой заключается в подключении или отключении фильтрового драйвера в зависимости от того, активна ли файловая система или нет (Рисунок 21).
*Рисунок 21: Дизассемблирование функции DriverNotificationRoutine.
Авторы вредоносного ПО создали драйвер со следующими функциями, основанными на фильтровом драйвере:
Прикрепляя драйвер как фильтровый драйвер к файловой системе и реализуя IRP_MJ_SET_INFORMATION (Рисунок 22), авторы могут проверять имя файла, которое предполагается удалить, в списке запрещенных.
*Рисунок 22: Дизассемблирование функции IrpMjSetInformationHandler.
Если имя файла находится в списке запрещенных, обработчик вернет STATUS_ACCESS_DENIED и прекратит обработку IRP. В противном случае он передаст его дальше в лежащий в основе драйвер в стеке устройств (Рисунок 23).
*Рисунок 23: Дизассемблирование функции IrpMjGenericHandler.
Предотвращение Удаления Ключей Реестра
Функция предотвращения удаления ключей реестра и связанных с ними значений защищает реестр, связанный со службой ядерного драйвера Windows.
Эта функция работает за счет регистрации процедуры обратного вызова реестра (RegistryCallback), которая активируется при каждом изменении в реестре и сравнивает путь в реестре с путем службы.
Предотвращение Чтения Запрещенных Файлов (За Исключением Разрешенных Процессов)
Эта функция использует тот же механизм фильтрового драйвера файловой системы, который описан в функции предотвращения самоудаления для IRP_MJ_READ (Рисунок 24).
*Рисунок 24: Дизассемблирование функции IrpMjReadHandler.
Она сначала проверяет имя файла, к которому осуществляется доступ, затем проверяет, содержит ли имя или заканчивается одной из следующих запрещенных строк:
*Рисунок 25: Пример попытки вывода содержимого файла cookies.db, когда руткит загружен.
Обфускация Строк
Во многих случаях руткит скрывает важные строки, такие как список запрещенных имен файлов или список разрешенных имен процессов, используя следующую обфускацию. Он инициализирует строку с REGISTRY\MACHINE\SOFTWARE и использует различные битовые арифметические манипуляции (Рисунок 26) для раскрытия множества строк, таких как:
*Рисунок 26: Вид дизассемблирования техники деобфускации строк.
Хотя нам хотелось бы создать скрипт для раскрытия этих обфусцированных строк, к сожалению, авторы затруднили это, используя случайные битовые операции и значения для каждой строки.
Поиск Руткитов
В отличие от вредоносного ПО пользовательского режима, которое в основном импортирует из библиотек, таких как kernel32.dll и ntdll.dll, руткиты ядра почти исключительно импортируют свои API-функции из ntoskrnl.exe, что является самим ядром. Этот факт полезен при поиске руткитов в VirusTotal (VT), так как он упрощает поиск драйверов с вредоносными намерениями.
Например, мы можем использовать следующий запрос (Фрагмент 11):
*Фрагмент 11: Пример запроса в VirusTotal для поиска вредоносных драйверов.
Запрос будет искать файлы в формате PE, которые не подписаны или не являются доверенными, и импортируют их из ntoskrnl.exe.
Другой вариант - использовать правило Yara при поиске более конкретного набора файлов.
Мы также можем использовать уникальное использование API с дополнительным бинарным шаблоном или строками для поиска новых образцов нашего вредоносного драйвера или руткита.
Так же, как и при анализе образца и поиске похожих файлов (старых или новых), мы можем использовать некоторые свойства кода, такие как тег, используемый в ExAllocatePoolWithTag и символы .pdb, для поиска файлов, связанных с нашим исходным бинарным файлом.
Пример такого правила будет следующим (Фрагмент 12):
*Сниппет 12: Пример правила Yara для поиска вредоносных драйверов (они же руткиты).
Заключение: "Руткиты не ушли в прошлое"
Как мы видели на примерах в этом блоге, руткиты все еще активны и нацелены на современные версии Windows, включая Windows 10 и 11 в архитектурах x86 и x64.
Мы увидели, что руткиты эволюционировали от методов Hooking и DKOM, о которых мы рассказывали в прошлом блоге, к другим методам, таким как драйверы фильтра файловой системы и подписанные драйверы с помощью украденных сертификатов, чтобы избежать срабатывания PatchGuard и "обойти" средства защиты DSE, а также решения EDR (endpoint detection and remediation).
Такие продукты, как CyberArk Endpoint Privilege Manager, могут предотвратить подобные угрозы, используя контроль наименьших привилегий или просто удалив учетную запись администратора из системы и тем самым предотвратив установку новых драйверов, поскольку ни один непривилегированный пользователь в системе не имеет прав на установку драйвера.
Resources
https://codemachine.com/articles/kernel_structures.html
https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/registering-fast-i-o-dispatch-routines
https://github.com/apriorit/file-system-filter
ORIGINAL ARTICLE IN ENGLISH
Translated from english to russian by S3VE7N

В предыдущей части (часть 1) мы рассмотрели несколько вариантов реализации руткитов. Теперь мы сосредоточимся на анализе руткитов ядра, рассмотрев два примера руткитов, найденных в природе: Husky Rootkit и Mingloa/CopperStealer Rootkit. На этих примерах мы поделимся нашими знаниями о методах и методологии анализа руткитов.
Прежде чем мы погрузимся в анализ, приведем несколько рекомендаций о том, как мы подходили к этому драйверу ядра Windows, а также некоторые предварительные знания, которые помогут понять назначение ключевых функций в двоичном файле.
DriverEntry

Давайте начнем с точки входа двоичного файла. В случае драйвера ядра Windows это DriverEntry.
Обычно DriverEntry включает следующие блоки кода:
- Вызовы функций IoCreateDevice и IoCreateSymbolicLink.
- Инициализация массива Major Function указателями на функции различных обработчиков.
- Назначение процедуры DriverUnload указателем на функцию-обработчик.
C:
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
DbgPrint("Привет, мир!\n");
UNICODE_STRING deviceName;
UNICODE_STRING symbolicLink;
RtlInitUnicodeString(&deviceName, L"\\Device\\TeaParty");
RtlInitUnicodeString(&symbolicLink, L"\\DosDevices\\TeaParty");
IoCreateDevice(DriverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &ptrDeviceObject);
IoCreateSymbolicLink(&symbolicLink, &deviceName);
DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;
DriverObject->MajorFunction[IRP_MJ_READ] = DriverRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DriverWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
DriverDeviceControl; DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
Следующий фрагмент кода (Фрагмент 2) демонстрирует, как выглядит дизассемблирование той же функции DriverEntry.
C:
sub_140001690 proc near
var_48= dword ptr -48h
Exclusive= byte ptr -40h
DeviceObject= qword ptr -38h
DestinationString= _UNICODE_STRING ptr -28h
SymbolicLinkName= _UNICODE_STRING ptr -18h
push rbx
sub rsp, 60h
mov rbx, rcx
lea rcx, aHelloWorld ; "Привет, мир!\n"
call DbgPrint
lea rdx, SourceString ; "\\Device\\TeaParty"
lea rcx, [rsp+68h+DestinationString] ; DestinationString
call cs:RtlInitUnicodeString
lea rdx, aDosdevicesTeap ; "\\DosDevices\\TeaParty"
lea rcx, [rsp+68h+SymbolicLinkName] ; DestinationString
call cs:RtlInitUnicodeString
lea rax, DeviceObject
mov r9d, 22h ; '"' ; DeviceType
mov [rsp+68h+DeviceObject], rax ; DeviceObject
lea r8, [rsp+68h+DestinationString] ; DeviceName
mov [rsp+68h+Exclusive], 0 ; Exclusive
xor edx, edx ; DeviceExtensionSize
and [rsp+68h+var_48], 0
mov rcx, rbx ; DriverObject
call cs:IoCreateDevice
lea rdx, [rsp+68h+DestinationString] ; DeviceName
lea rcx, [rsp+68h+SymbolicLinkName] ; SymbolicLinkName
call cs:IoCreateSymbolicLink
lea rax, sub_140001280
mov [rbx+70h], rax
lea rax, sub_140001280
mov [rbx+80h], rax
lea rax, sub_140001280
mov [rbx+88h], rax
lea rax, sub_140001280
mov [rbx+90h], rax
lea rax, sub_1400012B0
mov [rbx+0E0h], rax
lea rax, sub_1400014B0
mov [rbx+68h], rax
xor eax, eax
add rsp, 60h
pop rbx
retn
sub_140001690 endp
DriverUnload

DriverUnload - это функция, которая вызывается при выгрузке драйвера.
Цель этой функции-обработчика - очистить все ресурсы, созданные драйвером во время его инициализации и выполнения — например, удалить созданные в DriverEntry устройство и символическую ссылку.
Также будет стратегически правильно вызвать функцию ExFreePoolWithTag для освобождения памяти пула, выделенной в функции DriverEntry.
C:
void DriverUnload(PDRIVER_OBJECT pDriverObject)
{
UNREFERENCED_PARAMETER(pDriverObject);
UNICODE_STRING deviceName;
UNICODE_STRING symbolicLink;
RtlInitUnicodeString(&deviceName, L"\Device\TeaParty");
RtlInitUnicodeString(&symbolicLink, L"\DosDevices\TeaParty");
IoDeleteDevice(ptrDeviceObject);
IoDeleteSymbolicLink(&symbolicLink);
DbgPrint("Выгрузка драйвера\n");
}
Структуры Ядра Windows

Для полного понимания дизассемблирования драйвера ядра Windows, мы также должны быть знакомы с некоторыми структурами ядра, используемыми диспетчером объектов и другими компонентами в ядре.
Например, следующая структура - это DRIVER_OBJECT (Фрагмент 4).
C:
0: kd> dt nt!_DRIVER_OBJECT
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x008 DeviceObject : Ptr64 _DEVICE_OBJECT
+0x010 Flags : Uint4B
+0x018 DriverStart : Ptr64 Void
+0x020 DriverSize : Uint4B
+0x028 DriverSection : Ptr64 Void
+0x030 DriverExtension : Ptr64 _DRIVER_EXTENSION
+0x038 DriverName : _UNICODE_STRING
+0x048 HardwareDatabase : Ptr64 _UNICODE_STRING
+0x050 FastIoDispatch : Ptr64 _FAST_IO_DISPATCH
+0x058 DriverInit : Ptr64 long
+0x060 DriverStartIo : Ptr64 void
+0x068 DriverUnload : Ptr64 void
+0x070 MajorFunction : [28] Ptr64 long
Важно отследить основные функции IRP, используемые драйвером, при его реверс-инжиниринге.
Например, анализируя смещения структуры (Фрагмент 4) и дизассемблирование (Фрагмент 2), мы можем определить, что sub_1400014B0 - это DriverUnload.
Мы также можем использовать значения кодов основных функций IRP, описанные в wdm.h/ntddk.h, чтобы заключить, что sub_140001280 (в Фрагменте 2) - это обработчик функции для IRP_MJ_CREATE, проверив основную функцию кода, которая даст нам результат 0x70 из смещения MajorFunction (0x70) в структуре DRIVER_OBJECT. Это очевидно 0x00*РазмерУказателя (8 в архитектуре x64); следовательно, мы имеем дело с IRP_MJ_CREATE.
Таким же образом мы можем определить, что являются обработчиками функций для IRP_MJ_CLOSE, IRP_MJ_READ, IRP_MJ_WRITE и IRP_MJ_DEVICE_CONTROL.
C:
//
// Определение основных кодов функций для IRP.
//
#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_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 IRP_MJ_PNP // Устаревшие....
#define IRP_MJ_MAXIMUM_FUNCTION 0x1b
Другие структуры ядра, с которыми мы должны быть знакомы при нашем анализе, - это структуры IRP и IO_STACK_LOCATION.
IRP, также известный как пакет запроса ввода-вывода, представляет собой структуру, представляющую запрос ввода-вывода во время его создания, перемещения между различными драйверами в стеке устройств и до момента завершения запроса.
IRP создается, когда из пользовательского режима вызывается DeviceIoControl с определенной операцией IOCTL на дескрипторе объекта устройства, полученном пользователем.
C:
0: kd> dt nt!_IRP
+0x000 Type : Int2B
+0x002 Size : Uint2B
+0x004 AllocationProcessorNumber : Uint2B
+0x006 Reserved : Uint2B
+0x008 MdlAddress : Ptr64 _MDL
+0x010 Flags : Uint4B
+0x018 AssociatedIrp : <unnamed-tag>
+0x020 ThreadListEntry : _LIST_ENTRY
+0x030 IoStatus : _IO_STATUS_BLOCK
+0x040 RequestorMode : Char
+0x041 PendingReturned : UChar
+0x042 StackCount : Char
+0x043 CurrentLocation : Char
+0x044 Cancel : UChar
+0x045 CancelIrql : UChar
+0x046 ApcEnvironment : Char
+0x047 AllocationFlags : UChar
+0x048 UserIosb : Ptr64 _IO_STATUS_BLOCK
+0x050 UserEvent : Ptr64 _KEVENT
+0x058 Overlay : <unnamed-tag>
+0x068 CancelRoutine : Ptr64 void
+0x070 UserBuffer : Ptr64 Void
+0x078 Tail : <unnamed-tag></unnamed-tag></unnamed-tag></unnamed-tag>
Кроме того, IO_STACK_LOCATION представляет текущее положение IRP в стеке устройств (и, следовательно, поле CurrentLocation в структуре IRP является указателем на IO_STACK_LOCATION).
Структура IO_STACK_LOCATION содержит поле Parameters типа union, которое указывает различные параметры, используемые различными основными функциями в драйвере.
Например, если текущая операция - IRP_MJ_DEVICE_CONTROL, будут использоваться параметры типа DeviceIoControl, содержащие OutputBufferLength, InputBufferLength, IoControlCode и Type3InputBuffer.
C:
0: kd> dt nt!_IO_STACK_LOCATION
+0x000 MajorFunction : UChar
+0x001 MinorFunction : UChar
+0x002 Flags : UChar
+0x003 Control : UChar
+0x008 Parameters : <unnamed-tag>
+0x000 Create : <unnamed-tag>
+0x000 SecurityContext : Ptr64 _IO_SECURITY_CONTEXT
+0x008 Options : Uint4B
+0x010 FileAttributes : Uint2B
+0x012 ShareAccess : Uint2B
+0x018 EaLength : Uint4B
+0x000 Read : <unnamed-tag>
+0x000 Length : Uint4B
+0x008 Key : Uint4B
+0x010 ByteOffset : _LARGE_INTEGER
+0x000 Write : <unnamed-tag>
+0x000 Length : Uint4B
+0x008 Key : Uint4B
+0x010 ByteOffset : _LARGE_INTEGER
+0x000 DeviceIoControl : <unnamed-tag>
+0x000 OutputBufferLength : Uint4B
+0x008 InputBufferLength : Uint4B
+0x010 IoControlCode : Uint4B
+0x018 Type3InputBuffer : Ptr64 Void
+0x028 DeviceObject : Ptr64 _DEVICE_OBJECT
+0x030 FileObject : Ptr64 _FILE_OBJECT
+0x038 CompletionRoutine : Ptr64 long
+0x040 Context : Ptr64 Void
</unnamed-tag></unnamed-tag></unnamed-tag></unnamed-tag></unnamed-tag>
Вооружившись новыми знаниями о драйверах ядра Windows и способах поиска ключевых функций в драйверах Windows, давайте рассмотрим некоторые примеры из реального мира, встречающиеся на практике.
Кейс-стади №1: Кампания APT29 с использованием Brute Ratel C4 и распространением руткита 'Husky'

Это исследование возникло в результате анализа образцов, связанных с кампанией, упомянутой также в блоге Palo Alto Networks Unit 42 о Brute Ratel C4. К сожалению, они не предоставили технического анализа этого образца, поэтому мы решили более глубоко исследовать его самостоятельно.
Детали образца
MD5 9b664450b36154b74d610f0e22e27814
SHA-1 af26cd435ff3858af6ad2d44c24e887e7dd0ca88
SHA-256 31acf37d180ab9afbcf6a4ec5d29c3e19c947641a2d9ce3ce56d71c1f576c069
Imphash 5b3ab951f23e44df83ede26ae92f6bee
SSDEEP 6144:+K2v/VfyLez5cjWNYXBtIhMDXdiq+o5IDvCzwg:Wv/VfyLU5cjC0QUXddgvC1
Размер файла 284,92 КБ (291760 байт)
Обзор образца
Образец представляет собой драйвер ядра, подписанный утекшим сертификатом NVIDIA от группы LAP$US. Он использует метод Heresy's Gate, найденный zerosum0x0 (Рисунок 1), который является техникой инжекции кода в режиме пользователя из драйвера ядра, обходя SMEP.
*Рисунок 1: Дизассемблирование подписанного драйвера, использующего метод Heresy's Gate от zerosum0x0.
Внедренный шеллкод использует классические техники, такие как обход списка InLoadOrderModuleList для поиска дескрипторов библиотек и разрешение API-функций, таких как LoadLibraryA и GetProcAddress, которые могут быть использованы для разрешения любых других API.
Также шеллкод довольно длинный для анализа (Рисунок 2) и очень похож на шеллкод, описанный в упомянутом блоге Unit 42, поскольку он использует множественные инструкции push для хранения данных в стеке. Данные, хранящиеся в стеке, включают:
- Base64-кодированные конфигурационные данные для Brute Ratel C4
- Полезная нагрузка Brute Ratel C4
- Портативный исполняемый файл (PE) 64, который является упакованным с помощью VMProtect драйвером ядра и загружается позже
*Рисунок 2: Фрагмент шеллкода, помещающего множество значений в стек и формирующего Base64 блоб.
Конфигурацию Brute Ratel C4 можно расшифровать с помощью следующего короткого скрипта (Фрагмент 8):
Python:
from base64 import b64decode
from Crypto.Cipher import ARC4
key = "bYXJm/3#M?:XyMBF"
config = ARC4.new(key).decrypt(b64decode('bScTbyzbJIZKRbUKJNxk4KSWzypzwOlmKYpJMoODY+J6JpEARPoRxs/8XbJFbiITTg2iIZaq5GO76zB8kqR4wcMkKLxkjeqCnSYWF/s7CuFuokklDxTJRJQBDg0RYuCbfJ/kbRvGSESP3LP1rRG3z+rArJ44a3w62sKyShanpLXIcpDJPD6qxSJt2rYEP2ZY6yKeKlDvRKCaBbn3dBUK9Hgo/pPUC9nju1UGm/rEt+igbAIcyRKqK1G0MR/N7HumXP358JWc4fHJHOjJtTZVaM/9xjjPlXnWUJCA4pCNiBxzK0l/C+v5FS/nMVro34SdLE/PBDSuDr/7cI06rnupd8/h/tnJY0IaTPOWGQbQZU8DAycX8mgypjju//q3sSS47fl8Cgl94slHqnHCaM6lDmXp9UZc+Qf0FtXb+JkYQQNjGDUJi/LThu2wTV5N8aYI2gTnPSqHBfEOt781Z8uqQn8dTQv63MUv0gqpaQgY4ocM64L1WqZBxWcBDoA90W7s9NR62UIPtMp3+M3/aygdtNvXATCSxpVfwy+TlHE2/rc16NdzCE/qbtFC+B6uwWr9kOt+ep20JmOJGpcPmk/89Iix141g5ZlsVo7IDfQMwWEeB3wpastWCjQmUEwQLsybAcEkECUL3Jq903jtN5+dJ/DoDspq+ZGYkvdPXTC2YJvSPQ36hcEd3FzwkZs3TLMwQc5KGQB1v4QLL/PF01s9yigv6MUciA05buztn+Ho/8+pbIS4RP3ZyKyaLeuAZzTIh0DE4Hmm6ADZgOmEp/K/JiF2+gUtw4TxxcJt3TJzR+o/2gAmO20tFrxX+5xEZpFgTC++2gbknK3HmH/RHjH8TgGD+NUpn68ZmjHfMrrs+J/nQcPYB1G1cDlBlgRvPzO1a4UBEqSobVPNPsTrQl8hSW2rlexByS3G03aacYqj5TDG8ITXP0q9DLsH9njNgc1aOCe5YtdaLKcZIaHUCrb4cVyQLepQ7Iwv5Pj4UZY9q4BecRF6hl2x6wOFPNwHNaZO8cxMiDi/QvkpW/PoqSfcw67/DHmcCpy3b28Pu2/Ews3JeBJBpgrm4kXGxgutdaRnxz+VUo56zPyM8WKXgilLliTOWQ/zjzToiM8My3F1ylL3e1Bu9JANzx3kRoYGXCbS7fMv/hC4FEl0d4h8AIpOFniIe//MEOEDErda3VhgiARQ5A=='))
Фрагмент 8: Код, используемый для декодирования и расшифровки конфигурации из Base64 блоба, извлеченного из стека.
После расшифровки конфигурации мы получаем следующий результат:
Код:
[
'PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxiYXRjaD4KICAgIDxhZGQgaWQ9ImVKanhEMlZva2FDcFRwUE4iPgogICAgICAgIDxhdXRob3I+R2FtYmFsdWUsVmVsIElOQzwvYXV0aG9yPgogICAgICAgIDx0aXRsZT5YTUwgRGV2ZWxvcGVyJ3MgR3VpZGU8L3RpdGxlPgogICAgICAgIDxnZW5yZT5TWVNURU08L2dlbnJlPgogICAgICAgIDxjb3VudD4yMTEwNDI3OTg3MzExMDcwPC9jb3VudD4KICAgICAgICA8cmVsZWFzZXM+MjAwMC0xMC0wMTwvcmVsZWFzZXM+CiAgICAgICAgPGRlc2NyaXB0aW9uPg==',
'PC9kZXNjcmlwdGlvbj4KICAgIDwvYWRkPgo8L2JhdGNoPgo=',
'0',
'1',
'ds.windowsupdate.eu.org',
'443',
'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko',
'dZuSxhxTjFGSI5hWuuDH',
'akrKnFLZK9IRaWVRL1LX',
'/previous-versions/windows,/latest/developerguide/documents-batch-xml.html,/XBLWinClient/v10_video/configuration.xml,/verifyservice/servicechannel.hxs,/AS/API/WindowsCortanaPane/V2/Suggestions,/windows/status/actions,/node/report.xml,/staticsb/statics/latest/fixed/wdgts_conf.xml,/threshold/xls.aspx',
'Content-Type: application/xhtml+xml',
''
]
Расшифрованные данные конфигурации (Фрагмент 9) включают в себя базовые настройки для полезной нагрузки Brute Ratel C4, включая адрес и порт сервера управления (C2) для начала общения, Base64-кодированный шаблон того, как должен выглядеть запрос к серверу C2, и различные пути на сервере C2 для разнообразных функций и опций.
*Рисунок 3: Разбор сценария атаки.
Мы обнаружили, что x64 руткит, установленный вместе с образцом Brute Ratel C4 на зараженной машине, был более интересным, так как он был полностью проигнорирован другими производителями, анализирующими этот же образец.
Руткит "Husky"
Как мы уже упоминали, x64 rootkit, который мы окрестили как руткит "Husky", был распространён вместе с полезной нагрузкой Brute Ratel.
Драйвер ядра был защищён с помощью VMProtect и подписан сертификатом, выданным на имя "SHANGMAO CHEN" (Рисунок 4).
*Рисунок 4: Сертификат, использованный руткитом.
DriverEntry
Поскольку эта функция DriverEntry (Рисунок 5) упакована и обфусцирована, получить из неё какую-либо информацию довольно сложно. Она начинается с серии безусловных переходов (jmp) и фактически ведет к стабу распаковки VMProtect.
*Рисунок 5: VMProtect-защищенный DriverEntry, показывающий безусловную инструкцию перехода в качестве первой инструкции.
Однако после распаковки мы нашли функции, такие как GsDriverEntry, которые содержат гораздо больше информации, а также важные строки (Рисунок 6), которые мы можем использовать в нашем анализе.
*Рисунок 6: Дизассемблирование ветвления из GsDriverEntry, содержащее строки URL-адресов с thpt (перемешанная версия HTTP) в качестве протокола URL.
C2 Коммуникация
Руткит напрямую взаимодействует с \Device\Tcp для осуществления связи. По этой причине подключения остаются скрытыми от инструментов пользовательского режима, таких как netstat и tcpview, работающих на зараженной машине.
Альтернативой может служить использование Wireshark на хост-машине виртуальной машины для перехвата совместно используемого сетевого интерфейса гостевой машины с целью мониторинга всего трафика коммуникации зараженной ВМ (Рисунки 7 и 8).
*Рисунок 7: Захват сетевого трафика в Wireshark, инициированного руткитом.
Вредоносное ПО общается с несколькими доменами и относительными путями для каждого домена.
*Рисунок 8: Веб-запрос и ответ от сервера по пути /xccdd в URL, показывающие полезную нагрузку ответа.
Стеганография
Внимание привлек определенный HTTP-трафик с изображениями (JPEG – заголовок JFIF), которые были загружены с следующего URL: http://pic.rmb.bdstatic.com//bjh/.jpeg.
JPEG-файлы (Рисунок 9) содержали изображения собак, которые на первый взгляд казались совершенно невинными, поэтому я назвал руткит "Husky" в честь этих изображений. Следует добавить, что это доказательство того, что я плохо разбираюсь в породах собак, так как мне позже сказали, что на этих изображениях на самом деле не хаски.
*Pисунок 9: Фотография собаки, которая мне показалась хаски и содержала скрытую полезную нагрузку.
Каждый JPEG также содержал стеганографическую полезную нагрузку в виде данных, добавленных в конец изображения по смещению 0x1769 после разделителя из нескольких нулей (Рисунок 10).
*Рисунок 10: Представление в Hexview разделителя между концом изображения и началом скрытой полезной нагрузки в .jpg с изображением собаки.
Анализируя данные, мы видим, что первые 32 байта совпадают с ответом сервера на предыдущий запрос по адресу hxxp://rxeva6w.com:10100/xccdd в формате hexlified (Фрагмент 10).
Код:
“\x97\x17\xa1\xfd\x4f\x88\xe2\x84\x19\x35\x09\x3e\x93\xcb\x1e\xf2\x5d\x96\x88\x29\x6a\xf8\x9b\x99\xbc\x57\x87\xf5\x6d\x6e\x21\xc2”
Иронично, но домен rxeva6w.com имеет 0/88 обнаружений (Рисунок 11).
*Рисунок 11: VirusTotal показывает нулевой уровень обнаружения (0/88) для домена rveva6w.com.
Шифрование
Алгоритм шифрования/дешифрования, используемый полезными нагрузками HTTP, представляет собой слегка модифицированный алгоритм DES с ключом “j_k*a-vb” (Рисунок 12).
*Рисунок 12: Ключ шифрования передается функции расшифровки DES.
Дополнительные Функции
Помимо коммуникации по HTTP и скрытия подключений, этот руткит также способен загружать новые модули, скачанные с различных URL-адресов.
Очевидно, этот руткит обладает дополнительными функциями, которые мы не рассматриваем в этом блоге, поэтому мы можем опубликовать продолжение в следующем посте или дать дополнительное обновление в будущем, поскольку мы продолжаем наш анализ.
Кейс-стади №2: Руткит Mingloa (CopperStealer)
Вредоносное ПО Mingloa было впервые обнаружено и названо ESET в 2019 году.
Позже о нем написала компания Proofpoint в этом блогпосте, где оно также получило название CopperStealer.
Считается, что у Mingloa китайское происхождение, что отражено в его названии. Это связано с короткой процедурой в компоненте пользователя, которая проверяет, не установлен ли язык упрощенного китайского (Рисунок 13), иначе происходит выход из программы.
*Рисунок 13: Проверка локали на упрощенный китайский язык.
В оригинальном блогпосте Proofpoint говорится следующее: “Анализируемый образец также может загрузить и активировать драйвер ядра. На данный момент цель этого драйвера неизвестна.“ Конечно, это утверждение побудило нас к дальнейшему исследованию.
Как отмечено в исследовании Proofpoint, вредоносное ПО содержит функции для поиска и кражи сохраненных паролей браузера. Помимо сохраненных паролей браузера, вредоносное ПО использует сохраненные куки-файлы для получения токена доступа пользователя от Facebook.
Это один из многих случаев, когда общие методы защиты учетных данных и токенов безопасности, такие как включенные в CyberArk Endpoint Privilege Manager, могут значительно ограничить воздействие программ-воров учетных данных, таких как CopperStealer. Если использовать эти методы, CopperStealer не сможет извлечь данные с зараженного компьютера (для получения дополнительной информации о методах скрапинга паролей из браузеров см. предыдущий блог CyberArk Labs).
Детали образца
MD5 6f38ca637f7978cefe7bf4dfcfeb9ad6
SHA-1 eb301689bb5154b90c0724cba47a3c8574120b42
SHA-256 d4d3127047979a1b9610bc18fd6a4d2f8ac0389b893bcb36506759ce2f20e7e4
Imphash 9192d1abce0f933180e0e907444e8bec
SSDEEP 384:ySAZEVur6CDbw+ynZDvZZvHnQZvZyEPJvHwr:yzZ0utw+yJOQr
Размер файла 21,27 КБ (21784 байта)
Обзор образца
Этот вредоносный модуль ядра был скомпилирован как для архитектур x86, так и x64.
*Рисунок 14: Анализ сценария атаки вредоносного ПО.
Драйвер подписан сертификатом, выданным на 大连纵梦网络科技有限公司 (Рисунок 15), что переводится как "Dalian Longmeng Network Technology Co. Ltd" или "Dalian Morningstar Network Technology". Возможно, этот сертификат был украден с зараженного компьютера или утек через сотрудника.
*Рисунок 15: Сертификат, выданный на "Dalian Longmeng Network Technology", использованный для подписи драйвера.
Установка Из Режима Пользователя
Давайте сначала рассмотрим рутину заражения вредоносным ПО в пользовательском режиме, предназначенную для развертывания драйвера (Рисунок 16).
*Рисунок 16: Дизассемблирование потока выполнения компонента в пользовательском режиме для установки драйвера.
Изучая этот фрагмент, мы видим, что функция InstallDriver получает один аргумент и сначала вызывается с значением аргумента 0. Во второй раз она вызывается со значением аргумента 1.
Если присмотреться к InstallDriver, видно, что она сначала пытается создать семафор (Рисунки 17 и 18), затем проверяет версию Windows. Если любой из этих вызовов не удастся, она завершится, не сделав ничего.
*Рисунок 17: Дизассемблирование начала функции InstallDriver в бинарном файле, где вызывается CreateSemaphoreWrapper.
Если предыдущие проверки прошли успешно, то вредоносное ПО продолжит работу, останавливая и удаляя любые службы с тем же именем и, в конце концов, сравнивая аргумент shouldInstallDriver со значением 0.
*Рисунок 18: Дизассемблирование функции CreateSemaphoreWrapper.
Если значение shouldInstallDriver равно 0, функция завершится без выполнения дополнительных инструкций. В противном случае она продолжит процесс установки соответствующего драйвера (Рисунок 19), встроенного в бинарный файл, в соответствии с архитектурой системы.
*Рисунок 19: Дизассемблирование функции InstallDriver, описывающее процесс установки драйвера в системе.
Эта часть кода также содержит логическую ошибку, которая не позволяет этому драйверу когда-либо быть загруженным.
Первый вызов InstallDriver, который предполагается использовать только для удаления существующего драйвера, также создаст семафор.
Второй вызов, который должен также установить драйвер, завершится преждевременно до того, как драйвер будет установлен, поскольку семафор уже существует.
Эта логическая ошибка является несколько загадочной, так как вредоносное ПО обычно тестируется на наличие таких ошибок. В данном случае оно либо было развернуто спешно без тестирования, либо еще не предназначалось для развертывания на зараженных машинах.
DriverEntry
Компонент ядра этого вредоносного ПО является устаревшим фильтровым драйвером файловой системы, который, в отличие от более современного мини-фильтра, может изменять поведение системы без использования функций обратного вызова фильтрации, таких как процедуры обратного вызова перед операцией или после операции.
Устаревшие фильтровые драйверы файловой системы могут напрямую изменять поведение файловой системы и вызываются для каждой операции ввода-вывода, такой как CREATE, READ и WRITE.
Изучая DriverEntry (Рисунок 20), мы видим, что две основные функции назначены для IRP_MJ_READ и IRP_MJ_SET_INFORMATION. Кроме того, регистрируются две функции обратного вызова — одна с использованием CmRegisterCallback, а другая — с использованием IoRegisterFsRegistrationChange.
*Рисунок 20: Дизассемблирование DriverEntry драйвера руткита Mingloa.
Когда вызывается IoRegisterFsRegistrationChange, в неё передаётся указатель на функцию DriverNotificationRoutine, цель которой заключается в подключении или отключении фильтрового драйвера в зависимости от того, активна ли файловая система или нет (Рисунок 21).
*Рисунок 21: Дизассемблирование функции DriverNotificationRoutine.
Авторы вредоносного ПО создали драйвер со следующими функциями, основанными на фильтровом драйвере:
- Самозащита: защита от удаления
- Предотвращение удаления ключа реестра (Служба Windows)
- Предотвращение чтения для списка запрещенных файлов (за исключением списка разрешенных процессов)
Прикрепляя драйвер как фильтровый драйвер к файловой системе и реализуя IRP_MJ_SET_INFORMATION (Рисунок 22), авторы могут проверять имя файла, которое предполагается удалить, в списке запрещенных.
*Рисунок 22: Дизассемблирование функции IrpMjSetInformationHandler.
Если имя файла находится в списке запрещенных, обработчик вернет STATUS_ACCESS_DENIED и прекратит обработку IRP. В противном случае он передаст его дальше в лежащий в основе драйвер в стеке устройств (Рисунок 23).
*Рисунок 23: Дизассемблирование функции IrpMjGenericHandler.
Предотвращение Удаления Ключей Реестра
Функция предотвращения удаления ключей реестра и связанных с ними значений защищает реестр, связанный со службой ядерного драйвера Windows.
Эта функция работает за счет регистрации процедуры обратного вызова реестра (RegistryCallback), которая активируется при каждом изменении в реестре и сравнивает путь в реестре с путем службы.
Предотвращение Чтения Запрещенных Файлов (За Исключением Разрешенных Процессов)
Эта функция использует тот же механизм фильтрового драйвера файловой системы, который описан в функции предотвращения самоудаления для IRP_MJ_READ (Рисунок 24).
*Рисунок 24: Дизассемблирование функции IrpMjReadHandler.
Она сначала проверяет имя файла, к которому осуществляется доступ, затем проверяет, содержит ли имя или заканчивается одной из следующих запрещенных строк:
- \\cookies.db\x00
- \\cookies.sqlite\x00
- \\Login Data\x00
- \\Cookies\x00
- \\WebCacheV01\x00
- \\explorer.exe\x00
- \\firefox.exe\x00
- \\Chrome.exe\x00
- \\opera.exe\x00
- \\Yandex.exe\x00
- \\baidu.exe\x00
- \\MicrosoftEdge.exe\x00
- \\MicrosoftEdgeCP.exe\x00
- \\rundll32.exe\x00
*Рисунок 25: Пример попытки вывода содержимого файла cookies.db, когда руткит загружен.
Обфускация Строк
Во многих случаях руткит скрывает важные строки, такие как список запрещенных имен файлов или список разрешенных имен процессов, используя следующую обфускацию. Он инициализирует строку с REGISTRY\MACHINE\SOFTWARE и использует различные битовые арифметические манипуляции (Рисунок 26) для раскрытия множества строк, таких как:
- \\explorer.exe\x00
- \\firefox.exe\x00
- \\Chrome.exe\x00
- \\opera.exe\x00
- \\Yandex.exe\x00
- \\baidu.exe\x00
- \\MicrosoftEdge.exe\x00
- \\MicrosoftEdgeCP.exe\x00
- \\rundll32.exe\x00
*Рисунок 26: Вид дизассемблирования техники деобфускации строк.
Хотя нам хотелось бы создать скрипт для раскрытия этих обфусцированных строк, к сожалению, авторы затруднили это, используя случайные битовые операции и значения для каждой строки.
Поиск Руткитов
В отличие от вредоносного ПО пользовательского режима, которое в основном импортирует из библиотек, таких как kernel32.dll и ntdll.dll, руткиты ядра почти исключительно импортируют свои API-функции из ntoskrnl.exe, что является самим ядром. Этот факт полезен при поиске руткитов в VirusTotal (VT), так как он упрощает поиск драйверов с вредоносными намерениями.
Например, мы можем использовать следующий запрос (Фрагмент 11):
Код:
not tag:signed and not tag:trusted and tag:peexe and imports:ntoskrnl.exe and positives:13+
Запрос будет искать файлы в формате PE, которые не подписаны или не являются доверенными, и импортируют их из ntoskrnl.exe.
Другой вариант - использовать правило Yara при поиске более конкретного набора файлов.
Мы также можем использовать уникальное использование API с дополнительным бинарным шаблоном или строками для поиска новых образцов нашего вредоносного драйвера или руткита.
Так же, как и при анализе образца и поиске похожих файлов (старых или новых), мы можем использовать некоторые свойства кода, такие как тег, используемый в ExAllocatePoolWithTag и символы .pdb, для поиска файлов, связанных с нашим исходным бинарным файлом.
Пример такого правила будет следующим (Фрагмент 12):
Код:
import "pe"
rule CopperStealerDriverx8664
{
strings:
$a0 = { 5f 4c 45 5f }
$a1 = { 5f 45 4c 5f }
$a2 = "_EL_" ascii wide
$a3 = "_LE_" ascii wide
$b = /f:\\sys\\objfre[0-9a-zA-Z_\\]*\\FsFilter(32|64)?.pdb/
condition:
uint16(0) == 0x5A4D
and uint32(uint32(0x3C)) == 0x00004550
and (
pe.machine == pe.MACHINE_AMD64
or pe.machine == pe.MACHINE_I386
)
and $b
and pe.imports("ntoskrnl.exe", "ExAllocatePoolWithTag")
and any of ($a*)
}
Заключение: "Руткиты не ушли в прошлое"
Как мы видели на примерах в этом блоге, руткиты все еще активны и нацелены на современные версии Windows, включая Windows 10 и 11 в архитектурах x86 и x64.
Мы увидели, что руткиты эволюционировали от методов Hooking и DKOM, о которых мы рассказывали в прошлом блоге, к другим методам, таким как драйверы фильтра файловой системы и подписанные драйверы с помощью украденных сертификатов, чтобы избежать срабатывания PatchGuard и "обойти" средства защиты DSE, а также решения EDR (endpoint detection and remediation).
Такие продукты, как CyberArk Endpoint Privilege Manager, могут предотвратить подобные угрозы, используя контроль наименьших привилегий или просто удалив учетную запись администратора из системы и тем самым предотвратив установку новых драйверов, поскольку ни один непривилегированный пользователь в системе не имеет прав на установку драйвера.
Resources
https://codemachine.com/articles/kernel_structures.htmlhttps://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/registering-fast-i-o-dispatch-routines
https://github.com/apriorit/file-system-filter
ORIGINAL ARTICLE IN ENGLISH
Translated from english to russian by S3VE7N