Габриэль Ландау из Elastic Security. Модификация EDRSandblast — см. оригинальный README ниже.
Интегрирует GodFault в EDR Sandblast, достигая того же результата без использования каких-либо уязвимых драйверов.
C:UsersuserDesktopOffsets>EDRSandblast.exe --kernelmode cmd
______ _____ _____ _____ _ _ _ _
| ____| __ | __ / ____| | | | | | | |
| |__ | | | | |__) | (___ __ _ _ __ __| | |__ | | __ _ ___| |_
| __| | | | | _ / ___ / _` | '_ / _` | '_ | |/ _` / __| __|
| |____| |__| | | ____) | (_| | | | | (_| | |_) | | (_| __ | |_
|______|_____/|_| _|_____/ __,_|_| |_|__,_|_.__/|_|__,_|___/__|
D3FC0N 30 Edition | Thomas DIOT (@_Qazeer) & Maxime MEIGNAN (@th3m4ks)
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives...
[+] Loading kernel related offsets from the CSV file
[*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe
[+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)!
[+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations...
[+] [NotifyRountines] Enumerating process creation callbacks
[+] Running command: GodFault.exe -t 2684
[?] Server does not appear to be running. Attempting to install it...
[+] CSRSS PID is 748
[+] Testing initial ability to acquire PROCESS_ALL_ACCESS to System: Failure
[+] Ready. Spawning WinTcb.
[+] SpawnPPL: Waiting for child process to finish.
[+] Thread 2684 (KTHREAD FFFF910961E4C080) has been blessed by GodFault
[+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500]
[+] [NotifyRountines] fffff8034e9efdc0 [WdFilter.sys + 0x4fdc0]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460]
[+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0]
[+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980]
[+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890]
[+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0]
[+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70]
[+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00]
[+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550]
[+] [NotifyRountines] Found a total of 1 EDR / security products driver(s)
[+] [NotifyRountines] Enumerating thread creation callbacks
[+] [NotifyRountines] fffff8034e9f15c0 [WdFilter.sys + 0x515c0]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff8034e9f1350 [WdFilter.sys + 0x51350]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010]
[+] [NotifyRountines] Found a total of 2 EDR / security products driver(s)
[+] [NotifyRountines] Enumerating image loading callbacks
[+] [NotifyRountines] fffff8034e9f0820 [WdFilter.sys + 0x50820]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710]
[+] [NotifyRountines] Found a total of 1 EDR / security products driver(s)
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication...
[+] [ObjectCallblacks] Enumerating Process object callbacks :
[+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications:
[+] [ObjectCallblacks] Status: Enabled
[+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30]
[+] [ObjectCallblacks] Callback belongs to an EDR and is enabled!
[+] [ObjectCallblacks] Enumerating Thread object callbacks :
[+] [ObjectCallblacks] Object callbacks are present !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state...
[+] [ETWTI] ETW Threat Intelligence Provider is ENABLED!
[+] Process is NOT "safe" to launch our payload, removing monitoring and starting another process...
[+] [ETWTI] Disabling the ETW Threat Intel provider by patching ProviderEnableInfo at 0xffff91095ce8c430 with 0x00.
[+] [ETWTI] The ETW Threat Intel provider was successfully disabled!
[+] Removing kernel callbacks registered by EDR for process creation, thread creation and image loading...
[+] [NotifyRountines] Removing process creation callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c2a8 | callback struct: 0xffff91095dbf3a5f | callback function: 0xfffff8034e9efdc0]
[+] [NotifyRountines] Removing thread creation callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a0 | callback struct: 0xffff91095dbf3b1f | callback function: 0xfffff8034e9f15c0]
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a8 | callback struct: 0xffff91095dbf3b4f | callback function: 0xfffff8034e9f1350]
[+] [NotifyRountines] Removing image loading callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c6a0 | callback struct: 0xffff91095dbf3e4f | callback function: 0xfffff8034e9f0820]
[+] Disabling kernel callbacks registered by EDR for process and thread opening or handle duplication...
[+] [ObjectCallblacks] Disabling WdFilter.sys callback...
[+] All EDR drivers were successfully removed from Kernel callbacks!
==================================================
Starting a new unmonitored process...
==================================================
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives...
[+] Loading kernel related offsets from the CSV file
[*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe
[+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)!
[+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations...
[+] [NotifyRountines] Enumerating process creation callbacks
[+] Running command: GodFault.exe -t 8344
[+] Thread 8344 (KTHREAD FFFF91096169F080) has been blessed by GodFault
[+] Initial blessing successful
[+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500]
[+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460]
[+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0]
[+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980]
[+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890]
[+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0]
[+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70]
[+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00]
[+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550]
[+] [NotifyRountines] No EDR driver(s) found!
[+] [NotifyRountines] Enumerating thread creation callbacks
[+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010]
[+] [NotifyRountines] No EDR driver(s) found!
[+] [NotifyRountines] Enumerating image loading callbacks
[+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710]
[+] [NotifyRountines] No EDR driver(s) found!
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication...
[+] [ObjectCallblacks] Enumerating Process object callbacks :
[+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications:
[+] [ObjectCallblacks] Status: Disabled
[+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30]
[+] [ObjectCallblacks] Callback belongs to an EDR but is disabled.
[+] [ObjectCallblacks] Enumerating Thread object callbacks :
[+] [ObjectCallblacks] Object callbacks are not found !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state...
[+] [ETWTI] ETW Threat Intelligence Provider is DISABLED!
[+] Process is "safe" to launch our payload
[+] Kernel callbacks have normally been removed, starting cmd.exe
WARNING: EDR kernel callbacks will be restored after exiting the cmd prompt (by typing exit)
WARNING: While unlikely, the longer the callbacks are removed, the higher the chance of being detected / causing a BSoD upon restore is!
Microsoft Windows [Version 10.0.22621.1702]
(c) Microsoft Corporation. All rights reserved.
C:UsersuserDesktopOffsets>
? Оригинальный README ниже?
EDRSandBlast
— это инструмент, написанный на C
, который использует уязвимый подписанный драйвер для обхода обнаружений EDR (обратные вызовы Notify Routine, обратные вызовы объектов и поставщик ETW TI
) и защиты LSASS
. Также реализовано несколько методов отсоединения пользовательского пространства, чтобы избежать мониторинга пользовательского пространства.
Начиная с выпуска, комбинация методов userland ( --usermode
) и Kernel-land ( --kernelmode
) использовалась для дампа памяти LSASS
при проверке EDR без блокировки и создания событий, связанных с «Сбросом учетных данных ОС», в продукте (облаке). ) консоль. Испытания проводились на трех различных продуктах EDR и в каждом случае оказались успешными.
Продукты EDR используют обратные вызовы «Подпрограммы уведомления» ядра в Windows для уведомления ядра о системной активности, такой как создание процессов и потоков и загрузка изображений ( exe
/ DLL
).
Эти обратные вызовы ядра определяются на уровне ядра, обычно из драйвера, реализующего обратные вызовы, с использованием ряда документированных API ( nt!PsSetCreateProcessNotifyRoutine
, nt!PsSetCreateThreadNotifyRoutine
и т. д.). Эти API добавляют подпрограммы обратного вызова, предоставляемые драйвером, к недокументированным массивам подпрограмм в пространстве ядра:
PspCreateProcessNotifyRoutine
для создания процессаPspCreateThreadNotifyRoutine
для создания потоковPspLoadImageNotifyRoutine
для загрузки изображений EDRSandBlast
перечисляет процедуры, определенные в этих массивах, и удаляет все процедуры обратного вызова, связанные с предопределенным списком драйверов EDR (поддерживается более 1000 драйверов продуктов безопасности, см. раздел «Обнаружение драйверов EDR»). Перечисление и удаление становятся возможными благодаря использованию произвольный примитив чтения/записи памяти ядра, предоставляемый эксплуатацией уязвимого драйвера (см. раздел «Уязвимые драйверы»).
Смещения вышеупомянутых массивов восстанавливаются с использованием нескольких методов, см. раздел «Смещения».
Продукты EDR (и даже EPP) часто регистрируют «обратные вызовы объектов» с помощью API ядра nt!ObRegisterCallbacks
. Эти обратные вызовы позволяют продукту безопасности получать уведомления при каждом создании дескриптора для определенных типов объектов (обратные вызовы объектов, связанных с процессами, потоками и рабочими столами, теперь поддерживаются Windows). Генерация дескриптора может происходить при открытии объекта (вызов OpenProcess
, OpenThread
и т. д.), а также дублирование дескриптора (вызов DuplicateHandle
и т. д.).
Получив уведомление от ядра о каждой из этих операций, продукт безопасности может проанализировать легитимность создания дескриптора ( например, неизвестный процесс пытается открыть LSASS ) и даже заблокировать его в случае обнаружения угрозы.
При каждой регистрации обратного вызова с использованием ObRegisterCallbacks
новый элемент добавляется в двусвязный список CallbackList
присутствующий в объекте _OBJECT_TYPE
, описывающий тип объекта, на который влияет обратный вызов (процесс, поток или рабочий стол). К сожалению, эти элементы описываются структурой, которая не документирована и не опубликована в файлах символов Microsoft. Однако изучение его в различных версиях ntoskrnl.exe
, похоже, указывает на то, что структура не менялась между (по крайней мере) сборками Windows 10 10240 и 22000 (с 2015 по 2022 год).
Упомянутая структура, представляющая регистрацию обратного вызова объекта, выглядит следующим образом:
typedef struct OB_CALLBACK_ENTRY_t {
LIST_ENTRY CallbackList ; // linked element tied to _OBJECT_TYPE.CallbackList
OB_OPERATION Operations ; // bitfield : 1 for Creations, 2 for Duplications
BOOL Enabled ; // self-explanatory
OB_CALLBACK * Entry ; // points to the structure in which it is included
POBJECT_TYPE ObjectType ; // points to the object type affected by the callback
POB_PRE_OPERATION_CALLBACK PreOperation ; // callback function called before each handle operation
POB_POST_OPERATION_CALLBACK PostOperation ; // callback function called after each handle operation
KSPIN_LOCK Lock ; // lock object used for synchronization
} OB_CALLBACK_ENTRY ;
Упомянутая выше структура OB_CALLBACK
также недокументирована и определяется следующим:
typedef struct OB_CALLBACK_t {
USHORT Version ; // usually 0x100
USHORT OperationRegistrationCount ; // number of registered callbacks
PVOID RegistrationContext ; // arbitrary data passed at registration time
UNICODE_STRING AltitudeString ; // used to determine callbacks order
struct OB_CALLBACK_ENTRY_t EntryItems [ 1 ]; // array of OperationRegistrationCount items
WCHAR AltitudeBuffer [ 1 ]; // is AltitudeString.MaximumLength bytes long, and pointed by AltitudeString.Buffer
} OB_CALLBACK ;
Чтобы отключить обратные вызовы объектов, зарегистрированных в EDR, в EDRSandblast
реализованы три метода; однако на данный момент включен только один.
Enabled
OB_CALLBACK_ENTRY
Это метод по умолчанию, включенный в EDRSandblast
. Чтобы обнаружить и отключить обратные вызовы объектов, связанных с EDR, просматривается список CallbackList
, расположенный в объектах _OBJECT_TYPE
привязанных к типам Process и Thread . На оба _OBJECT_TYPE
указывают общедоступные глобальные символы ядра PsProcessType
и PsThreadType
.
Предполагается, что каждый элемент списка соответствует структуре OB_CALLBACK_ENTRY
, описанной выше (предположение, которое, по-видимому, справедливо по крайней мере во всех сборках Windows 10 на момент написания). Функции, определенные в полях PreOperation
и PostOperation
предназначены для проверки того, принадлежат ли они драйверу EDR, и если да, то обратные вызовы просто отключаются путем переключения флага Enabled
.
Несмотря на то, что это довольно безопасный метод, он неудобно полагаться на недокументированную структуру; чтобы снизить риск небезопасного манипулирования этой структурой, выполняются базовые проверки для проверки того, что некоторые поля имеют ожидаемые значения:
Enabled
имеет значение TRUE
или FALSE
( не смейтесь, BOOL
— это int
число, поэтому оно может быть любым, кроме 1
или 0
);Operations
— OB_OPERATION_HANDLE_CREATE
, OB_OPERATION_HANDLE_DUPLICATE
или оба;ObjectType
указывает на PsProcessType
или PsThreadType
. CallbackList
потоков и процесса Другая стратегия, которая не опирается на недокументированную структуру (и, следовательно, теоретически более устойчива к изменениям ядра NT), — это отсоединение всего CallbackList
как для процессов, так и для потоков. Объект _OBJECT_TYPE
имеет следующий вид:
struct _OBJECT_TYPE {
LIST_ENTRY TypeList ;
UNICODE_STRING Name ;
[...]
_OBJECT_TYPE_INITIALIZER TypeInfo ;
[...]
LIST_ENTRY CallbackList ;
}
Если указатели Flink
и Blink
CallbackList
LIST_ENTRY
указывают на сам LIST_ENTRY
, фактически список становится пустым. Поскольку структура _OBJECT_TYPE
публикуется в символах ядра, этот метод не опирается на жестко закодированные смещения/структуры. Однако у него есть некоторые недостатки.
Во-первых, невозможно отключить только обратные вызовы из EDR; действительно, этот метод влияет на все обратные вызовы объектов, которые могли быть зарегистрированы «законным» программным обеспечением. Тем не менее следует отметить, что обратные вызовы объектов не используются ни одним предустановленным компонентом в Windows 10 (на момент написания), поэтому их отключение не должно повлиять на стабильность машины (тем более, если отключение носит временный характер).
Второй недостаток заключается в том, что операции обработки дескрипторов процессов или потоков происходят очень часто (почти непрерывно) при нормальном функционировании ОС. Таким образом, если используемый примитив записи ядра не может выполнить запись QWORD
«атомарно», существует большая вероятность того, что указатель _OBJECT_TYPE.CallbackList.Flink
будет доступен ядру в середине его перезаписи. Например, уязвимый драйвер MSI RTCore64.sys
может одновременно выполнять только запись DWORD
, поэтому для перезаписи указателя потребуются два отдельных IOCTL, между которыми ядро имеет высокую вероятность его использования (что приведет к сбою). С другой стороны, уязвимый драйвер DELL DBUtil_2_3.sys
может выполнять записи произвольных размеров за один IOCTL, поэтому использование этого метода с ним не рискует вызвать сбой.
Последний найденный нами метод — полное отключение поддержки объектных обратных вызовов для потоков и процессов. Внутри структуры _OBJECT_TYPE
, соответствующей типам процессов и потоков, находится поле TypeInfo
, следующее за документированной структурой _OBJECT_TYPE_INITIALIZER
. Последний содержит битовое поле ObjectTypeFlags
, флаг SupportsObjectCallbacks
которого определяет, поддерживает ли описываемый тип объекта (Процесс, Поток, Рабочий стол, Токен, Файл и т.д.) регистрацию обратного вызова объекта или нет. Как указывалось ранее, только типы объектов «Процесс», «Поток» и «Рабочий стол» поддерживают эти обратные вызовы при установке Windows на момент написания статьи.
Поскольку бит SupportsObjectCallbacks
проверяется ObpCreateHandle
или ObDuplicateObject
еще до чтения CallbackList
(и, конечно, перед выполнением обратных вызовов), переключение бита во время выполнения ядра эффективно отключает выполнение всех обратных вызовов объекта.
Основным недостатком метода является то, что KPP (« PatchGuard ») отслеживает целостность некоторых (всех?) структур _OBJECT_TYPE
и запускает 0x109 Bug Check
с параметром 4, равным 0x8
, что означает, что структура типа объекта была изменена.
Однако достаточно быстрого выполнения отключения/повторного включения (и «злонамеренного» действия между ними) должно быть достаточно, чтобы «гонять» PatchGuard (если только вам не повезло и периодическая проверка не выполняется в неподходящий момент).
Поставщик ETW Microsoft-Windows-Threat-Intelligence
регистрирует данные об использовании некоторых API Windows, которые обычно используются злонамеренно. Сюда входит API nt!MiReadWriteVirtualMemory
, вызываемый nt!NtReadVirtualMemory
(который используется для дампа памяти LSASS
) и контролируемый функцией nt!EtwTiLogReadWriteVm
.
Продукты EDR могут использовать журналы, созданные поставщиком ETW TI
через службы или процессы, запущенные как SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT
или PS_PROTECTED_ANTIMALWARE_LIGHT
соответственно и связанные с драйвером Early Launch Anti Malware (ELAM)
.
Как опубликовано slaeryan
в блоге CNO Development Labs
, провайдер ETW TI
можно полностью отключить, исправив в памяти ядра его атрибут ProviderEnableInfo
на 0x0
. Дополнительную информацию об этой технике можно найти в замечательном вышеупомянутом сообщении в блоге.
Аналогично удалению обратных вызовов ядра, необходимые смещения ntoskrnl.exe
( nt!EtwThreatIntProvRegHandleOffset
, GuidEntry
_ETW_REG_ENTRY
и ProviderEnableInfo
_ETW_GUID_ENTRY
) вычисляются в файле NtoskrnlOffsets.csv
для ряда версий ядра Windows.
Чтобы легко отслеживать действия, выполняемые процессами, продукты EDR часто используют механизм, называемый перехватом пользовательского пространства . Во-первых, продукты EDR регистрируют обратный вызов ядра (обычно обратные вызовы загрузки образа или создания процесса , см. выше), который позволяет им получать уведомления при каждом запуске процесса.
Когда процесс загружается Windows и до его фактического запуска, EDR может внедрить некоторую специальную DLL в адресное пространство процесса, которое содержит его логику мониторинга. Во время загрузки эта DLL вводит « перехватчики » в начало каждой функции, которую должен контролировать EDR. Во время выполнения, когда контролируемые функции вызываются наблюдаемым процессом, эти перехватчики перенаправляют поток управления на некоторый код контроля, присутствующий в DLL EDR, что позволяет ему проверять аргументы и возвращать значения этих вызовов.
В большинстве случаев отслеживаемые функции представляют собой системные вызовы (такие как NtReadVirtualMemory
, NtOpenProcess
и т. д.), реализации которых находятся в ntdll.dll
. Перехват вызовов функций Nt*
позволяет продуктам находиться как можно ближе к границе области пользователя/ядра (оставаясь при этом в области пользователя), но также можно отслеживать функции из некоторых DLL более высокого уровня.
Ниже приведены примеры одной и той же функции до и после подключения к продукту EDR:
NtProtectVirtualMemory proc near
mov r10 , rcx
mov eax , 50h
test byte ptr ds : 7FFE0308h , 1
jnz short loc_18009D1E5
syscall
retn
loc_18009D1E5:
int 2Eh
retn
NtProtectVirtualMemory endp
NtProtectVirtualMemory proc near
jmp sub_7FFC74490298 ; --> "hook", jump to EDR analysis function
int 3 ; overwritten instructions
int 3 ; overwritten instructions
int 3 ; overwritten instructions
test byte_7FFE0308 , 1 ; <-- execution resumes here after analysis
jnz short loc_7FFCB44AD1E5
syscall
retn
loc_7FFCB44AD1E5:
int 2Eh
retn
NtProtectVirtualMemory endp
Хуки пользовательской области имеют «слабую сторону»: они располагаются в памяти пользовательской области, что означает, что они могут быть непосредственно наблюдаемы и изменены исследуемым процессом. Основная идея автоматического обнаружения перехватчиков в адресном пространстве процесса состоит в том, чтобы сравнить различия между исходной DLL на диске и библиотекой, находящейся в памяти, которая потенциально была изменена EDR. Чтобы выполнить это сравнение, EDRSandblast выполняет следующие шаги:
InLoadOrderModuleList
расположенному в PEB
(чтобы избежать вызова любого API, который может отслеживаться и вызывать подозрения). Примечание. Этот процесс можно обобщить, чтобы найти различия где угодно в недоступных для записи разделах, а не только в начале экспортируемых функций, например, если продукты EDR начинают применять перехватчики в середине функции :) Таким образом, инструмент не используется, это было реализовано в findDiffsInNonWritableSections
.
Чтобы обойти мониторинг, осуществляемый этими перехватчиками, возможно несколько методов, каждый из которых имеет свои преимущества и недостатки.
Самый интуитивно понятный способ обойти мониторинг на основе перехватчиков — удалить перехватчики. Поскольку перехватчики присутствуют в памяти, доступной самому процессу, удалить перехватчик процесс может просто:
Этот подход довольно прост и может использоваться для одновременного удаления всех обнаруженных хуков. Выполняемое в начале атакующим инструментом, это позволяет остальной части кода полностью не знать о механизме перехвата и нормально работать без контроля.
Однако у него есть два основных недостатка. EDR, вероятно, отслеживает использование NtProtectVirtualMemory
, поэтому использовать его для изменения разрешений страницы, на которой были установлены перехватчики, является (по крайней мере, концептуально) плохой идеей. Кроме того, если поток выполняется EDR и периодически проверяет целостность перехватчиков, это также может вызвать некоторое обнаружение.
Для получения подробной информации о реализации проверьте путь кода функции unhook()
, если unhook_method
равен UNHOOK_WITH_NTPROTECTVIRTUALMEMORY
.
Важное примечание: для простоты этот метод реализован в EDRSandblast как базовый метод, используемый для демонстрации других методов обхода; каждый из них демонстрирует, как получить неотслеживаемую версию NtProtectVirtualMemory
, но потом выполняет ту же операцию (отсоединяя конкретный перехват).
Чтобы обойти конкретный хук, можно просто «перепрыгнуть» и выполнить остальную часть функции как есть. Во-первых, из файла DLL необходимо восстановить исходные байты отслеживаемой функции, которые были перезаписаны EDR для установки перехватчика. В нашем предыдущем примере кода это будут байты, соответствующие следующим инструкциям:
mov r10 , rcx
mov eax , 50h
Идентификация этих байтов — простая задача, поскольку мы можем выполнить чистый анализ версий библиотеки как в памяти, так и на диске, как описано ранее. Затем мы собираем инструкцию перехода, предназначенную для перенаправления потока управления на код, следующий сразу за перехватчиком, по адресу NtProtectVirtualMemory + sizeof(overwritten_instructions)
jmp NtProtectVirtualMemory + 8
Наконец, мы объединяем эти коды операций, сохраняем их в (новой) исполняемой памяти и сохраняем указатель на них. Этот объект называется « батутом » и затем может использоваться как указатель на функцию, что строго эквивалентно исходной функции NtProtectVirtualMemory
.
Основное преимущество этого метода, как и любого другого метода, описанного ниже, заключается в том, что перехватчик никогда не стирается, поэтому любая проверка целостности, выполняемая EDR для перехватчиков, должна пройти успешно. Однако для этого требуется выделить память, доступную для записи, а затем для исполнения, что типично для выделения шелл-кода, что привлекает внимание EDR.
Для получения подробной информации о реализации проверьте путь кода функции unhook()
, если unhook_method
равен UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
. Помните, что этот метод продемонстрирован только в нашей реализации и, в конечном итоге, используется для удаления перехватчиков из памяти, как и любой другой метод, описанный ниже.
Продукт EDR, чтобы его хук работал, должен сохранять где-то в памяти коды операций, которые он удалил. Хуже ( или «лучше», с точки зрения злоумышленника ) для эффективного использования исходных инструкций EDR, вероятно, выделил себе где-то батут для выполнения исходной функции после перехвата вызова.
Этот батут можно найти и использовать в качестве замены подцепленной функции без необходимости выделять исполняемую память или вызывать какой-либо API, кроме VirtualQuery
, который, скорее всего, не отслеживается, поскольку является безобидной функцией.
Чтобы найти батут в памяти, мы просматриваем все адресное пространство с помощью VirtualQuery
в поисках выделенной и исполняемой памяти. Для каждой такой области памяти мы сканируем ее в поисках инструкции перехода, нацеленной на адрес, следующий за перезаписанными инструкциями ( NtProtectVirtualMemory+8
в нашем предыдущем примере). Затем батут можно использовать для вызова подключённой функции без срабатывания перехватчика.
Этот метод работает на удивление хорошо, поскольку восстанавливает почти все батуты на протестированном EDR. Для получения подробной информации о реализации проверьте путь кода функции unhook()
, если unhook_method
равен UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
.
Еще один простой способ получить доступ к неотслеживаемой версии функции NtProtectVirtualMemory
— загрузить дубликат версии библиотеки ntdll.dll
в адресное пространство процесса. Поскольку две идентичные библиотеки DLL могут быть загружены в одном процессе, при условии, что они имеют разные имена, мы можем просто скопировать законный файл ntdll.dll
в другое место, загрузить его с помощью LoadLibrary
(или переопределить процесс загрузки) и получить доступ к функции с помощью GetProcAddress
например.
Этот метод очень прост для понимания и реализации, и он имеет хорошие шансы на успех, поскольку большинство продуктов EDR не переустанавливают перехватчики на вновь загруженные библиотеки DLL после запуска процесса. Однако основным недостатком является то, что копирование подписанных Microsoft двоичных файлов под другим именем часто рассматривается продуктами EDR как подозрительное.
Эта методика тем не менее реализована в EDRSandblast
. Для получения подробной информации о реализации проверьте путь кода функции unhook()
, если unhook_method
равен UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY
.
Чтобы использовать функции, связанные с системными вызовами, одна программа может переопределить системные вызовы (в ассемблере) для вызова соответствующих функций ОС, фактически не касаясь кода в ntdll.dll
, который может отслеживаться EDR. Это полностью обходит любое перехват пользовательского пространства, выполняемое для функций системного вызова в ntdll.dll
.
Тем не менее, это имеет некоторые недостатки. Во-первых, это подразумевает возможность знать список номеров системных вызовов необходимых программе функций, который меняется для каждой версии Windows. Тем не менее, это смягчается за счет реализации нескольких эвристик, которые, как известно, работали во всех предыдущих версиях Windows NT (сортировка экспорта ntdll
's' Zw*
, поиск mov rax, #syscall_number
в связанной функции ntdll
и т. д.) и проверка того, что все они возвращают один и тот же результат (более подробную информацию см. Syscalls.c
).
Кроме того, функции, которые технически не являются системными вызовами (например, LoadLibraryX
/ LdrLoadDLL
), также могут отслеживаться и не могут быть просто переопределены с помощью системного вызова.
Техника прямых системных вызовов реализована в EDRSandblast. Как говорилось ранее, он используется только для безопасного выполнения NtProtectVirtualMemory
и удаления всех обнаруженных перехватчиков.
Для получения подробной информации о реализации проверьте путь кода функции unhook()
, если unhook_method
равен UNHOOK_WITH_DIRECT_SYSCALL
.
Как говорилось ранее, каждое действие, требующее чтения или записи памяти ядра, зависит от уязвимого драйвера, предоставляющего этот примитив. В EDRSanblast добавить поддержку нового драйвера, обеспечивающего примитив чтения/записи, можно «легко» сделать, нужно реализовать всего три функции:
ReadMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
, которая копирует байты Size
из Address
ядра Address в буфер пользовательской области Buffer
;WriteMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
, которая копирует байты Size
из пользовательского буфера Buffer
в Address
ядра Address ;CloseDriverHandle_DRIVERNAME()
, который гарантирует закрытие всех дескрипторов драйвера (необходим перед операцией удаления, которая на данный момент не зависит от драйвера). Например, EDRSandblast в настоящее время поддерживает два драйвера: RTCore64.sys
(SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD
) и DBUtils_2_3.sys
(SHA256: 0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5
). Следующий код в KernelMemoryPrimitives.h
необходимо обновить, если необходимо изменить используемый уязвимый драйвер или реализовать новый.
#define RTCore 0
#define DBUtil 1
// Select the driver to use with the following #define
#define VULN_DRIVER RTCore
#if VULN_DRIVER == RTCore
#define DEFAULT_DRIVER_FILE TEXT("RTCore64.sys")
#define CloseDriverHandle CloseDriverHandle_RTCore
#define ReadMemoryPrimitive ReadMemoryPrimitive_RTCore
#define WriteMemoryPrimitive WriteMemoryPrimitive_RTCore
#elif VULN_DRIVER == DBUtil
#define DEFAULT_DRIVER_FILE TEXT("DBUtil_2_3.sys")
#define CloseDriverHandle CloseDriverHandle_DBUtil
#define ReadMemoryPrimitive ReadMemoryPrimitive_DBUtil
#define WriteMemoryPrimitive WriteMemoryPrimitive_DBUtil
#endif
В настоящее время используется несколько методов, чтобы определить, принадлежит ли конкретный драйвер или процесс продукту EDR или нет.
Во-первых, для этой цели можно просто использовать имя драйвера. Действительно, Microsoft выделяет определенные номера, называемые «Высоты», для всех драйверов, которым необходимо вставлять обратные вызовы в ядро. Это обеспечивает детерминированный порядок выполнения обратных вызовов, независимый от порядка регистрации, но только на основе использования драйвера. Список водителей (поставщиков), зарезервировавших определенную высоту , можно найти в MSDN. Как следствие, Microsoft предлагает почти полный список названий драйверов безопасности, связанных с продуктами безопасности, в основном в списках «FSFilter Anti-Virus» и «FSFilter Activity Monitor». Эти списки имен драйверов встроены в EDRSandblast, а также в дополнительные материалы.
Более того, исполняемые файлы EDR и DLL чаще всего имеют цифровую подпись с использованием сертификата подписи поставщика. Таким образом, проверка подписывающего лица исполняемого файла или библиотеки DLL, связанной с процессом, может позволить быстро идентифицировать продукты EDR.
Кроме того, драйверы должны быть напрямую подписаны Microsoft, чтобы их можно было загружать в пространство ядра. Хотя поставщик драйвера не является непосредственно подписывающим лицом самого драйвера, может показаться, что имя поставщика по-прежнему включено в атрибут подписи; тем не менее, этот метод обнаружения еще предстоит изучить и внедрить.
Наконец, при столкновении с EDR, неизвестным EDRSandblast, лучший подход — запустить инструмент в режиме «аудита» и проверить список драйверов, имеющих зарегистрированные обратные вызовы ядра; затем имя драйвера можно добавить в список, инструмент перекомпилировать и повторно запустить.
Механизм Local Security Authority (LSA) Protection
, впервые представленный в Windows 8.1 и Windows Server 2012 R2, использует технологию Protected Process Light (PPL)
для ограничения доступа к процессу LSASS
. Защита PPL
регулирует и ограничивает такие операции, как внедрение памяти или дамп памяти защищенных процессов, даже из процесса, имеющего привилегию SeDebugPrivilege
. В рамках модели защиты процессов только процессы, работающие с более высокими уровнями защиты, могут выполнять операции над защищенными процессами.
Структура _EPROCESS
, используемая ядром Windows для представления процесса в памяти ядра, включает поле _PS_PROTECTION
определяющее уровень защиты процесса через его атрибуты Type
( _PS_PROTECTED_TYPE
) и Signer
( _PS_PROTECTED_SIGNER
).
Записывая в память ядра, процесс EDRSandblast может повысить свой уровень защиты до PsProtectedSignerWinTcb-Light
. Этого уровня достаточно для дампа памяти процесса LSASS
, поскольку он «доминирует» над PsProtectedSignerLsa-Light
, уровнем защиты процесса LSASS
, работающего с механизмом RunAsPPL
.
EDRSandBlast
реализует самозащиту следующим образом:
NtQuerySystemInformation
чтобы найти открытый дескриптор текущего процесса и адрес структуры EPROCESS
текущего процесса в памяти ядра.Micro-Star MSI Afterburner
для перезаписи поля _PS_PROTECTION
текущего процесса в памяти ядра. Смещения поля _PS_PROTECTION
относительно структуры EPROCESS
(определяемой используемой версией ntoskrnl
) вычисляются в файле NtoskrnlOffsets.csv
. Microsoft Credential Guard
— это технология изоляции на основе виртуализации, представленная в Microsoft Windows 10 (Enterprise edition)
, которая предотвращает прямой доступ к учетным данным, хранящимся в процессе LSASS
.
Когда Credentials Guard
активирован, процесс LSAIso
( LSA изолированный ) создается в Virtual Secure Mode
— функции, которая использует расширения виртуализации ЦП для обеспечения дополнительной безопасности данных в памяти. Доступ к процессу LSAIso
ограничен даже для доступа с контекстом безопасности NT AUTHORITYSYSTEM
. При обработке хеша процесс LSA
выполняет вызов RPC
к процессу LSAIso
и ожидает продолжения результата LSAIso
. Таким образом, процесс LSASS
не будет содержать никаких секретов и будет хранить LSA Isolated Data
.
Как указано в оригинальном исследовании, проведенном N4kedTurtle
: « Wdigest
можно включить в системе с Credential Guard, исправив значения g_fParameter_useLogonCredential
и g_IsCredGuardEnabled
в памяти». Активация Wdigest
приведет к сохранению учетных данных в открытом виде в памяти LSASS
для любых новых интерактивных входов в систему (без необходимости перезагрузки системы). Более подробную информацию об этой технике можно найти в оригинальном сообщении в исследовательском блоге.
EDRSandBlast
просто делает исходный PoC немного более дружественным к opsec и обеспечивает поддержку ряда версий wdigest.dll
(посредством вычисленных смещений для g_fParameter_useLogonCredential
и g_IsCredGuardEnabled
).
Чтобы надежно выполнять операции обхода мониторинга ядра, EDRSandblast должен точно знать, где читать и записывать память ядра. Это делается с помощью смещений глобальных переменных внутри целевого образа (ntoskrnl.exe, wdigest.dll), а также смещения определенных полей в структурах, определения которых публикуются Microsoft в файлах символов. Эти смещения специфичны для каждой сборки целевых образов и должны быть собраны хотя бы один раз для конкретной версии платформы.
Выбор использования «жестко закодированных» смещений вместо поиска по шаблонам для поиска структур и переменных, используемых EDRSandblast, оправдан тем фактом, что недокументированные API, ответственные за добавление / удаление обратных вызовов ядра, могут быть изменены и что любая попытка чтения или записи ядра память по неправильному адресу может (и часто будет) привести к Bug Check
( Blue Screen of Death
»). Сбой машины неприемлем ни в сценариях «красной команды», ни в обычных сценариях тестирования на проникновение, поскольку сбой машины хорошо виден защитникам и потеряет все учетные данные, которые все еще находились в памяти на момент атаки.
Для получения смещений для каждой конкретной версии Windows реализовано два подхода.
Необходимые смещения ntoskrnl.exe
и wdigest.dll
можно извлечь с помощью предоставленного скрипта Python ExtractOffsets.py
, который использует radare2
и r2pipe
для загрузки и анализа символов из файлов PDB и извлечения из них необходимых смещений. Затем смещения сохраняются в файлах CSV для последующего использования EDRSandblast.
Чтобы обеспечить поддержку широкого спектра сборок Windows, многие версии двоичных файлов ntoskrnl.exe
и wdigest.dll
ссылаются на Winbindex и могут быть автоматически загружены (и извлечены их смещения) с помощью ExtractOffsets.py
. Это позволяет извлекать смещения практически из всех файлов, которые когда-либо публиковались в пакетах обновлений Windows (на сегодняшний день доступно и предварительно рассчитано более 450 ntoskrnl.exe
и более 30 версий wdigest.dll
).
В EDRSandBlast
реализована дополнительная опция, позволяющая программе самой загружать необходимые файлы .pdb
с Microsoft Symbol Server, извлекать необходимые смещения и даже обновлять соответствующие файлы .csv
если они есть.
Использование опции --internet
значительно упрощает выполнение инструмента, но при этом создает дополнительный риск OpSec, поскольку во время процесса файл .pdb
загружается и сохраняется на диске. Это требуется для функций dbghelp.dll
используемых для анализа базы данных символов; однако в будущем может быть реализован полный анализ PDB в памяти, чтобы снять это требование и уменьшить занимаемое инструментом пространство.
Уязвимый драйвер RTCore64.sys
можно получить по адресу:
http://download-eu2.guru3d.com/afterburner/%5BGuru3D.com%5D-MSIAfterburnerSetup462Beta2.zip