Sysmon. Выводим мониторинг из строя максимально незаметно

D2

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

SYSMON INTERNALS​

Сразу оговорюсь, что техники, описанные в этой статье, требуют, чтобы у атакующего были административные привилегии. Кто‑то скажет: зачем же нам углубляться во внутрянку Windows, если с правами админа можно остановить службу Sysmon или вообще удалить ее? Ответ прост. Во‑первых, при реализации традиционных техник, таких как остановка службы, завершение процесса или полное удаление Sysmon, штатный аудит Windows сформирует массу событий (начиная с EventID 4689 в журнале Security и заканчивая событием EventID 1 в журнале System от провайдера FilterManager). Во‑вторых, это просто полезное упражнение: мы должны как можно глубже разбираться в подсистеме безопасности любимой ОС, чтобы понимать техники, которыми пользуются хакеры.

Как уже говорилось в статье «Sysmon для безопасника», Sysmon — это инструмент, предназначенный для углубленного мониторинга активности, происходящей в системе, который позволяет существенно расширить штатный аудит ОС Windows.

Вот как его работа выглядит изнутри.

proch-sysmon-service-and-driver.PNG


Первая команда (search <ServicePattern>) ищет зарегистрированные в системе службы и драйверы с помощью Win32-функции EnumServicesStatus(). Видно, что в системе действуют две сущности: служба режима пользователя — Sysmon64 и драйвер SysmonDrv. Обе сущности запускаются автоматически при старте системы (поле StartType). Эту информацию мы получаем с помощью команды show all <ServiceName>, которая последовательно вызывает QueryServiceStatus() (для получения текущего статуса работы — запущена или остановлена) и QueryServiceConfig() (для получения оставшейся информации).

Логично предположить, что события (или часть событий), которые пишет служба Sysmon64 в журнал Microsoft-Windows-Sysmon\Operational, формируются модулем ядра SysmonDrv, а для взаимодействия службы и драйвера должно быть коммуникационное устройство (device). Чтобы проверить это, посмотрим на список хендлов, открытых службой Sysmon64 (процессом Sysmon64.exe).

Получим идентификатор процесса Sysmon64.exe (search name <ProcessPattern>).

proch-sysmon-process-search.PNG


Команда search name <ProcessPattern> работает через вызов CreateToolhelp32Snapshot(): функции Process32First() и Process32Next().

После того как мы получим ID процесса, посмотрим список его хендлов.
proch-sysmon-show-handles.PNG


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

Команда show handles <PID> <Type> реализует вызов NtQueryInformationProcess() с параметром ProcessHandleInformation.
Код: Скопировать в буфер обмена
Код:
__kernel_entry NTSTATUS NtQueryInformationProcess(
  [in]            HANDLE           ProcessHandle,
  [in]            PROCESSINFOCLASS ProcessInformationClass,
  [out]           PVOID            ProcessInformation,
  [in]            ULONG            ProcessInformationLength,
  [out, optional] PULONG           ReturnLength
);
Первый параметр (ProcessHandle) — дескриптор процесса, хендлы которого мы хотим увидеть. Его можно получить при помощи функции OpenProcess() с параметром PROCESS_QUERY_INFORMATION.

ProcessInformationClass — это характер запрашиваемой информации. В нашем случае этот параметр равен ProcessHandleInformation, то есть мы хотим получить информацию о хендлах процесса.

Следующим идет ProcessInformation, указатель на память, в которую будет помещен результат нашего запроса — PPROCESS_HANDLE_SNAPSHOT_INFORMATION, то есть указатель на структуру, содержащую список всех хендлов и их количество.

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

ЗАКРЫТИЕ ХЕНДЛА ЧЕРЕЗ ДУБЛИКАТ​

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

Существует несколько способов это сделать, но мы воспользуемся самым, на мой взгляд, простым — с помощью вызова DuplicateHandle():
Код: Скопировать в буфер обмена
Код:
BOOL DuplicateHandle(
  [in]  HANDLE   hSourceProcessHandle,
  [in]  HANDLE   hSourceHandle,
  [in]  HANDLE   hTargetProcessHandle,
  [out] LPHANDLE lpTargetHandle,
  [in]  DWORD    dwDesiredAccess,
  [in]  BOOL     bInheritHandle,
  [in]  DWORD    dwOptions
);
С помощью этой функции можно копировать хендлы между процессами, причем у нее есть интересная опция, которая позволяет при копировании закрывать исходный хендл. Этим мы и воспользуемся. Наш код будет выглядеть следующим образом:
Код: Скопировать в буфер обмена
Код:
DuplicateHandle(
  hProcess,
  hObject,
  GetCurrentProcess(),
  &hDupObject,
  0,
  FALSE,
  DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE
);
Первый параметр (hProcess) — дескриптор процесса, хендл из которого мы хотим скопировать закрыть. Тут важно не забыть, что передаваемый объект процесса hProcess должен быть открыт с маской доступа PROCESS_DUP_HANDLE.

Следующий параметр (hObject) — сам закрываемый дескриптор.

Третий (GetCurrentProcess()) и четвертый (&hDupObject) параметры указывают на процесс‑приемник и сам хендл‑приемник соответственно.

Последний параметр содержит две константы: первая (DUPLICATE_SAME_ACCESS) значит, что новый хендл должен иметь такую же маску доступа, как и исходный; вторая (DUPLICATE_CLOSE_SOURCE) — что необходимо закрыть исходный хендл после операции дублирования. Отлично, это то, что нам нужно.

Итак, как мы видели ранее, у процесса Sysmon64.exe есть открытый описатель для объекта \Device\SysmonDrv. Попробуем его закрыть.

proch-close-sysmondrv-device-handle-modify.png


Мы закрыли описатель с идентификатором 0x310, однако сразу после этого процесс Sysmon64.exe успешно открыл его повторно (0x4с4) и продолжил работать как ни в чем не бывало. Поэтому перейдем к тяжелой артиллерии, а эта техника нам еще понадобится позже.

ОБЪЕКТЫ ДРАЙВЕРОВ​

Раз уж не получилось манипулировать с хендлом, попробуем поманипулировать с дескриптором безопасности (Security Descriptor), который определяет правила предоставления доступа к устройству \Device\SysmonDrv. Но для начала давай вспомним, что такое объект драйвера (driver) и объект устройства (device), а также как устроена подсистема управления доступом в Windows. Если ты хорошо знаком с такими понятиями, как Driver Object, Device Object, Security Descriptor и Access Token, можешь смело пропускать этот и следующий разделы.

При загрузке драйвера в адресное пространство ядра система создает структуру _DRIVER_OBJECT, то есть создает специальный объект типа драйвер (driver). Для наглядности воспользуемся отладчиком WinDbg и посмотрим на содержимое этой структуры.
windbg-show-sysmondrv-driver-object.PNG


По смещению 0x008 от начала структуры живет элемент DeviceObject — указатель на структуру _DEVICE_OBJECT — это другой объект ядра, который необходим для коммуникаций между процессом режима пользователя и самим драйвером. Иными словами, коммуникационное устройство обеспечивает передачу информации между пользовательским процессом и драйвером. Именно для этих целей драйверы создают объекты device, а пользовательские процессы могут взаимодействовать с ними.
windbg-show-sysmondrv-device-object.PNG


Самый интересный элемент структуры — это SecurityDescriptor. Именно там хранятся сведения о том, кому разрешено взаимодействовать с устройством и по какой маске доступа. Его содержимым мы и будем манипулировать.

КАК УСТРОЕНА БЕЗОПАСНОСТЬ В WINDOWS​

В подсистеме управления доступом Windows есть три участника (роли):
  • субъект доступа;
  • объект доступа;
  • независимый арбитр.
Субъектом доступа принято называть поток (или процесс, в этой статье мы будем считать эти понятия синонимами), который пытается получить доступ к ресурсу (например, файлу или ключу реестра) — объекту доступа. Потоки исполняются в контексте определенного пользователя. Это означает, что процесс может получить ровно столько доступа, сколько предоставлено учетной записи, от имени которой запущен процесс.

За принадлежность потока к контексту конкретного пользователя отвечает структура ядра _Token (Access Token, маркер доступа). Это своеобразный «паспорт субъекта», который система проверяет всякий раз, как только процесс затребует выполнение какой‑либо операции над объектом (чтение ключа реестра, запись в файл, чтение из коммуникационного устройства). Структура _Token содержит следующие сведения:
  • от имени какой учетной записи запущен процесс (идентификатор учетной записи);
  • каким группам безопасности принадлежит учетная запись (идентификаторы групп);
  • какие у учетной записи привилегии.
Получим адрес Token из структуры _EPROCESS (это структура ядра, которая описывает объект процесса).

windbg-search-token-struct-address.PNG


По факту Token «указывает на указатель» _EX_FAST_REF, а не на сам Token. Причем _EX_FAST_REF имеет тип union (в терминологии языка C) и включает в себя три элемента. Напомню, union предполагает, что все элементы типа совместно используют выделенную им память.

Элементы Object и Value — 8 байт, RefCnt — 4 бита. Значение RefCnt используется внутренними механизмами ядра Windows для подсчета количества ссылок. Таким образом, чтобы получить адрес указателя на структуру _Token, нужно занулить последние 4 бита значения в элементе Object.
windbg-calc-token-struct-address.PNG



Посмотреть на содержимое Access Token в удобном виде можно с помощью расширения !token.
windbg-show-access-token-of-sysmon64-exe.PNG


Видим, что Sysmon64.exe запущен от имени учетной записи Local System (S-1-5-18). Более подробно о том, что такое маркер доступа, можно почитать в статьях «Windows Tokens» и «Токены. От появления до продвижения в Active Directory» на «Хабрахабре».

Объектом доступа называется любой элемент Windows, доступ к которому может быть произвольно ограничен. По своей сути Windows имеет объектную модель, то есть все ресурсы являются объектами. Файлы, ключи реестра, процессы, устройства ввода‑вывода — это всё объекты, доступ к которым контролируется средствами ОС. Сведения о том, кому можно получить доступ к объекту, а кому нет, содержатся в Security Descriptor (SD, дескриптор безопасности объекта, структура ядра _SECURITY_DESCRIPTOR). Посмотрим на содержимое дескриптора безопасности для нашего устройства \Device\SysmonDrv. Для этого проделаем следующий путь:
  1. С помощью расширения !drvobj получим адрес структуры _DRIVER_OBJECT для нашего драйвера SysmonDrv.
  2. Оттуда получим значение DeviceObject, указывающее на объект коммуникационного устройства \Device\SysmonDrv (_DEVICE_OBJECT).
  3. А уже из _DEVICE_OBJECT — указатель на _SECURITY_DESCRIPTOR.
windbg-search-sd-of-sysmondrv-device.PNG



Посмотрим на содержимое дескриптора безопасности нашего объекта.
windbg-show-sd-of-sysmondrv-device-object.PNG


В этой структуре хранятся:
  • сведения о хозяине объекта (object owner), в нашем случае это Local System (S-1-5-18);
  • данные об основной группе объекта (object primary group) — Local System;
  • списки DACL и SACL.
Именно список DACL содержит перечень учетных записей (идентификаторов), которым разрешено (или запрещено) получать доступ к объекту. При этом указывается метод доступа (маски, операции доступа).

Подробнее о структуре дескрипторов безопасности можно прочитать в статье на «Кодебай».

У нас есть субъект доступа, изъявивший желание получить доступ к защищаемому ресурсу (субъект имеет свой Access Token, содержащий сведения об учетной записи пользователя, ее членстве в группах и привилегиях), и объект доступа (то есть сам защищаемый ресурс), имеющий Security Descriptor (сведения о том, кому и какой доступ разрешен). Теперь нам нужен независимый арбитр, который сможет на основе этой информации решить, предоставлять ли доступ.

В качестве такого независимого арбитра выступает компонент исполнительной подсистемы — Security Reference Monitor (SRM). Этот компонент отвечает за проверку того, может ли субъект получить доступ к объекту по запрашиваемому методу доступа на основе своих внутренних алгоритмов.

Итак, подытожим:
  • субъект доступа — процесс Sysmon64.exe, запущенный от имени учетной записи Local System (S-1-5-18);
  • Sysmon64.exe читает данные, формируемые драйвером SysmonDrv, с помощью коммуникационного устройства — \Device\SysmonDrv (объект доступа);
  • устройство \Device\SysmonDrv имеет свой Security Descriptor, который атакующий хочет модифицировать таким образом, чтобы субъекту (процессу Sysmon64.exe) было отказано в доступе.

ОСЛЕПЛЯЕМ SYSMON​

Освежив знания о подсистеме управления доступом, приступим к нашей основной задаче — попробуем ослепить Sysmon. В этом разделе мы изменим Security Descriptor таким образом, чтобы процессу Sysmon64.exe было отказано в предоставлении доступа к устройству \Device\SysmonDrv.

Посмотрим на содержимое дескриптора безопасности коммуникационного устройства. Для этого нам потребуется получить хендл на устройство с помощью функции CreateFile() и последующего вызова GetSecurityInfo(). Давай подробнее остановимся на этих функциях. CreateFile() умеет получать описатели для множества объектов, в том числе и для коммуникационных устройств:
Код: Скопировать в буфер обмена
Код:
HANDLE CreateFileW(
  [in]           LPCWSTR               lpFileName,
  [in]           DWORD                 dwDesiredAccess,
  [in]           DWORD                 dwShareMode,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  [in]           DWORD                 dwCreationDisposition,
  [in]           DWORD                 dwFlagsAndAttributes,
  [in, optional] HANDLE                hTemplateFile
);
Давай подробнее разберем каждый из семи параметров.
  1. lpFileName принимает имя объекта, в нашем случае это \\.\SysmonDrv — символическая ссылка на \Device\SysmonDrv.
  2. dwDesiredAccess — перечень флагов, описывающих запрашиваемый доступ (маска доступа). Подсистема безопасности Windows устроена таком образом, что перед тем, как выполнить операцию над объектом, надо указать перечень необходимых доступов. Чтобы посмотреть значения Owner, Group и список DACL, нужно указать READ_CONTROL, а чтобы посмотреть или изменить список SACL — значение ACCESS_SYSTEM_SECURITY (последний флаг требует наличия у вызывающего процесса включенных привилегий SE_SECURITY_NAME, в противном случае получим ошибку ERROR_ACCESS_DENIED).
  3. dwShareMode определяет режим совместного использования. В нашем случае устанавливается 0.
  4. lpSecurityAttributes принимает указатель на дескриптор безопасности, который будет ассоциирован с объектом. Этот параметр имеет значение только в случае создания нового объекта.
  5. dwCreationDisposition определяет действие для существующих или несуществующих файлов или устройств, в нашем случае его значение будет равно OPEN_EXISTING.
  6. dwFlagsAndAttributes определяет перечень флагов и атрибутов, с которыми запрашивается доступ к объекту. Здесь мы укажем FILE_FLAG_BACKUP_SEMANTICS, что сообщит подсистеме безопасности о необходимости получить доступ в обход существующего списка DACL (наличие данной опции требует у вызывающего процесса включенных привилегий SE_BACKUP_NAME и SE_RESTORE_NAME).
  7. hTemplateFile — используется только при создании новых объектов.
Вот как будет выглядеть наш вызов CreateFile():
Код: Скопировать в буфер обмена
Код:
hDevice = CreateFile(
  lpDeviceName,
  READ_CONTROL | ACCESS_SYSTEM_SECURITY,
  0,
  NULL,
  OPEN_EXISTING,
  FILE_FLAG_BACKUP_SEMANTICS,
  NULL
);
После получения описателя для устройства \\.\SysmonDrv воспользуемся функцией GetSecurityInfo(), которая позволит вытащить информацию из дескриптора безопасности:
Код: Скопировать в буфер обмена
Код:
DWORD GetSecurityInfo(
  [in]            HANDLE               handle,
  [in]            SE_OBJECT_TYPE       ObjectType,
  [in]            SECURITY_INFORMATION SecurityInfo,
  [out, optional] PSID                 *ppsidOwner,
  [out, optional] PSID                 *ppsidGroup,
  [out, optional] PACL                 *ppDacl,
  [out, optional] PACL                 *ppSacl,
  [out, optional] PSECURITY_DESCRIPTOR *ppSecurityDescriptor
);
Первый параметр (handle) принимает описатель нашего объекта, который мы получили с помощью CreateFile().

Далее нужно указать тип объекта (ObjectType), дескриптор безопасности которого мы хотим посмотреть. Укажем SE_UNKNOWN_OBJECT_TYPE.

В следующем аргументе (SecurityInfo) мы указываем, какие именно поля дескриптора безопасности мы хотим получить. В нашем случае это вот такое объединение:
Код: Скопировать в буфер обмена
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION
Оставшиеся параметры указывают на структуры данных, в которые будет помещена требуемая нам информация.

В итоге вызов GetSecurityInfo() имеет следующий вид:
Код: Скопировать в буфер обмена
Код:
GetSecurityInfo(
  hObject,
  SE_UNKNOWN_OBJECT_TYPE,
  OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION,
  &pOwner,
  &pGroup,
  &pDacl,
  &pSacl,
  &pSecurityDescriptor
);
Посмотрим на дескриптор безопасности для \Device\SysmonDrv. С помощью команд show owner <Device> и show group <Device> можно узнать хозяина объекта и группу, а командой show sd <Device> получить полное содержание SD в формате SDDL.

proch-show-sysmondrv-device-sd.PNG


Security Descriptor выглядит следующим образом:
Код: Скопировать в буфер обмена
O:SYG:SYD:(A;;0x1201bf;;;WD)(A;;FA;;;SY)(A;;FA;;;BA)(A;;0x1200a9;;;RC)S:AI
О том, как правильно читать такой формат, подробно написано в статье «Как смотреть SDDL и не ломать глаза о точки с запятыми» на «Хабрахабре». Мы остановимся лишь на одной его части — списке избирательного контроля доступа (DACL). Он имеет такой вид:
Код: Скопировать в буфер обмена
D:(A;;0x1201bf;;;WD)(A;;FA;;;SY)(A;;FA;;;BA)(A;;0x1200a9;;;RC)
Cписок избирательного контроля доступа (Discretionary Access Control List, DACL) состоит из отдельных записей (Access Control Entries, ACE). Каждая ACE оформляется в отдельных круглых скобках. Именно DACL хранит информацию о том, какому субъекту и какой именно доступ разрешен, а какой запрещен. Его мы разберем подробнее.

В глаза сразу бросается запись (A;;FA;;;SY), которая говорит о том, что учетной записи Local System (SY) возможно предоставлять (A) полный доступ (FA). Попробуем явно запретить любой доступ для этой учетной записи. Для этого символ A заменим D — (D;;FA;;;SY). Переписать Security Descriptor можно командой set sddldacl <DACL>. Она реализована через вызов функции SetSecurityInfo(), которая принимает параметры, аналогичные GetSecurityInfo(), поэтому не будем разбирать его подробно.
proch-rewrite-sysmondrv-device-sd.PNG


Проверим, что из этого выйдет, если попросить систему закрыть хендл на устройство с помощью уже известной нам техники — закрытие хендла через дубликат.
proch-check-result-1-after-rewrite-sysmondrv-device-sd.PNG


Видим, что ничего не изменилось и у процесса Sysmon64.exe осталось открытое устройство \Device\SysmonDrv, но уже с другим описателем. Вероятно, мы чего‑то не учли! Вернемся к дескриптору безопасности.

Обратим внимание на первую запись в списке DACL — (A;;0x1201bf;;;WD), которая означает, что для учетной записи Everyone (WD) предоставлен (A) какой‑то довольно широкий доступ — 0x1201bf (присутствуют как минимум FILE_READ_DATA и FILE_WRITE_DATA). Уберем ее из списка DACL и повторим операцию по закрытию хендла на устройство.
proch-rewrite-and-update-2-sysmondrv-device-sd.PNG


«Ура! Сработало!» — скажет хакер и продолжит свои грязные делишки в нашей системе.

Ну а мы посмотрим в журнал Microsoft-Windows-Sysmon\Operational и убедимся, что наш Sysmon, к сожалению, больше ничего не пишет.
sysmon-eventid-255.PNG


Но не стоит отчаиваться, мы как безопасники получили «плюс один» детект для нашего SIEM:
Код: Скопировать в буфер обмена
Provider Name='Microsoft-Windows-Sysmon' and EventID='255' and Data Name.Description='Failed to access the driver - Last error: Неверный дескриптор'
С позиции же «спецов по проникновению» здесь есть еще куда посмотреть, но, пожалуй, мы продолжим это увлекательное занятие в другой раз.

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