Добиваем мониторинг. Как работают продвинутые атаки на Sysmon

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Получив доступ к взломанной системе, атакующий первым делом реализует техники уклонения от обнаружения, которые позволяют ослабить мониторинг безопасности инфраструктуры и, как следствие, действовать максимально незаметно.

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

Эта статья логически делится на две части — по источникам информации, на основе которых Sysmon формирует свои события. В первой мы посмотрим, как можно нарушить взаимодействие между драйвером SysmonDrv и службой Sysmon64, во второй — поговорим о воздействии на подсистему ETW. Итак, поехали!

ВОЗДЕЙСТВУЕМ НА УСТРОЙСТВО SYSMONDRV​

В моем предыдущем материале я рассказывал о том, как ослепить расширенный мониторинг, манипулируя с дескриптором безопасности объекта коммуникационного устройства \\Device\SysmonDrv и провоцируя Sysmon на повторное открытие хендла этого устройства (вызов DuplicateHandle() с опцией DUPLICATE_CLOSE_SOURCE).

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

Фейковый DosDevice​

Суть этой техники заключается в том, чтобы создать символическую ссылку (симлинк) на коммуникационное устройство \\Device\SysmonDrv раньше, чем это сделает драйвер Sysmon. Успешная эксплуатация позволит атакующему подменить объект коммуникационного устройства и тем самым ослепить Sysmon. Процесс режима пользователя Sysmon64.exe не сможет получать никакую информацию и, как следствие, не будет формировать события.

Для понимания работы этой техники давай вспомним, как работает инструмент Sysmon. В его состав входят два компонента:
  1. Драйвер, который с помощью колбэков и мини‑фильтров отслеживает события, происходящие в системе: создание и завершение процессов, создание файлов, доступ к памяти процесса, создание удаленного потока, загрузку библиотек и другие.
  2. Служба режима пользователя, которая получает информацию от драйвера SysmonDrv и формирует события безопасности.
Драйвер SysmonDrv в процессе своей инициализации создает коммуникационное устройство \\Device\SysmonDrv, чтобы служба Sysmon64 могла получать от него информацию. Однако здесь существует небольшая особенность: пользовательские приложения не могут напрямую взаимодействовать с девайсами. Для этой цели в системе создается символическая ссылка, через которую и происходит все «общение». Вот как это выглядит в WinObj.

winobj-sysmondrv-symlink.png


А что будет, если атакующий попытается подменить эту ссылку? Давай посмотрим.

В системе существует специальный вызов DefineDosDevice, который позволяет создавать (и переопределять) символические ссылки на устройства:
Код: Скопировать в буфер обмена
Код:
BOOL DefineDosDeviceW(
  [in]            DWORD   dwFlags,
  [in]            LPCWSTR lpDeviceName,
  [in, optional]  LPCWSTR lpTargetPath
);
  • В параметре dwFlags передаются флаги, контролирующие работу функции. Укажем DDD_NO_BROADCAST_SYSTEM, что будет означать «не сообщать никому об изменении».
  • Во втором параметре (lpDeviceName) указываем имя симлинка — SysmonDrv, а в третьем (lpTargetPath) — фейковый путь к устройству (\??\Device\fakedrv).
Благодаря функции мы можем подменить симлинк SysmonDrv.

winobj-fake-sysmondrv-symlink.png


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

Для регистрации служб есть специальная функция CreateService:
Код: Скопировать в буфер обмена
Код:
SC_HANDLE CreateServiceA(
  [in]            SC_HANDLE hSCManager,
  [in]            LPCSTR    lpServiceName,
  [in, optional]  LPCSTR    lpDisplayName,
  [in]            DWORD     dwDesiredAccess,
  [in]            DWORD     dwServiceType,
  [in]            DWORD     dwStartType,
  [in]            DWORD     dwErrorControl,
  [in, optional]  LPCSTR    lpBinaryPathName,
  [in, optional]  LPCSTR    lpLoadOrderGroup,
  [out, optional] LPDWORD   lpdwTagId,
  [in, optional]  LPCSTR    lpDependencies,
  [in, optional]  LPCSTR    lpServiceStartName,
  [in, optional]  LPCSTR    lpPassword
);
  • Первым параметром (hSCManager) передается хендл базы данных диспетчера управления службами. Его можно получить с помощью функции OpenSCManager.
  • Второй (lpServiceName) и третий (lpDisplayName) параметры принимают имя регистрируемой службы. Установим их равными FakeDosDevice.
  • Так как вызов CreateService возвращает хендл на созданную службу, с которым можно работать дальше, а сама служба — это объект ядра, в четвертом параметре (dwDesiredAccess) передается запрашиваемый доступ.
  • Далее мы указываем тип службы — SERVICE_WIN32_OWN_PROCESS (параметр dwServiceType).
  • С помощью параметра dwStartType указываем тип автозапуска. В нашем случае он равен SERVICE_AUTO_START.
  • В параметре dwErrorControl передается уровень критичности и действие в случае ошибки (SERVICE_ERROR_NORMAL).
  • В конце (lpBinaryPathName) указываем путь к исполняемому файлу FakeDosDevice.exe, получаемый динамически с помощью функции QueryFullProcessImageName.
  • Оставшиеся параметры установим в NULL.
Итак, службу FakeDosDevice мы зарегистрировали. Теперь ее нужно добавить в зависимости Sysmon64. Если в системе стоит Sysmon версии 15 и выше, то штатными средствами это сделать не получится, поскольку он работает как Protected Process.

sc-config-access-deny-for-set-sysmon64-depend.png


В этом случае мы можем изменить конфигурацию Sysmon64 напрямую в реестре — добавить ключик DependOnService. Для работы с реестром нам понадобятся две функции: RegCreateKey и RegSetValueEx.

Функция RegCreateKeyA получает доступ к заданной ветке реестра:
Код: Скопировать в буфер обмена
Код:
LSTATUS RegCreateKeyA(
  [in]           HKEY   hKey,
  [in, optional] LPCSTR lpSubKey,
  [out]          PHKEY  phkResult
);
В параметре hKey указываем ветку HKEY_LOCAL_MACHINE. В lpSubKey — путь к параметрам службы Sysmon64:
Код: Скопировать в буфер обмена
SYSTEM\CurrentControlSet\Services\Sysmon64\
В phkResult получаем результат в виде хендла, указывающего на ветку реестра.

Чтобы создать ключ DependOnService, воспользуемся функцией RegSetValueEx:
Код: Скопировать в буфер обмена
Код:
LSTATUS RegSetValueExA(
  [in]           HKEY   hKey,
  [in, optional] LPCSTR lpValueName,
                 DWORD  Reserved,
  [in]           DWORD  dwType,
  [in]     const BYTE  *lpData,
  [in]           DWORD   cbData
);
  • С помощью параметра hKey передаем хендл на ранее открытую ветку реестра.
  • Во втором параметре (lpValueName) указываем имя нового ключа — DependOnService.
  • В dwType — его тип (REG_MULTI_SZ).
  • В параметре lpData передаем указатель на буфер данных, которые будут использованы как значение ключа реестра.
  • Последним параметром (cbData) передаем размер передаваемого буфера.
Если запустить результат нашего творчества с расширенным админским токеном, то в системе зарегистрируется служба FakeDosDevice, которая будет стартовать перед Sysmon64 и делать фейковую ссылку SysmonDrv на \\Device\fakedrv, что не позволит Sysmon взаимодействовать с собственным драйвером.

В журнале Microsoft-Windows-Sysmon\Operational мы увидим событие аналогичное тому, что было при манипуляциях с дескриптором безопасности.

winevt-sysmon-operational-error-device-descriptor.png



Подмена хендла SysmonDrv​

В прошлом материале мы изменяли дескриптор безопасности устройства \\Device\SysmonDrv и заставляли Sysmon повторно получать хендл, указывающий на это устройство (благо такая возможность есть в коде самого Sysmon).

Сегодня давай посмотрим на такую же технику, но на сей раз попробуем собственноручно подменить хендл объекта \\Device\SysmonDrv. Эта техника носит название Handle Hijacking и подробно описана в статье на «Хакере». Кратко напомню алгоритм действий:
  1. Приостанавливаем выполнение процесса с помощью вызова NtSuspendProcess().
  2. С помощью функции NtQueryInformationProcess() получаем таблицу хендлов процесса Sysmon64.exe.
  3. Закрываем хендл на устройство с помощью вызова DuplicateHandle.
  4. С помощью CreateFile() получаем описатель для устройства \\Device\Tcp (устройство может быть любым, отличным от SysmonDrv).
  5. Внедряем новый описатель в процесс Sysmon, повторно вызывая DuplicateHandle(), и возобновляем его работу NtResumeProcess().
Давай начнем с приостановки выполнения процесса Sysmon64.exe.

proch-suspend-sysmon-process.png


Это можно сделать с помощью вызова NtSuspendProcess(), который на вход принимает единственный аргумент — хендл, указывающий на процесс Sysmon64.exe с маской PROCESS_SUSPEND_RESUME.
Код: Скопировать в буфер обмена
Код:
NTSTATUS NTAPI NtSuspendProcess(
  IN HANDLE ProcessHandle
);
Посмотрим в таблицу хендлов.

proch-handle-table-of-sysmon-process.png


Эта операция выполняется с помощью вызова NtQueryInformationProcess, который мы подробно рассматривали в предыдущем материале.

Закрываем хендл устройства \\Device\SysmonDrv (0x32c) и повторно смотрим в таблицу хендлов.

proch-close-sysmondrv-device-handle.png


Убеждаемся, что у процесса Sysmon64.exe больше нет дескриптора для устройства \\Device\SysmonDrv.

Хендл закрывается с помощью вызова DuplicateHandle:
Код: Скопировать в буфер обмена
Код:
BOOL DuplicateHandle(
  [in]   HANDLE   hSourceProcessHandle,
  [in]   HANDLE   hSourceHandle,
  [in]   HANDLE   hTargetProcessHandle,
  [out]  LPHANDLE lpTargetHandle,
  [in]   DWORD    dwDesiredAccess,
  [in]   BOOL     bInheritHandle,
  [in]   DWORD    dwOptions
);
  • Параметр hSourceProcessHandle принимает хендл процесса, из которого происходит копирование хендла. Доступ к процессу должен быть получен по маске PROCESS_DUP_HANDLE.
  • В параметре hSourceHandle передается хендл устройства \\Device\SysmonDrv, который был получен на предыдущем шаге (с помощью вызова NtQueryInformationProcess()).
  • Третьим (hTargetProcessHandle) передается хендл текущего процесса, получаемый с помощью GetCurrentProcess().
  • В четвертом параметре (lpTargetHandle) передаем указатель на переменную типа HANDLE, в которую будет помещен скопированный хендл.
  • И последним параметром (dwOptions) передаем опцию копирования DUPLICATE_CLOSE_SOURCE, которая указывает на необходимость закрыть исходный хендл после копирования («закрыть исходный хендл в процессе Sysmon»).
Идем дальше и получаем хендл устройства \\Device\Tcp.

proch-open-tcp-device-handle.png


Эта операция выполняется с помощью вызова CreateFile, который мы подробно рассматривали в предыдущем материале.

Делаем инъекцию этого хендла в процесс Sysmon64.

proch-inject-tcp-device-handle-to-sysmon-process.png



И возобновляем выполнение процесса Sysmon64.exe.

Это делается с помощью вызова NtResumeProcess(), который, как и NtSuspendProcess(), принимает единственный аргумент — хендл процесса с маской PROCESS_SUSPEND_RESUME:
Код: Скопировать в буфер обмена
Код:
NTSTATUS NTAPI NtResumeProcess(
  IN HANDLE  ProcessHandle
);
Давай посмотрим в журнал Microsoft-Windows-Sysmon\Operational.

winevt-sysmon-operational-error-invalid-parameter.png


Видим множество однотипных событий EventID 255 и получаем еще один детект:
Код: Скопировать в буфер обмена
Provider.Name='Microsoft-Windows-Sysmon' and EventID='255' and EventData.ID= 'DriverCommunication' and EventData.Description='Failed to retrieve events - Last error: Параметр задан неверно.'

МАНИПУЛЯЦИИ С ETW​

В одной из предыдущих статей мы меняли дескриптор безопасности на сессию сбора событий. Это позволяло полностью ослепить Sysmon, однако в оснастке «Просмотр событий» появлялось явное свидетельство того, что происходит что‑то неладное.

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

Изменяем параметры отбора событий провайдера​

Давай теперь посмотрим, как можно фильтровать (отбирать) события, формируемые провайдерами, на уровне сессий ETW. Эта техника позволит ослепить Sysmon без каких‑либо свидетельств в журнале Microsoft-Windows-Sysmon\Operational и дополнительных перезагрузок системы.

За поведение провайдера «внутри сессии» отвечает структура _TRACE_ENABLE_INFO.

windbg-trace-enable-info-stucture.png


Эта структура содержит два интересных нам параметра, с помощью которых можно «отфильтровать события»: MatchAnyKeyword и MatchAllKeyword. Давай остановимся на них подробнее.

Что же такое кейворды? Каждый провайдер содержит заранее предопределенный перечень событий. Сами по себе события могут быть объединены в группы. А каждой группе можно навесить теги, это и есть кейворды. Этот механизм позволяет делать выборку событий по категориям внутри сессии.

Как говорится в официальной документации, параметр MatchAnyKeyword представляет собой битовую маску, с помощью которой можно определить, какие события будут передаваться в сессию ETW. Причем, чтобы событие провайдера было передано в сессию трассировки, достаточно, чтобы любой из кейвордов был установлен в MatchAnyKeyword. Давай посмотрим это на примере EventID 8 провайдера Microsoft-Windows-Kernel-EventTracing.

wep-exp-show-events-kerneltracing-provider.png


На это событие навешано два кейворда — ETW_KEYWORD_PROVIDER и ETW_KEYWORD_REGISTRATION. Таким образом, чтобы оно попало в сессию, в параметре MatchAnyKeyword должны быть установлены в единицу 6-й или 10-й биты. Могут быть установлены как оба бита одновременно — 0x220 (544), так и любой из них в отдельности — 0x200 (512) или 0x20 (32).

Параметр MatchAllKeyword отличается от MatchAnyKeyword тем, что для того, чтобы событие было передано в сессию, должны быть установлены сразу все биты в маске. Для EventID 8 (провайдера Microsoft-Windows-Kernel-EventTracing) параметр MatchAllKeyword должен иметь значение 0x220 (544).

На практике часто значение MatchAllKeyword равно нулю, а параметр MatchAnyKeyword — 0xFFFFFFFF'FFFFFFFF. Это позволяет потреблять все события от провайдера.

Изменить эти параметры из пользовательского пространства можно с помощью вызова EnableTraceEx2:
Код: Скопировать в буфер обмена
Код:
ULONG WMIAPI EnableTraceEx2(
  [in]           TRACEHANDLE              TraceHandle,
  [in]           LPCGUID                  ProviderId,
  [in]           ULONG                    ControlCode,
  [in]           UCHAR                    Level,
  [in]           ULONGLONG                MatchAnyKeyword,
  [in]           ULONGLONG                MatchAllKeyword,
  [in]           ULONG                    Timeout,
  [in, optional] PENABLE_TRACE_PARAMETERS EnableParameters
);
  • В параметр TraceHandle передается хендл сессии трассировки.
  • Второй параметр (ProviderId) принимает GUID провайдера.
  • С помощью ControlCode указываем на необходимость изменить параметры работы провайдера (EVENT_CONTROL_CODE_ENABLE_PROVIDER).
  • В параметрах MatchAnyKeyword и MatchAllKeyword указываем требуемый уровень выборки событий провайдера.
Итак, давай посмотрим на сессию EventLog-Microsoft-Windows-Sysmon-Operational.

proch-show-sysmon-session.png


Эта сессия потребляет события от одного провайдера — Microsoft-Windows-Sysmon. Причем потребляет все события (параметр MatchAnyKeyword). Давай попробуем изменить этот параметр и посмотрим, что будет.

proch-set-matchanykeyword-for-sysmon-provider.png


Команда set manykeyword <SessionHandle> <ProviderGuid> <Bitmask> вызывает функцию EnableTraceEx2() и устанавливает параметр MatchAnyKeyword.

После проделанных манипуляций проверяем журнал Microsoft-Windows-Sysmon\Operational и убеждаемся, что события больше не формируются. При этом сам Sysmon даже никак не оповестил нас об этом.

Давай попробуем изменить параметр MatchAllKeyword.

proch-set-matchallkeyword-for-sysmon-provider.png


Повторно смотрим в журнал Sysmon и убеждаемся в отсутствии событий.

На этом, пожалуй, закончим издеваться над Sysmon и пойдем думать о том, как все это детектировать. Спасибо всем, у кого хватило терпения дочитать до конца!

Автор @driverenok, twitter.com/driverenok
Источник xakep.ru
 
Сверху Снизу