
Превращает уязвимые подписанные драйверы в оружие для обхода обратных вызовов ядра EDR, обратных вызовов объектов, провайдера ETW TI и хуков пользовательского режима с целью дампа памяти LSASS и извлечения учетных данных.
EDRSandBlast — это инструмент, написанный на C, который использует уязвимый подписанный драйвер для обхода обнаружения EDR (обратных вызовов Notify Routine, объектных обратных вызовов и провайдера ETW TI) и защиты LSASS. Также реализованы несколько методов снятия перехватчиков в пользовательском режиме для обхода мониторинга на уровне пользователя.
На момент выпуска для дампа памяти LSASS под наблюдением EDR использовалась комбинация методов пользовательского режима (--usermode) и режима ядра (--kernelmode), при этом не было ни блокировок, ни событий, связанных с «OS Credential Dumping», в (облачной) консоли продукта. Тесты проводились на 3 различных продуктах EDR и были успешны в каждом случае.
Продукты EDR используют обратные вызовы «Notify Routines» ядра в 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 год).
Упомянутая структура, представляющая регистрацию объектного обратного вызова, выглядит следующим образом:```C 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`, упомянутая выше, также не документирована и определяется следующим образом:```C
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 выглядит следующим образом:```C
struct _OBJECT_TYPE {
LIST_ENTRY TypeList;
UNICODE_STRING Name;
[...]
_OBJECT_TYPE_INITIALIZER TypeInfo;
[...]
LIST_ENTRY CallbackList;
}
Задание указателей `Flink` и `Blink` структуры `LIST_ENTRY` списка `CallbackList` указывать на саму `LIST_ENTRY` фактически делает список пустым. Поскольку структура `_OBJECT_TYPE` опубликована в символах ядра, этот метод не полагается на жестко заданные смещения/структуры. Однако у него есть некоторые недостатки.
Первый заключается в невозможности отключить только обратные вызовы от EDR; действительно, этот метод затрагивает все объектные обратные вызовы, которые могли быть зарегистрированы «легитимным» программным обеспечением. Тем не менее, следует отметить, что объектные обратные вызовы не используются ни одним предустановленным компонентом в Windows 10 (на момент написания), поэтому их отключение не должно повлиять на стабильность машины (тем более если отключение временное).