
Sfrutta driver firmati vulnerabili per bypassare le callback del kernel EDR, le callback degli oggetti, il provider ETW TI e gli hook in userland per il dump della memoria di LSASS e l'estrazione delle credenziali.
EDRSandBlast è uno strumento scritto in C che sfrutta un driver vulnerabile firmato per bypassare i rilevamenti EDR (callback Notify Routine, Object Callback e provider ETW TI) e le protezioni di LSASS. Sono implementate anche molteplici tecniche di unhooking in userland per eludere il monitoraggio in userland.
Alla data del rilascio, una combinazione di tecniche in userland (--usermode) e kernel-land (--kernelmode) è stata utilizzata per dumpare la memoria di LSASS sotto l'esame dell'EDR, senza essere bloccata né generare eventi correlati a "OS Credential Dumping" nella console (cloud) del prodotto. I test sono stati eseguiti su 3 diversi prodotti EDR e hanno avuto successo in ogni caso.
I prodotti EDR utilizzano i callback "Notify Routines" del kernel su Windows per essere notificati dal kernel dell'attività di sistema, come la creazione di processi e thread e il caricamento di immagini (exe / DLL).
Questi callback del kernel sono definiti dal kernel, solitamente dal driver che implementa i callback, utilizzando una serie di API documentate (nt!PsSetCreateProcessNotifyRoutine, nt!PsSetCreateThreadNotifyRoutine, ecc.). Queste API aggiungono routine di callback fornite dal driver a array non documentati di routine nello spazio del kernel:
PspCreateProcessNotifyRoutine per la creazione di processiPspCreateThreadNotifyRoutine per la creazione di threadPspLoadImageNotifyRoutine per il caricamento di immaginiEDRSandBlast enumera le routine definite in questi array e rimuove qualsiasi routine di callback collegata a un elenco predefinito di driver EDR (più di 1000 driver di prodotti di sicurezza supportati, vedere la sezione Rilevamento dei driver e processi EDR). L'enumerazione e la rimozione sono possibili grazie allo sfruttamento di una primitiva arbitraria di lettura/scrittura della memoria del kernel fornita dallo sfruttamento di un driver vulnerabile (vedere la sezione Driver vulnerabili).
Gli offset degli array suddetti vengono recuperati utilizzando molteplici tecniche, fare riferimento alla sezione Offset.
I prodotti EDR (e anche EPP) registrano spesso "Object callback" tramite l'uso dell'API del kernel nt!ObRegisterCallbacks. Questi callback consentono al prodotto di sicurezza di essere notificato ad ogni generazione di handle su specifici tipi di oggetto (i callback di oggetti relativi a processi, thread e desktop sono ora supportati da Windows). Una generazione di handle può avvenire all'apertura di un oggetto (chiamata a OpenProcess, OpenThread, ecc.) così come alla duplicazione di handle (chiamata a DuplicateHandle, ecc.).
Essendo notificato dal kernel per ciascuna di queste operazioni, un prodotto di sicurezza può analizzare la legittimità della creazione dell'handle (ad es. un processo sconosciuto sta tentando di aprire LSASS) e persino bloccarlo se viene rilevata una minaccia.
Ad ogni registrazione di callback utilizzando ObRegisterCallbacks, un nuovo elemento viene aggiunto alla lista doppiamente collegata CallbackList presente nell'oggetto _OBJECT_TYPE che descrive il tipo di oggetto interessato dal callback (sia esso un processo, un thread o un desktop). Sfortunatamente, questi elementi sono descritti da una struttura che non è documentata né pubblicata nei file dei simboli da Microsoft. Tuttavia, studiandola da varie versioni di ntoskrnl.exe sembra indicare che la struttura non sia cambiata tra (almeno) Windows 10 build 10240 e 22000 (dal 2015 al 2022).
La struttura menzionata, che rappresenta una registrazione di callback di oggetto, è la seguente:```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;
La struttura `OB_CALLBACK` menzionata sopra è anch'essa non documentata, ed è definita dal seguente:```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;
Per disabilitare i callback degli oggetti registrati da EDR, in EDRSandblast sono implementate tre tecniche; tuttavia al momento solo una è abilitata.
Enabled di OB_CALLBACK_ENTRYQuesta è la tecnica predefinita abilitata in EDRSandblast. Per rilevare e disabilitare i callback degli oggetti correlati a EDR, viene esplorata la lista CallbackList situata negli oggetti _OBJECT_TYPE associati ai tipi Process e Thread. Entrambi i _OBJECT_TYPE sono puntati da simboli globali pubblici nel kernel, PsProcessType e PsThreadType.
Si assume che ogni elemento della lista corrisponda alla struttura OB_CALLBACK_ENTRY descritta sopra (assunzione che sembra valere almeno in tutte le build di Windows 10 al momento della scrittura). Le funzioni definite nei campi PreOperation e PostOperation vengono localizzate per verificare se appartengono a un driver EDR e, in tal caso, i callback vengono semplicemente disabilitati attivando/disattivando il flag Enabled.
Sebbene sia una tecnica abbastanza sicura, ha l'inconveniente di basarsi su una struttura non documentata; per ridurre il rischio di manipolazione non sicura di questa struttura, vengono eseguiti controlli di base per validare che alcuni campi abbiano i valori attesi:
Enabled è TRUE o FALSE (non ridere, un BOOL è un int, quindi potrebbe essere qualsiasi cosa diversa da 1 o 0);Operations è OB_OPERATION_HANDLE_CREATE, OB_OPERATION_HANDLE_DUPLICATE o entrambi;ObjectType punta a PsProcessType o PsThreadType.CallbackList di thread e processoUn'altra strategia che non si basa su una struttura non documentata (ed è quindi teoricamente più robusta rispetto ai cambiamenti del kernel NT) è lo scollegamento dell'intera CallbackList sia per i processi che per i thread. L'oggetto _OBJECT_TYPE è il seguente:```C
struct _OBJECT_TYPE {
LIST_ENTRY TypeList;
UNICODE_STRING Name;
[...]
_OBJECT_TYPE_INITIALIZER TypeInfo;
[...]
LIST_ENTRY CallbackList;
}
Facendo in modo che i puntatori `Flink` e `Blink` della `LIST_ENTRY` del `CallbackList` puntino alla `LIST_ENTRY` stessa, la lista viene effettivamente resa vuota. Poiché la struttura `_OBJECT_TYPE` è pubblicata nei simboli del kernel, la tecnica non si basa su offset/strutture hardcoded. Tuttavia, presenta alcuni svantaggi.
Il primo è l'impossibilità di disabilitare solo le callback dell'EDR; infatti, la tecnica interessa tutte le callback degli oggetti che potrebbero essere state registrate da software "legittimo". Va comunque notato che le callback degli oggetti non sono utilizzate da alcun componente preinstallato su Windows 10 (al momento della scrittura), quindi disabilitarle non dovrebbe influire sulla stabilità della macchina (ancor più se la disabilitazione è solo temporanea).
Il secondo svantaggio è che le operazioni sugli handle di processi o thread sono molto frequenti (quasi continue) nel normale funzionamento del sistema operativo. Pertanto, se la primitiva di scrittura nel kernel utilizzata non è in grado di eseguire una scrittura `QWORD` in modo "atomico", c'è un'alta probabilità che il puntatore `_OBJECT_TYPE.CallbackList.Flink` venga accesso dal kernel nel bel mezzo della sua sovrascrittura. Ad esempio, il driver vulnerabile MSI `RTCore64.sys` può scrivere solo un `DWORD` alla volta, quindi saranno necessari 2 IOCTL distinti per sovrascrivere il puntatore, tra i quali il kernel ha un'alta probabilità di utilizzarlo (provocando un crash). D'altra parte, il driver vulnerabile DELL `DBUtil_2_3.sys` può eseguire scritture di dimensioni arbitrarie in un unico IOCTL, quindi con questo metodo non si rischia un crash.
#### Disabilitazione completa delle callback degli oggetti
Un'ultima tecnica che abbiamo trovato è quella di disabilitare completamente il supporto delle callback degli oggetti per thread e processi. All'interno della struttura `_OBJECT_TYPE` corrispondente ai tipi processo e thread, si trova un campo `TypeInfo`, che segue la struttura documentata `_OBJECT_TYPE_INITIALIZER`. Quest'ultima contiene un campo di bit `ObjectTypeFlags`, il cui flag `SupportsObjectCallbacks` determina se il tipo di oggetto descritto (Process, Thread, Desktop, Token, File, ecc.) supporta o meno la registrazione di callback. Come già detto, al momento della scrittura solo i tipi Process, Thread e Desktop supportano queste callback su un'installazione Windows.
Poiché il bit `SupportsObjectCallbacks` viene verificato da `ObpCreateHandle` o `ObDuplicateObject` prima ancora di leggere il `CallbackList` (e prima di eseguire le callback, ovviamente), invertire il bit in fase di esecuzione del kernel disabilita effettivamente l'esecuzione di tutte le callback degli oggetti.
Il principale svantaggio del metodo è semplicemente che il *KPP* ("*PatchGuard*") monitora l'integrità di alcune (tutte?) strutture `_OBJECT_TYPE` e attiva un [`Bug Check 0x109`](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/bug-check-0x109---critical-structure-corruption) con il parametro 4 uguale a `0x8`, che indica che una struttura del tipo di oggetto è stata alterata.
Tuttavia, eseguire la disabilitazione/riabilitazione (e l'azione "maligna" nel mezzo) abbastanza velocemente dovrebbe bastare per "superare" *PatchGuard* (a meno che non si sia sfortunati e un controllo periodico venga eseguito proprio nel momento sbagliato).
### Bypass dell'EDR tramite scollegamento delle callback dei minifilter
Il sistema Windows Filter Manager consente a un EDR di caricare un driver "minifilter" e registrare callback per essere notificato delle operazioni di I/O, come apertura, lettura, scrittura di file, ecc.
Ecco un breve riepilogo delle diverse strutture interne utilizzate dal filter manager:
- Il Filter Manager stabilisce un "frame" (`_FLTP_FRAME`) come sua struttura radice;
- Una struttura "volume" (`_FLT_VOLUME`) viene istanziata per ogni "disco" gestito dal Filter Manager (possono essere partizioni, copie shadow o volumi speciali corrispondenti a named pipe o file system remoti);
- A ciascun driver minifilter registrato corrisponde una struttura "filter" (`_FLT_FILTER`), che descrive varie proprietà come le operazioni supportate;
- Questi minifilter non sono tutti collegati a ciascun volume; una struttura "instance" (`_FLT_INSTANCE`) viene creata per segnare ciascuna delle associazioni
filter<->volume;
- I minifilter registrano funzioni callback che devono essere eseguite prima e/o dopo operazioni specifiche (apertura file, scrittura, lettura, ecc.). Queste callback sono descritte in strutture `_CALLBACK_NODE` e possono essere accesse in diversi modi:
- Un array di tutti i `_CALLBACK_NODE` implementati da un'istanza di un minifilter si trova nella struttura `_FLT_INSTANCE`; l'array è indicizzato dal codice della "funzione principale" IRP, una costante che rappresenta le operazioni gestite dalle callback (`IRP_MJ_CREATE`, `IRP_MJ_READ`, ecc.).
- Inoltre, tutti i `_CALLBACK_NODE` implementati da istanze collegate a un volume specifico sono raggruppati in liste collegate, memorizzate nell'array `_FLT_VOLUME.Callbacks.OperationLists` indicizzato dai codici delle funzioni principali IRP.
Queste diverse strutture vengono esplorate da `EDRSandblast` per rilevare i filtri associati a driver correlati all'EDR, e vengono enumerati i nodi callback contenenti funzioni di monitoraggio. Per disabilitarne l'effetto, i nodi vengono scollegati dalle loro liste, rendendoli temporaneamente invisibili al filter manager.
In questo modo, per un periodo specificato, l'EDR può essere completamente all'oscuro di qualsiasi operazione sui file. Un esempio base potrebbe essere la creazione di un file di dump della memoria di lsass sul disco, che non attiverebbe alcuna analisi da parte dell'EDR e quindi nessuna rilevazione basata sul file stesso.
### Bypass dell'EDR tramite disattivazione del provider ETW Microsoft-Windows-Threat-Intelligence
Il provider `ETW Microsoft-Windows-Threat-Intelligence` registra i dati relativi all'uso di alcune API Windows comunemente utilizzate in modo malevolo. Tra queste, l'API `nt!MiReadWriteVirtualMemory`, chiamata da `nt!NtReadVirtualMemory` (usata per dumpare la memoria di `LSASS`) e monitorata dalla funzione `nt!EtwTiLogReadWriteVm`.
I prodotti EDR possono consumare i log prodotti dal provider `ETW TI` attraverso servizi o processi eseguiti rispettivamente come `SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT` o `PS_PROTECTED_ANTIMALWARE_LIGHT`, e associati a un driver `Early Launch Anti Malware (ELAM)`.
Come pubblicato da [`slaeryan` in un post del blog `CNO Development Labs`](https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider), il provider `ETW TI` può essere completamente disabilitato patchando, nella memoria del kernel, il suo attributo `ProviderEnableInfo` a `0x0`. Si rimanda al succitato ottimo post del blog per maggiori informazioni sulla tecnica.
Analogamente alla rimozione delle callback del kernel, gli offset necessari di `ntoskrnl.exe` (`nt!EtwThreatIntProvRegHandleOffset`, `_ETW_REG_ENTRY`'s `GuidEntry` e `_ETW_GUID_ENTRY`'s `ProviderEnableInfo`) vengono calcolati nel file `NtoskrnlOffsets.csv` per un certo numero di versioni del kernel Windows.
### Bypass dell'EDR tramite bypass dell'hooking in userland
#### Come funziona l'hooking in userland
Per monitorare facilmente le azioni eseguite dai processi, i prodotti EDR spesso implementano un meccanismo chiamato *userland hooking*. Innanzitutto, i prodotti EDR registrano una callback del kernel (solitamente callback di *caricamento immagine* o *creazione processo*, vedi sopra) che consente loro di essere notificati all'avvio di ogni processo.
Quando un processo viene caricato da Windows, e prima che inizi effettivamente, l'EDR è in grado di iniettare una DLL personalizzata nello spazio degli indirizzi del processo, contenente la sua logica di monitoraggio. Durante il caricamento, questa DLL inietta "*hook*" all'inizio di ogni funzione che deve essere monitorata dall'EDR. In fase di esecuzione, quando le funzioni monitorate vengono chiamate dal processo sotto sorveglianza, questi hook reindirizzano il flusso di controllo verso un codice di supervisione presente nella DLL dell'EDR, che consente di ispezionare gli argomenti e i valori di ritorno di queste chiamate.
Molto spesso, le funzioni monitorate sono chiamate di sistema (come `NtReadVirtualMemory`, `NtOpenProcess`, ecc.), le cui implementazioni risiedono in `ntdll.dll`. Intercettare le chiamate alle funzioni `Nt*` consente ai prodotti di essere il più vicini possibile al confine tra userland e kernel-land (pur rimanendo in userland), ma possono essere monitorate anche funzioni di DLL di livello superiore.
Di seguito sono riportati esempi della stessa funzione, prima e dopo essere stata hookata dal prodotto EDR:```assembly
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
No input content provided.```assembly 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
#### Rilevamento degli hook
Gli hook in userland hanno la "debolezza" di trovarsi nella memoria userland, il che significa che sono
direttamente osservabili e modificabili dal processo sotto esame. Per rilevare automaticamente
gli hook nello spazio degli indirizzi del processo, l'idea principale è confrontare le differenze tra
la DLL originale su disco e la libreria residente in memoria, che è stata potenzialmente
modificata da un EDR. Per eseguire questo confronto, EDRSandblast segue i seguenti passaggi:
* L'elenco di tutte le DLL caricate viene enumerato grazie a `InLoadOrderModuleList` situata
nel `PEB` (per evitare di chiamare qualsiasi API che potrebbe essere monitorata e sospetta)
* Per ogni DLL caricata, il suo contenuto su disco viene letto e le sue intestazioni analizzate. La
libreria corrispondente, residente in memoria, viene anch'essa analizzata per identificare sezioni, esportazioni,
ecc.
* I rilocamenti della DLL vengono analizzati e applicati, tenendo conto dell'indirizzo di base della
libreria caricata corrispondente. Ciò consente al contenuto sia della libreria in memoria che
della DLL proveniente dal disco di avere esattamente lo stesso contenuto (nelle sezioni in cui
vengono applicati i rilocamenti), rendendo così il confronto affidabile.
* Le funzioni esportate vengono enumerate e vengono confrontati i primi byte delle versioni "in memoria" e "su disco".
Qualsiasi differenza indica un'alterazione apportata dopo
il caricamento della DLL, e quindi è molto probabilmente un hook di un EDR.
Nota: Il processo può essere generalizzato per trovare differenze ovunque in sezioni non scrivibili
e non solo all'inizio delle funzioni esportate, ad esempio se i prodotti EDR iniziano
ad applicare hook nel mezzo di una funzione :) Quindi non utilizzato dallo strumento, questo è stato
implementato in `findDiffsInNonWritableSections`.
Per bypassare il monitoraggio eseguito da questi hook, sono possibili molteplici tecniche,
ciascuna con vantaggi e svantaggi.
#### Bypass degli hook tramite ... unhooking
Il metodo più intuitivo per bypassare il monitoraggio basato su hook è rimuovere gli
hook. Poiché gli hook sono presenti in memoria accessibile dal processo stesso, per
rimuovere un hook, il processo può semplicemente:
* Modificare i permessi sulla pagina in cui si trova l'hook (RX -> RWX o RW)
* Scrivere i byte originali che sono noti grazie al contenuto della DLL su disco
* Ripristinare i permessi a RX
Questo approccio è abbastanza semplice e può essere utilizzato per rimuovere tutti gli hook rilevati in una
volta sola. Eseguito da uno strumento offensivo all'inizio, consente al resto del codice di
essere completamente ignaro del meccanismo di hooking e di funzionare normalmente senza essere
monitorato.
Tuttavia, presenta due principali svantaggi. L'EDR sta probabilmente monitorando l'uso di
`NtProtectVirtualMemory`, quindi usarlo per modificare i permessi della pagina in cui gli
hook sono stati installati è (almeno concettualmente) una cattiva idea. Inoltre, se un thread
viene eseguito dall'EDR e controlla periodicamente l'integrità degli hook, anche questo potrebbe
innescare qualche rilevamento.
Per i dettagli implementativi, controlla il percorso del codice della funzione `unhook()` quando `unhook_method` è
`UNHOOK_WITH_NTPROTECTVIRTUALMEMORY`.
**Nota importante: per semplicità, questa tecnica è implementata in EDRSandblast come
tecnica di base utilizzata per *mostrare* le altre tecniche di bypass; ciascuna di esse dimostra
come ottenere una versione non monitorata di `NtProtectVirtualMemory`, ma esegue la stessa
operazione successivamente (unhooking di un hook specifico).**
#### Bypass degli hook tramite un trampolino personalizzato
Per bypassare un hook specifico, è possibile semplicemente "saltare oltre" ed eseguire il resto della
funzione così com'è. Innanzitutto, i byte originali della funzione monitorata, che sono stati
sovrascritti dall'EDR per installare l'hook, devono essere recuperati dal file DLL. Nel nostro
precedente esempio di codice, questi sarebbero i byte corrispondenti alle seguenti
istruzioni:```assembly
mov r10, rcx
mov eax, 50h
Identificare questi byte è un compito semplice poiché siamo in grado di eseguire un diff pulito delle versioni in memoria e su disco della libreria, come descritto in precedenza. Quindi, assembliamo un'istruzione di salto costruita per reindirizzare il flusso di controllo al codice immediatamente successivo all'hook, all'indirizzo `NtProtectVirtualMemory + sizeof(overwritten_instructions)````assembly jmp NtProtectVirtualMemory+8
Infine, concateniamo questi opcode, li memorizziamo in memoria (nuovamente) eseguibile e manteniamo un puntatore ad essi. Questo oggetto è chiamato "*trampoline*" e può quindi essere utilizzato come puntatore a funzione, strettamente equivalente alla funzione originale `NtProtectVirtualMemory`.
Il principale vantaggio di questa tecnica, come per tutte le tecniche seguenti, è che l'hook non viene mai cancellato, quindi qualsiasi controllo di integrità eseguito sugli hook dall'EDR dovrebbe passare. Tuttavia, richiede l'allocazione di memoria scrivibile e poi eseguibile, tipica dell'allocazione di shellcode, attirando così l'attenzione dell'EDR.
Per i dettagli implementativi, controlla il percorso del codice della funzione `unhook()` quando `unhook_method` è `UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE`. Ricorda che la tecnica è solo mostrata nella nostra implementazione e, in definitiva, viene utilizzata per **rimuovere** gli hook dalla memoria, come tutte le tecniche seguenti.
#### Hook bypass using the own EDR's trampoline
Il prodotto EDR, affinché il suo hook funzioni, deve salvare da qualche parte in memoria gli opcode che ha rimosso. Peggio (o "meglio", dal punto di vista dell'attaccante*), per utilizzare effettivamente le istruzioni originali, l'EDR probabilmente si è allocato un *trampoline* da qualche parte per eseguire la funzione originale dopo aver intercettato la chiamata.
Questo trampoline può essere cercato e utilizzato come sostituto della funzione hookata, senza la necessità di allocare memoria eseguibile o chiamare alcuna API eccetto `VirtualQuery`, che molto probabilmente non è monitorata essendo una funzione innocua.
Per trovare il trampoline in memoria, esploriamo l'intero spazio degli indirizzi usando `VirtualQuery` cercando memoria committed ed eseguibile. Per ogni regione di memoria di questo tipo, la scansioniamo per cercare un'istruzione di salto che abbia come destinazione l'indirizzo successivo alle istruzioni sovrascritte (`NtProtectVirtualMemory+8` nel nostro esempio precedente). Il trampoline può quindi essere utilizzato per chiamare la funzione hookata senza attivare l'hook.
Questa tecnica funziona sorprendentemente bene, poiché recupera quasi tutti i trampoline sugli EDR testati. Per i dettagli implementativi, controlla il percorso del codice della funzione `unhook()` quando `unhook_method` è `UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE`.
#### Hook bypass using duplicate DLL
Un altro metodo semplice per ottenere l'accesso a una versione non monitorata della funzione `NtProtectVirtualMemory` è caricare una versione duplicata della libreria `ntdll.dll` nello spazio degli indirizzi del processo. Poiché due DLL identiche possono essere caricate nello stesso processo, a patto che abbiano nomi diversi, possiamo semplicemente copiare il file legittimo `ntdll.dll` in un'altra posizione, caricarlo usando `LoadLibrary` (o reimplementare il processo di caricamento), e accedere alla funzione usando `GetProcAddress` per esempio.
Questa tecnica è molto semplice da capire e implementare, e ha una discreta possibilità di successo, poiché la maggior parte dei prodotti EDR non reinstalla gli hook sulle DLL appena caricate una volta che il processo è in esecuzione. Tuttavia, il principale svantaggio è che copiare binari firmati Microsoft con un nome diverso è spesso considerato sospetto dai prodotti EDR stessi.
Questa tecnica è comunque implementata in `EDRSandblast`. Per i dettagli implementativi, controlla il percorso del codice della funzione `unhook()` quando `unhook_method` è `UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY`.
#### Hook bypass using direct syscalls
Per utilizzare funzioni relative alle system call, un programma può reimplementare le syscall (in assembly) per chiamare le corrispondenti funzionalità del sistema operativo senza toccare effettivamente il codice in `ntdll.dll`, che potrebbe essere monitorato dall'EDR. Questo bypassa completamente qualsiasi hook a livello utente effettuato sulle funzioni syscall in `ntdll.dll`.
Questo presenta comunque alcuni svantaggi. Innanzitutto, ciò implica essere in grado di conoscere l'elenco dei numeri di syscall delle funzioni di cui il programma ha bisogno, che cambia per ogni versione di Windows. Questo è comunque mitigato implementando molteplici euristiche che funzionano in tutte le versioni passate di Windows NT (ordinando gli export `Zw*` di `ntdll`, cercando l'istruzione `mov rax, #syscall_number` nella corrispondente funzione `ntdll`, ecc.), e verificando che restituiscano tutte lo stesso risultato (vedi `Syscalls.c` per maggiori dettagli).
Inoltre, funzioni che tecnicamente non sono syscall (ad es. `LoadLibraryX`/`LdrLoadDLL`) potrebbero essere monitorate anch'esse, e non possono essere semplicemente reimplementate utilizzando una syscall.
La tecnica delle syscall dirette è implementata in EDRSandblast. Come precedentemente affermato, viene utilizzata solo per eseguire `NtProtectVirtualMemory` in modo sicuro e rimuovere tutti gli hook rilevati.
Per i dettagli implementativi, controlla il percorso del codice della funzione `unhook()` quando `unhook_method` è `UNHOOK_WITH_DIRECT_SYSCALL`.
### Vulnerable drivers exploitation
Come precedentemente affermato, ogni azione che richiede una lettura o scrittura della memoria del kernel si basa su un driver vulnerabile per fornire questa primitiva. In EDRSanblast, aggiungere il supporto per un nuovo driver che fornisce la primitiva di lettura/scrittura può essere fatto "facilmente", devono essere implementate solo tre funzioni:
* Una funzione `ReadMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)` che copia `Size` byte dall'indirizzo kernel `Address` al buffer userland `Buffer`;
* Una funzione `WriteMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)` che copia `Size` byte dal buffer userland `Buffer` all'indirizzo kernel `Address`;
* Una funzione `CloseDriverHandle_DRIVERNAME()` che garantisce che tutti gli handle al driver siano chiusi (necessaria prima dell'operazione di disinstallazione che è indipendente dal driver, per ora).
Ad esempio, due driver sono attualmente supportati da EDRSandblast, `RTCore64.sys` (SHA256: `01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD`) e `DBUtils_2_3.sys` (SHA256: `0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5`). Il seguente codice in `KernelMemoryPrimitives.h` deve essere aggiornato se il driver vulnerabile utilizzato deve essere cambiato, o se ne viene implementato uno nuovo.```C
#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
Attualmente vengono utilizzate diverse tecniche per determinare se un driver o un processo specifico appartiene a un prodotto EDR o meno.
Innanzitutto, il nome del driver può essere semplicemente utilizzato a questo scopo. Infatti, Microsoft assegna numeri specifici chiamati "Altitude" a tutti i driver che necessitano di inserire callback nel kernel. Ciò consente un ordine deterministico nell'esecuzione dei callback, indipendentemente dall'ordine di registrazione, ma basato esclusivamente sull'uso del driver. Un elenco di (fornitori di) driver che hanno riservato specifiche altitude può essere trovato su MSDN. Di conseguenza, Microsoft offre un elenco quasi completo dei nomi dei driver di sicurezza legati ai prodotti di sicurezza, principalmente negli elenchi "FSFilter Anti-Virus" e "FSFilter Activity Monitor". Questi elenchi di nomi di driver sono incorporati in EDRSandblast, insieme a contributi aggiuntivi.
Inoltre, gli eseguibili e le DLL EDR sono più spesso che no firmati digitalmente utilizzando il certificato di firma del fornitore. Pertanto, controllare il firmatario di un eseguibile o DLL associato a un processo può consentire di identificare rapidamente i prodotti EDR.
Inoltre, i driver devono essere firmati direttamente da Microsoft per essere caricati nello spazio del kernel. Sebbene il fornitore del driver non sia direttamente il firmatario del driver stesso, sembra che il nome del fornitore sia comunque incluso all'interno di un attributo della firma; questa tecnica di rilevamento è comunque ancora da studiare e implementare.
Infine, quando si ha a che fare con un EDR sconosciuto a EDRSandblast, l'approccio migliore è eseguire lo strumento in modalità "audit" e controllare l'elenco dei driver che hanno registrato callback del kernel; quindi il nome del driver può essere aggiunto all'elenco, lo strumento ricompilato e rieseguito.
Il meccanismo di Protezione LSA (Local Security Authority), introdotto per la prima volta in Windows 8.1 e Windows Server 2012 R2, sfrutta la tecnologia Protected Process Light (PPL) per limitare l'accesso al processo LSASS. La protezione PPL regola e limita operazioni come l'iniezione di memoria o il dump della memoria di processi protetti, anche da un processo che possiede il privilegio SeDebugPrivilege. Nel modello di protezione dei processi, solo i processi eseguiti con livelli di protezione superiori possono eseguire operazioni su processi protetti.
La struttura _EPROCESS, utilizzata dal kernel Windows per rappresentare un processo nella memoria del kernel, include un campo _PS_PROTECTION che definisce il livello di protezione di un processo attraverso i suoi attributi Type (_PS_PROTECTED_TYPE) e Signer (_PS_PROTECTED_SIGNER).
Scrivendo nella memoria del kernel, il processo EDRSandblast è in grado di aggiornare il proprio livello di protezione a PsProtectedSignerWinTcb-Light. Questo livello è sufficiente per eseguire il dump della memoria del processo LSASS, poiché "domina" su PsProtectedSignerLsa-Light, il livello di protezione del processo LSASS che viene eseguito con il meccanismo RunAsPPL.
EDRSandBlast implementa l'auto-protezione come segue:
NtQuerySystemInformation per trovare l'handle aperto
sul processo corrente e l'indirizzo della struttura EPROCESS del processo corrente nella memoria del kernel._PS_PROTECTION del processo
corrente nella memoria del kernel. Gli offset del campo _PS_PROTECTION
rispetto alla struttura EPROCESS (definiti dalla versione di ntoskrnl in
uso) vengono calcolati nel file NtoskrnlOffsets.csv.Microsoft Credential Guard è una tecnologia di isolamento basata sulla virtualizzazione,
introdotta in Microsoft Windows 10 (edizione Enterprise) che impedisce
l'accesso diretto alle credenziali memorizzate nel processo LSASS.
Quando Credentials Guard è attivato, viene creato un processo LSAIso (LSA Isolated) in
Virtual Secure Mode, una funzionalità che sfrutta le estensioni di virtualizzazione
della CPU per fornire una maggiore sicurezza dei dati in memoria. L'accesso al
processo LSAIso è limitato anche per un accesso con il
contesto di sicurezza NT AUTHORITY\SYSTEM. Quando elabora un hash, il processo LSA
effettua una chiamata RPC al processo LSAIso e attende il
risultato di LSAIso per continuare. Pertanto, il processo LSASS non conterrà alcun
segreto e, al suo posto, memorizzerà Dati Isolati LSA.
Come affermato nella ricerca originale condotta da N4kedTurtle: "Wdigest può essere
abilitato su un sistema con Credential Guard applicando patch ai valori di
g_fParameter_useLogonCredential e g_IsCredGuardEnabled in memoria".
L'attivazione di Wdigest comporterà la memorizzazione di credenziali in chiaro
nella memoria di LSASS per tutti i nuovi accessi interattivi (senza richiedere un riavvio del
sistema). Fare riferimento al
post originale del blog di ricerca
per maggiori dettagli su questa tecnica.
EDRSandBlast rende semplicemente il PoC originale un po' più opsec-friendly e
fornisce supporto per un certo numero di versioni di wdigest.dll (attraverso offset
calcolati per g_fParameter_useLogonCredential e g_IsCredGuardEnabled).
Per eseguire in modo affidabile le operazioni di bypass del monitoraggio del kernel, EDRSandblast ha bisogno di sapere esattamente dove leggere e scrivere nella memoria del kernel. Questo viene fatto utilizzando gli offset di variabili globali all'interno dell'immagine target (ntoskrnl.exe, wdigest.dll), nonché l'offset di campi specifici in strutture le cui definizioni sono pubblicate da Microsoft nei file dei simboli. Questi offset sono specifici per ogni build delle immagini target e devono essere raccolti almeno una volta per una specifica versione della piattaforma.
La scelta di utilizzare offset "hardcoded" invece di ricerche per pattern per individuare le strutture
e le variabili utilizzate da EDRSandblast è giustificata dal fatto che le API non documentate
responsabili dell'aggiunta/rimozione di callback del kernel sono soggette a modifiche e che qualsiasi tentativo
di leggere o scrivere memoria del kernel all'indirizzo sbagliato può (e spesso lo farà) risultare in un
Bug Check (Blue Screen of Death). Un crash della macchina non è accettabile sia in scenari di
red-teaming che di penetration testing normale, poiché una macchina che crasha
è altamente visibile per i difensori e perderà qualsiasi credenziale ancora in memoria al
momento dell'attacco.
Per recuperare gli offset per ogni versione specifica di Windows, sono implementati due approcci.
Gli offset necessari di ntoskrnl.exe e wdigest.dll possono essere estratti utilizzando lo
script Python ExtractOffsets.py fornito, che si basa su radare2 e r2pipe
per scaricare e analizzare i simboli dai file PDB ed estrarre gli offset necessari da
essi. Gli offset vengono quindi memorizzati in file CSV per un uso successivo da parte di EDRSandblast.
Per supportare fin da subito un'ampia gamma di build di Windows, molte versioni dei
binari ntoskrnl.exe e wdigest.dll sono referenziate da
Winbindex e possono essere scaricate automaticamente
(e i loro offset estratti) da ExtractOffsets.py. Ciò consente di estrarre offset
da quasi tutti i file mai pubblicati nei pacchetti di aggiornamento di Windows (ad oggi 450+
versioni di ntoskrnl.exe e 30+ versioni di wdigest.dll sono disponibili e pre-calcolate).
Un'opzione aggiuntiva è stata implementata in EDRSandBlast per consentire al programma
di scaricare i file .pdb necessari dal Microsoft Symbol Server, estrarre gli
offset richiesti e persino aggiornare i corrispondenti file .csv se presenti.
Usare l'opzione --internet rende l'esecuzione dello strumento molto più semplice, introducendo
tuttavia un rischio OpSec aggiuntivo, poiché un file .pdb viene scaricato e salvato su disco durante
il processo. Ciò è richiesto dalle funzioni dbghelp.dll utilizzate per analizzare il database
dei simboli; tuttavia, un parsing PDB completamente in memoria potrebbe essere implementato in futuro per
rimuovere questo requisito e ridurre l'impronta dello strumento.
EDRSandblast implementa pubblicamente il supporto di almeno 3 driver vulnerabili, gdrv.sys (predefinito),
RTCore64.sys e DBUtil_2_3.sys. Il driver effettivamente utilizzato viene deciso prima della compilazione
dello strumento (vedi #define VULN_DRIVER <nome driver> in includes/KernelMemoryPrimitive.h). Una copia
del driver vulnerabile deve essere scaricata e fornita a EDRSandblast affinché la sua operazione nel kernel
funzioni.
Gli hash dei driver testati sono menzionati all'inizio di ogni file Driver<nome>.c che implementa le
primitive di lettura e scrittura della memoria del kernel utilizzate da EDRSanblast. Utilizzando questi hash, i campioni dei driver possono essere
facilmente trovati su Internet, specialmente su https://www.loldrivers.io.
Ecco l'elenco dei driver vulnerabili supportati insieme ai link per il download:
| Driver supportato | Link per il download | SHA256 |
|---|---|---|
GDRV.sys | Link LOLDrivers | 31f4cfb4c71da44120752721103a16512444c13c2ac2d857a7e6f13cb679b427 |
RTCore64.sys | Link LOLDrivers | 01aa278b07b58dc46c84bd0b1b5c8e9ee4e62ea0bf7a695862444af32e87f1fd |
DBUtil_2_3.sys | Link LOLDrivers | 0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5 |
Usage: EDRSandblast.exe [-h | --help] [-v | --verbose] <audit | dump | cmd | credguard | firewall | load_unsigned_driver> [--usermode] [--unhook-method ] [--direct-syscalls] [--add-dll ]* [--kernelmode] [--dont-unload-driver] [--no-restore] [--nt-offsets <NtoskrnlOffsets.csv>] [--fltmgr-offsets <FltmgrOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [--ci-offsets <CiOffsets.csv>] [--internet] [--vuln-driver <RTCore64.sys>] [--vuln-service <SERVICE_NAME>] [--unsigned-driver <evil.sys>] [--unsigned-service <SERVICE_NAME>] [--no-kdp] [-o | --dump-output <DUMP_FILE>]
### Opzioni```
-h | --help Show this help message and exit.
-v | --verbose Enable a more verbose output.
Actions mode:
audit Display the user-land hooks and / or Kernel callbacks without taking actions.
dump Dump the process specified by --process-name (LSASS process by default), as '<process_name>' in the current directory or at the
specified file using -o | --output <DUMP_FILE>.
cmd Open a cmd.exe prompt.
credguard Patch the LSASS process' memory to enable Wdigest cleartext passwords caching even if
Credential Guard is enabled on the host. No kernel-land actions required.
firewall Add Windows firewall rules to block network access for the EDR processes / services.
load_unsigned_driver Load the specified unsigned driver, bypassing Driver Signature Enforcement (DSE).
WARNING: currently an experimental feature, only works if KDP is not present and enabled.
--usermode Perform user-land operations (DLL unhooking).
--kernelmode Perform kernel-land operations (Kernel callbacks removal and ETW TI disabling).
Hooking-related options:
--add-dll <dll name or path> Loads arbitrary libraries into the process' address space, before starting
anything.This can be useful to audit userland hooking for DLL that are not
loaded by default by this program. Use this option multiple times to load
multiple DLLs all at once.
Example of interesting DLLs to look at: user32.dll, ole32.dll, crypt32.dll,
samcli.dll, winhttp.dll, urlmon.dll, secur32.dll, shell32.dll...
--unhook-method <N> Choose the userland un-hooking technique, from the following:
0 Do not perform any unhooking (used for direct syscalls operations).
1 (Default) Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all
present userland hooks.
2 Constructs a 'unhooked' (i.e. unmonitored) version of NtProtectVirtualMemory, by allocating an executable trampoline jumping over the hook, and remove all present
userland hooks.
3 Searches for an existing trampoline allocated by the EDR itself, to get an 'unhooked'
(i.e. unmonitored) version of NtProtectVirtualMemory, and remove all present userland
hooks.
4 Loads an additional version of ntdll library into memory, and use the (hopefully unmonitored) version of NtProtectVirtualMemory present in this library to remove all
present userland hooks.
5 Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory, and uses it to remove all detected hooks
--direct-syscalls Use direct syscalls to dump the selected process memory without unhooking unserland hooks.
BYOVD options:
--dont-unload-driver Keep the vulnerable driver installed on the host
Default to automatically unsinstall the driver.
--no-restore Do not restore the EDR drivers' Kernel Callbacks that were removed.
Default to restore the callbacks.
--vuln-driver <gdrv.sys> Path to the vulnerable driver file.
Default to 'gdrv.sys' in the current directory.
--vuln-service <SERVICE_NAME> Name of the vulnerable service to intall / start.
Driver sideloading options:
--unsigned-driver <evil.sys> Path to the unsigned driver file.
Default to 'evil.sys' in the current directory.
--unsigned-service <SERVICE_NAME> Name of the unsigned driver's service to intall / start.
--no-kdp Switch to g_CiOptions patching method for disabling DSE (default is callback swapping).
Offset-related options:
--nt-offsets <NtoskrnlOffsets.csv> Path to the CSV file containing the required ntoskrnl.exe's offsets.
Default to 'NtoskrnlOffsets.csv' in the current directory.
--fltmgr-offsets <FltmgrOffsets.csv> Path to the CSV file containing the required fltmgr.sys's offsets
Default to 'FltmgrOffsets.csv' in the current directory.
--wdigest-offsets <WdigestOffsets.csv> Path to the CSV file containing the required wdigest.dll's offsets
(only for the 'credguard' mode).
Default to 'WdigestOffsets.csv' in the current directory.
--ci-offsets <CiOffsets.csv> Path to the CSV file containing the required ci.dll's offsets
(only for the 'load_unsigned_driver' mode).
Default to 'WdigestOffsets.csv' in the current directory.
-i | --internet Enables automatic symbols download from Microsoft Symbol Server
If a corresponding *Offsets.csv file exists, appends the downloaded offsets to the file for later use
OpSec warning: downloads and drops on disk a PDB file for the corresponding image
Dump options:
-o | --dump-output <DUMP_FILE> Output path to the dump file that will be generated by the 'dump' mode.
Default to 'process_name' in the current directory.
--process-name <NAME> File name of the process to dump (defaults to 'lsass.exe')
EDRSandBlast (solo x64) è stato compilato su Visual Studio 2019 (Windows SDK
Versione: 10.0.19041.0 e Plateform Toolset: Visual Studio 2019 (v142)).
Nota che ExtractOffsets.py è stato testato solo su Windows.```
pip.exe install -m .\requirements.txt
ExtractOffsets.py [-h] -i INPUT [-o OUTPUT] [-d] mode
positional arguments: mode ntoskrnl or wdigest. Mode to download and extract offsets for either ntoskrnl or wdigest
optional arguments: -h, --help show this help message and exit -i INPUT, --input INPUT Single file or directory containing ntoskrnl.exe / wdigest.dll to extract offsets from. If in download mode, the PE downloaded from MS symbols servers will be placed in this folder. -o OUTPUT, --output OUTPUT CSV file to write offsets to. If the specified file already exists, only new ntoskrnl versions will be downloaded / analyzed. Defaults to NtoskrnlOffsets.csv / WdigestOffsets.csv in the current folder. -d, --download Flag to download the PE from Microsoft servers using list of versions from winbindex.m417z.com.
## Detection
Dal punto di vista del difensore (vendor EDR, Microsoft, analisti SOC che esaminano la telemetria dell'EDR, ...), molteplici indicatori possono essere utilizzati per rilevare o prevenire questo tipo di tecniche.
### Whitelisting dei driver
Poiché ogni azione eseguita dallo strumento in memoria in modalità kernel si basa su un driver vulnerabile per leggere/scrivere contenuti arbitrari, gli eventi di caricamento dei driver dovrebbero essere attentamente esaminati dal prodotto EDR (o dagli analisti SOC) e generare un avviso per qualsiasi caricamento di driver anomalo, o addirittura bloccare i driver vulnerabili noti. Quest'ultimo approccio è addirittura [raccomandato da Microsoft stessa](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/microsoft-recommended-driver-block-rules): qualsiasi dispositivo Windows con HVCI (*Integrità del codice protetta da hypervisor*) abilitato include una blocklist di driver, e questo diventerà progressivamente un comportamento predefinito su Windows (lo è già su Windows 11).
### Controlli di integrità della memoria kernel
Poiché un attaccante potrebbe comunque utilizzare un driver vulnerabile sconosciuto per eseguire le stesse azioni in memoria, il driver EDR potrebbe verificare periodicamente che i suoi callback del kernel siano ancora registrati, ispezionando direttamente la memoria del kernel (come fa questo strumento), o semplicemente attivando eventi (creazione di processi, creazione di thread, caricamento di immagini, ecc.) e controllando che le funzioni callback siano effettivamente chiamate dal kernel esecutivo.
Come nota a margine, questo tipo di struttura dati potrebbe essere protetto tramite il recente meccanismo [Kernel Data Protection (KDP)](https://www.microsoft.com/security/blog/2020/07/08/introducing-kernel-data-protection-a-new-platform-security-technology-for-preventing-data-corruption/), che si basa su Virtual Based Security, al fine di rendere l'array dei callback del kernel non scrivibile senza chiamare le API corrette.
La stessa logica potrebbe applicarsi a variabili ETW sensibili come `ProviderEnableInfo`, abusata da questo strumento per disabilitare la generazione di eventi ETW Threat Intelligence.
### Rilevamento in modalità utente
Il primo indicatore che un processo sta attivamente cercando di eludere l'hooking in spazio utente è l'accesso ai file per ogni DLL corrispondente ai moduli caricati; in un'esecuzione normale, un processo in spazio utente raramente ha bisogno di leggere file DLL al di fuori di una chiamata `LoadLibrary`, specialmente `ntdll.dll`.
Per proteggere l'hooking delle API dall'essere aggirato, i prodotti EDR potrebbero verificare periodicamente che gli hook non vengano alterati in memoria, all'interno di ogni processo monitorato.
Infine, per rilevare l'elusione dell'hooking (abuso di un trampolino, uso di syscall dirette, ecc.) che non implica la rimozione degli hook, i prodotti EDR potrebbero potenzialmente fare affidamento sui callback del kernel associati alle syscall abusate (es. `PsCreateProcessNotifyRoutine` per la syscall `NtCreateProcess`, `ObRegisterCallbacks` per la syscall `NtOpenProcess`, ecc.) ed eseguire un'analisi dello stack di chiamate in modalità utente per determinare se la syscall è stata attivata da un percorso normale (`kernel32.dll` -> `ntdll.dll` -> syscall) o da uno anomalo (es. `program.exe` -> syscall diretta).
## Riconoscimenti
- Enumerazione e rimozione dei callback del kernel:
https://github.com/br-sn/CheekyBlinder
- Primitive di lettura/scrittura della memoria kernel tramite il driver vulnerabile
`Micro-Star MSI Afterburner`:
https://github.com/Barakat/CVE-2019-16098/
- Disabilitazione del provider ETW Threat Intelligence:
https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider
- Installazione/disinstallazione del driver: https://github.com/gentilkiwi/mimikatz
- Elenco iniziale dei nomi dei driver EDR:
https://github.com/SadProcessor/SomeStuff/blob/master/Invoke-EDRCheck.ps1
- Bypass di Credential Guard riabilitando `Wdigest` attraverso la memoria `LSASS`
patching: https://teamhydra.blog/2020/08/25/bypassing-credential-guard/
## Autori
[Thomas DIOT (Qazeer)](https://github.com/Qazeer/)
[Maxime MEIGNAN (themaks)](https://github.com/themaks)
## Ringraziamenti ai contributori
- [v1k1ngfr](https://github.com/v1k1ngfr): per il bypass del Driver Signature Enforcement (tramite patching di `g_CiOptions`) e il supporto del driver GDRV.sys
- [Windy Bug](https://github.com/0mWindyBug): per un bypass del Driver Signature Enforcement compatibile con KDP (tramite *callback swapping*) e il loro importante contributo sulla funzionalità di bypass del minifilter
## Licenza
Licenza CC BY 4.0 - https://creativecommons.org/licenses/by/4.0/