
Explora drivers assinados vulneráveis para contornar callbacks do kernel da EDR, callbacks de objetos, provedor ETW TI e hooks no espaço do usuário para despejo de memória do LSASS e extração de credenciais.
EDRSandBlast é uma ferramenta escrita em C que armamentiza um driver assinado vulnerável para contornar detecções de EDR (callbacks de rotinas de notificação, callbacks de objeto e provedor ETW TI) e proteções do LSASS. Múltiplas técnicas de unhooking em modo de usuário também são implementadas para evadir a monitoração em modo de usuário.
Na data do lançamento, a combinação de técnicas de modo de usuário (--usermode) e modo kernel (--kernelmode) foi usada para despejar a memória do LSASS sob escrutínio de EDR, sem ser bloqueada nem gerar eventos relacionados a "Despejo de Credenciais do SO" no console (em nuvem) do produto. Os testes foram realizados em 3 produtos EDR distintos e foram bem-sucedidos em cada caso.
Os produtos EDR usam callbacks de "Rotinas de Notificação" do kernel no Windows para serem notificados pelo kernel sobre atividade do sistema, como criação de processos e threads e carregamento de imagens (exe / DLL).
Esses callbacks do kernel são definidos a partir do modo kernel, geralmente pelo driver que implementa os callbacks, usando várias APIs documentadas (nt!PsSetCreateProcessNotifyRoutine, nt!PsSetCreateThreadNotifyRoutine, etc.). Essas APIs adicionam rotinas de callback fornecidas pelo driver a arrays não documentados de rotinas no espaço do kernel:
PspCreateProcessNotifyRoutine para criação de processosPspCreateThreadNotifyRoutine para criação de threadsPspLoadImageNotifyRoutine para carregamento de imagensEDRSandBlast enumera as rotinas definidas nesses arrays e remove qualquer rotina de callback vinculada a uma lista predefinida de drivers EDR (mais de 1000 drivers de produtos de segurança suportados, consulte a seção de detecção de drivers EDR). A enumeração e remoção são possíveis através da exploração de uma primitiva arbitrária de leitura/escrita de memória do kernel fornecida pela exploração de um driver vulnerável (consulte a seção de drivers vulneráveis).
Os offsets dos arrays mencionados são recuperados usando múltiplas técnicas; consulte a seção de Offsets.
Os produtos EDR (e até EPP) frequentemente registram "callbacks de objeto" através do uso da API do kernel nt!ObRegisterCallbacks. Esses callbacks permitem que o produto de segurança seja notificado a cada geração de handle em tipos específicos de objeto (callbacks de objeto relacionados a Processos, Threads e Desktops agora são suportados pelo Windows). Uma geração de handle pode ocorrer na abertura de objeto (chamada a OpenProcess, OpenThread, etc.), bem como na duplicação de handle (chamada a DuplicateHandle, etc.).
Ao ser notificado pelo kernel em cada uma dessas operações, um produto de segurança pode analisar a legitimidade da criação do handle (por exemplo, um processo desconhecido está tentando abrir o LSASS), e até bloqueá-lo se uma ameaça for detectada.
A cada registro de callback usando ObRegisterCallbacks, um novo item é adicionado à lista duplamente ligada CallbackList presente no objeto _OBJECT_TYPE que descreve o tipo de objeto afetado pelo callback (seja um Processo, uma Thread ou um Desktop). Infelizmente, esses itens são descritos por uma estrutura que não é documentada nem publicada em arquivos de símbolos pela Microsoft. No entanto, estudá-la a partir de várias versões do ntoskrnl.exe parece indicar que a estrutura não mudou entre (pelo menos) as compilações 10240 e 22000 do Windows 10 (de 2015 a 2022).
A estrutura mencionada, representando um registro de callback de objeto, é a seguinte:```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;
A estrutura `OB_CALLBACK` mencionada acima também não é documentada, e é definida pelo seguinte:```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;
De forma a desativar os callbacks de objeto registados pelo EDR, três técnicas estão implementadas no EDRSandblast; no entanto, apenas uma está ativa no momento.
Enabled de OB_CALLBACK_ENTRYEsta é a técnica padrão ativada no EDRSandblast. Para detetar e desativar callbacks de objeto relacionados com o EDR, é percorrida a lista CallbackList localizada nos objetos _OBJECT_TYPE associados aos tipos Process e Thread. Ambos os _OBJECT_TYPEs são apontados por símbolos globais públicos no kernel, PsProcessType e PsThreadType.
Cada item da lista é assumido como correspondendo à estrutura OB_CALLBACK_ENTRY descrita acima (suposição que parece ser válida pelo menos em todas as builds do Windows 10 à data da escrita). As funções definidas nos campos PreOperation e PostOperation são localizadas para verificar se pertencem a um driver de EDR e, em caso afirmativo, os callbacks são simplesmente desativados alternando a flag Enabled.
Embora seja uma técnica bastante segura, tem o inconveniente de depender de uma estrutura não documentada; para reduzir o risco de manipulação insegura desta estrutura, são realizadas verificações básicas para validar que alguns campos têm os valores esperados:
Enabled é TRUE ou FALSE (não ria, um BOOL é um int, por isso pode ser qualquer coisa menos 1 ou 0);Operations é OB_OPERATION_HANDLE_CREATE, OB_OPERATION_HANDLE_DUPLICATE ou ambos;ObjectType aponta para PsProcessType ou PsThreadType.CallbackList de threads e processosOutra estratégia que não depende de uma estrutura não documentada (sendo, portanto, teoricamente mais robusta contra alterações no kernel NT) é a desassociação de toda a CallbackList tanto para processos como para threads. O objeto _OBJECT_TYPE é o seguinte:```C
struct _OBJECT_TYPE {
LIST_ENTRY TypeList;
UNICODE_STRING Name;
[...]
_OBJECT_TYPE_INITIALIZER TypeInfo;
[...]
LIST_ENTRY CallbackList;
}
Fazer os ponteiros `Flink` e `Blink` do `CallbackList` `LIST_ENTRY` apontarem para
o próprio `LIST_ENTRY` torna efetivamente a lista vazia. Como a estrutura `_OBJECT_TYPE`
é publicada nos símbolos do kernel, a técnica não depende de offsets/estruturas fixas.
No entanto, tem algumas desvantagens.
A primeira é não conseguir desabilitar apenas callbacks do EDR; na verdade, a técnica afeta
todos os callbacks de objeto que poderiam ter sido registrados por software "legítimo". Deve-se
notar, no entanto, que os callbacks de objeto não são usados por nenhum componente pré-instalado
no Windows 10 (no momento em que este texto foi escrito), portanto desativá-los não deve afetar a
estabilidade da máquina (ainda mais se a desativação for apenas temporária).