Продолжаем цикл материалов о вредоносных техниках, применяемых злоумышленниками. Встречайте еще одну полезную статью от экспертного центра безопасности Positive Technologies. В прошлый раз мы говорили о том, как киберпреступники повышают привилегии, чтобы получить полный контроль над системой, и как  их работу можно обнаружить. Сегодня рассмотрим тактики, которые используют хакеры для злонамеренных действий с ядром ОС. Расскажем о новом плагине exploitmon, разработанном PT Expert Security Center для системы динамического анализа вредоносных файлов DRAKVUF, и разберем, как в PT Sandbox с его помощью обнаруживать попытки эксплуатации уязвимостей в ядре Windows.

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

Какие уязвимости атакующие обычно эксплуатируют в ядре?

В любой серьезной атаке злоумышленники для дальнейшего продвижения в корпоративной среде используют уязвимости в программном коде для повышения привилегий. Например, в декабре 2020 года одна из APT-группировок проэксплуатировала  уязвимость нулевого дня в графической системе Windows. Эта ошибка заключалась в десинхронизации ядерной структуры при создании графических окон для повышения привилегий до системных. Говоря иначе, примитив на запись получается с помощью небезызвестного и очень популярного у злоумышленников объекта tagWND, который в комбинации с примитивом на чтение перезаписывает токен безопасности процесса в ядерной структуре EPROCESS. Ниже перечислим основные техники эксплуатации уязвимостей в ядре.

HalDispatchTable

HalDispatchTable по праву можно считать самой любимой хакерами структурой, которую используют чуть ли не с появлением ОС Windows XP. Находящаяся в секции ядра с правами на запись структура представляет собой массив указателей на функцию. Ее можно сравнить с Import Address Table пользовательского процесса, однако здесь HalDispatchTable применяется для абстрагирования системы от железа, на котором она работает.

Интересно! Один из указателей на функцию легко может быть вызван из пользовательского процесса. Если посмотреть дизассемблирование функции KeQueryIntervalProfile, то можно заметить, что восьмая инструкция этой функции как раз вызывает HalQuerySystemInformation — в нашем случае смещение 4 байта, так как ОС 32-битная.

Как происходит переход на ядерный код? KeQueryIntervalProfile имеет приставку Ke, означающую, что функция ядерная, а значит, не экспортируется пользовательскими программами. Хорошо, что, есть еще одна одноименная функция с приставкой Nt, которую как раз можно вызывать из пользовательского процесса.

Таким образом, техника эксплуатации уязвимости выглядит следующим образом:

  1. Злоумышленник с помощью какой-либо уязвимости перезаписывает указатель в HalDispatchTable на адрес своего шелл-кода.

  2. Вызывает NtQueryIntervalProfile, а она в свою очередь — ядерную функцию KeQueryIntervalProfile, тем самым передавая управление на шелл-код.

Получается, что шелл-код выполняется в ядерном потоке и имеет полный доступ к OC.

Модификация токена безопасности

Токен — это объект, обеспечивающий разделение прав пользователей в системе. Он находится в EPROCESS — структуре в ядре ОС Windows, которая есть у любого процесса, а токен — всего лишь один из его полей. (Токену безопасности была посвящена наша предыдущая статья.) Итак, зачем атакующему токен?

Если перезаписать токен безопасности непривилегированного процесса пользователя на токен системного, то этот процесс получит системные права. Ниже приведен код кражи токена безопасности от группировки APT28, который заключается в нахождении системного процесса с идентификатором 4 и последующей перезаписи токена существующего процесса на токен системного. Стоит отметить, что этот код полностью проанализирован неким исследователем, восстановлен из оригинального образца и  доступен в открытом исходном коде на github.

Интересно! У перечисленных выше техник есть две общие черты, а именно:

·       выполнение пользовательского кода в ядре;

·       чтение и перезапись полей в динамических структурах ядра.

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

Чтобы показать, как мы детектируем эти и другие эксплойты, перейдем к рассказу о фреймворке DRAKVUF.

Что такое DRAKVUF

DRAKVUF — безагентная песочница динамического анализа файлов с открытым исходным кодом. Она позволяет перехватывать различные события, такие как доступ к памяти (r/w/x) или изменение регистров управления, моделезависимых регистров.

МSR-регистры — это тоже управляющие регистры, однако их отличает от других регистров то, что для записи и чтения используются специальные инструкции — RDMSR и WRMSR.

Преимуществ использования DRAKVUF несколько:

  1. Фреймворк создавался специально для скрытного динамического анализа, то есть запущенный процесс не понимает, что он под пристальным вниманием запускается в виртуальной машине, которая инструментируется гипервизором.

  2. API DRAKVUF не зависит от гостевой операционной системы и может работать с виртуальными машинами Linux и Windows.

  3. Легко расширяется с помощью плагинов — сейчас в открытом репозитории насчитывается 28 плагинов, каждый из которых имеет свои уникальные возможности и выполняет определенные действия.

  4. Использует LibVMI для мониторинга и изменения состояния виртуальной системы.

LibVMI (Virtual Machine Introspection) — это библиотека для мониторинга и модификации виртуальной машины, предоставляющая обширный API на все случаи жизни. Помимо всего прочего, она может перехватывать различные события, в том числе касающиеся модификации памяти. Это очень большой проект: LibVMI поддерживает огромное число гипервизоров, включая Xen, VirtualBox, VMware. Сейчас DRAKVUF работает с гипервизором Xen, под который в наибольшей степени подстроена LibVMI.

Как DRAKVUF и LibVMI перехватывают доступы к памяти

… а также ставят точки останова в коде, и при этом делают все это незаметно для гостевой операционной системы.

Такая возможность есть благодаря дополнительной таблице трансляции страниц, или extended page table (EPT) — аппаратной поддержке виртуализации, добавленной Intel. Она увеличивает производительность непосредственно виртуализации. При трансляции виртуальной памяти гостевой ОС в физическую память основной операционной системы гостевая ОС сначала достает адрес таблицы EPT PML4 из регистра управления 3. Затем виртуальный адрес разделяется на несколько частей, где каждая часть используется как смещение в этих таблицах. Таким образом, как только ОС пройдет три таблицы и дойдет до четвертой EPT, в работу включится наш гипервизор.

Что именно он делает?  Меняет права доступа к физической странице памяти в EPT так, что при доступе к ней происходит исключение Page Fault (EPT violation). Гипервизор перехватывает #PF, обнаруживает и обрабатывает событие, восстанавливая оригинальные права доступа страницы.

Давайте разберем на примере, как работает любой гипервизор с поддержкой EPT. У каждой записи в EPT есть флаги доступа к страницам. Если некая страница доступна на запись, и мы хотим ее перехватить, то следует поменять флаг доступа, к примеру, только на чтение. Соответственно, когда операционная система что-то запишет на нашу страницу, произойдет ошибка, которая и называется Page Fault. Гипервизор, в свою очередь, ее поймает — то есть узнает, что она произошла именно на той странице, которую он мониторит, обработает и восстановит оригинальные права. Таким образом LibVMI перехватывает доступы к памяти. Возьмем простую программу, которая что-то записывает в файл (см. рисунок ниже), запустим ее в DRAKVUF, и среди огромного числа событий, которые она успела перехватить и залогировать, увидим следующее:

Здесь сработал плагин filetracer, логирующий все доступы к файлам, — он поймал доступ к suspicious_file на системном вызове NtCreateFile и NtWriteFile. В данном случае перехваты происходят не на странице, а на определенные адреса: гипервизор по адресу заменяет инструкцию, которую мы хотим перехватить, на INT3, иным словами — Break Point. Кроме того, он обрабатывает ошибку, которую операционная система генерирует при выполнении этого адреса.

Ниже представлен ряд плагинов из репозитория с их функциями.

Exploitmon: что это и зачем он нужен

Exploitmon — это новый плагин для системы DRAKVUF, разработанный PT Expert Security Center c целью обнаружения попыток эксплуатации ядра ОС. Exploitmon отличает от любых других плагинов то, что он обнаруживает только конечный результат, а какую именно уязвимость в ядре использует злоумышленник, для него роли не играет.

Как обнаружить модификацию HalDispatchTable

Первый аргумент в функцию hal_table_overwrite, которая будет вызвана при записи на страницу (DRAKVUF позволяет регистрировать свои функции-обработчики на доступ к памяти), — инстанс DRAKVUF, а второй — данные о перехвате (где произошел, кто сделал, на каком адресе и дополнительная информация).

Далее проверяется, принадлежит ли адрес, куда была произведена запись, структуре HalDispatchTable. В случае если этот адрес находится в структуре, мы получаем красивое сообщение в логе.

Обнаружение модификации токена безопасности

Здесь следующий алгоритм:

  1. Сохраняем уже имеющиеся токены процессов на моменте запуска ВМ.

  2. Сохраняем токен на этапе создания нового процесса.

  3. Сверяем токены при завершении либо процесса, либо анализа ВМ. Есть шанс, что процесс не завершится до окончания анализа — тогда сверку токена можно делать при завершении ВМ.

Ей подается callback — наша функция, которая будет вызвана при каждом найденном процессе. Дело в том, что структуры EPROCESS соединены между собой двусвязным списком, и нумерация процессов в системе превращается в обход этого списка. Функция, которой передается управление при обходе такого списка, показана ниже.

Вторым аргументом является адрес EPROCESS-структуры — в этой функции считывается токен безопасности и уникальный идентификатор процесса, которые сохраняются в список. Важно отметить, что первые три бита (четыре — в случае 64-битной системы) у токена являются счетчиком ссылок на него, поэтому перед сохранением их необходимо удалить.

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

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

Если токены не совпадут, мы получим следующее сообщение:

Как обнаружить ядерный поток в пользовательской памяти

Легитимно ли исполнение ядерного потока в пользовательской памяти? Правильный ответ — нет. Пользовательская память отличается от ядерной специальным флагом в PTE-структуре. Если он выставлен — память ядерная, если нет — пользовательская. Состояние потока можно узнать с помощью специального API. Так, если два этих значения не совпадут, значит, у нас исполнение ядерного потока в пользовательской памяти.

ExGetPreviousMode — это ядерная функция, недоступная в пользовательском пространстве.

Пару слов о виртуальной памяти Windows:

  • процессы требуют память по необходимости;

  • страница памяти 4 КБ;

  • выделение памяти всегда кратно 64 КБ: если процессу потребовалась страница памяти, ОС выделит 64 КБ (только 60 КБ будет недоступно);

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

Виртуальная память процесса описывается специальной структурой под названием «VAD-дерево». Каждый узел в этом дереве представляет собой область виртуальной памяти и ее различные метаданные — права, размер и прочее. Если в отладчике открыть карту памяти процесса, то это как раз и будет дерево VAD, которое находится в ядре, тогда как отладчик получает информацию о памяти с помощью специального API. Указатель на корневой каталог хранится в EPROCESS-структуре процесса, а тип узла может быть либо Mapped VAD node (сюда относят подгруженные библиотеки, файлы шрифтов, языков и в целом память, которую можно разделить между процессами), либо Private VAD node (входит Private Heap, стек, память, которая выделяется под свои собственные нужды с помощью VirtualAlloc, различные распаковщики исполняемых файлов и так далее).

Когда процесс выделяет память и первый раз к ней обращается, например что-то записывает, происходит исключение page fault, которое ОС обрабатывает, выделяет физическую память под виртуальную и выполняет инструкцию заново. Весь процесс представлен ниже.

Выделим ключевые моменты обнаружения:

  1. Перехват MmAccessFault на выходе из функции, где как раз вступает в дело наш гипервизор.

  2. Перехват физической памяти на исполнение.

  3. Сверка последнего режима потока и типа памяти в обработчике перехватов физической страницы.

Если эти значения не совпадают — ядерный поток в пользовательской памяти обнаружен, и тогда появится сообщение ниже.

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

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

  2. Такой подход можно использовать в исследовательской среде, где мы, фильтруя поток, снижаем мощность, а также можем выделить больше ресурсов на обработку входящего файла.

Когда ресурса не хватает, этот функционал можно отключить специальным флагом при создании DRAKVUF.

Подробнее о PT Sandbox

PT Sandbox — песочница для защиты от целевых и массовых атак с применением неизвестного вредоносного ПО и угроз нулевого дня. Возможности продукта позволяют проводить статический анализ кода полученных объектов с помощью IAR-движка с собственными правилами для обнаружения вредоносного ПО. Кроме того, для обеспечения эффективного покрытия используется SDK сторонних вендоров. В PT Sandbox можно проводить динамический анализ: образцы запускаются в виртуальном окружении, для анализа поведения снимается трасса событий в системе (в том числе с помощью системы для динамического анализа вредоносных файлов DRAKVUF). Также можно записывать сетевой трафик, который сканируется сетевыми сигнатурами, анализировать дампы памяти процессов и извлекать созданные файлы из системы. Узнать больше о PT Sandbox можно здесь.

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


Авторы:

Алексей Вишняков

Руководитель отдела обнаружения вредоносного ПО PT ESC

Павел Максютин

Специалист отдела обнаружения вредоносного ПО PT ESC

Комментарии (0)