
Nutzt anfällige signierte Treiber aus, um EDR-Kernel-Callbacks, Objekt-Callbacks, ETW-TI-Provider und Userland-Hooks zu umgehen, für LSASS-Speicherauszug und Anmeldedatenextraktion.
EDRSandBlast ist ein in C geschriebenes Tool, das einen verwundbaren signierten Treiber ausnutzt, um EDR-Erkennungen (Notify Routine Callbacks, Object Callbacks und den ETW TI-Provider) sowie LSASS-Schutzmechanismen zu umgehen. Es werden mehrere Userland-Unhooking-Techniken implementiert, um die Überwachung im Benutzermodus zu umgehen.
Zum Zeitpunkt der Veröffentlichung wurde eine Kombination aus Userland- (--usermode) und Kernel-Land- (--kernelmode) Techniken verwendet, um den LSASS-Speicher unter EDR-Überwachung auszulesen, ohne blockiert zu werden oder Ereignisse im Zusammenhang mit "OS Credential Dumping" in der (Cloud-)Konsole des Produkts zu erzeugen. Die Tests wurden mit 3 verschiedenen EDR-Produkten durchgeführt und waren in jedem Fall erfolgreich.
EDR-Produkte nutzen Kernel-"Notify Routines"-Callbacks unter Windows, um vom Kernel über Systemaktivitäten wie Prozess- und Thread-Erstellung sowie das Laden von Images (exe / DLL) benachrichtigt zu werden.
Diese Kernel-Callbacks werden aus dem Kernel-Land heraus definiert, normalerweise vom Treiber, der die Callbacks implementiert, unter Verwendung einer Reihe dokumentierter APIs (nt!PsSetCreateProcessNotifyRoutine, nt!PsSetCreateThreadNotifyRoutine, usw.). Diese APIs fügen treiberbereitgestellte Callback-Routinen in undokumentierte Arrays von Routinen im Kernel-Space ein:
PspCreateProcessNotifyRoutine für die ProzesserstellungPspCreateThreadNotifyRoutine für die Thread-ErstellungPspLoadImageNotifyRoutine für das Laden von ImagesEDRSandBlast enumeriert die in diesen Arrays definierten Routinen und entfernt alle Callback-Routinen, die mit einer vordefinierten Liste von EDR-Treibern verknüpft sind (mehr als 1000 Treiber von Sicherheitsprodukten werden unterstützt, siehe den Abschnitt zur EDR-Treibererkennung). Die Enumeration und Entfernung wird durch die Ausnutzung einer primitiven Kernel-Speicher-Lese-/Schreibfähigkeit ermöglicht, die durch die Ausnutzung eines verwundbaren Treibers bereitgestellt wird (siehe Abschnitt zu verwundbaren Treibern).
Die Offsets der genannten Arrays werden mit verschiedenen Techniken wiederhergestellt, siehe Abschnitt zu Offsets.
EDR- (und sogar EPP-) Produkte registrieren oft "Object-Callbacks" über die Verwendung der Kernel-API nt!ObRegisterCallbacks. Diese Callbacks erlauben es dem Sicherheitsprodukt, bei jeder Handle-Erstellung für bestimmte Objekttypen (Prozesse, Threads und Desktop-bezogene Objekt-Callbacks werden von Windows unterstützt) benachrichtigt zu werden. Eine Handle-Erstellung kann beim Öffnen eines Objekts (Aufruf von OpenProcess, OpenThread usw.) sowie bei der Handle-Duplizierung (Aufruf von DuplicateHandle usw.) auftreten.
Indem das Sicherheitsprodukt vom Kernel bei jeder dieser Operationen benachrichtigt wird, kann es die Legitimität der Handle-Erstellung analysieren (z. B. versucht ein unbekannter Prozess, LSASS zu öffnen) und diese sogar blockieren, falls eine Bedrohung erkannt wird.
Bei jeder Callback-Registrierung mit ObRegisterCallbacks wird ein neues Element zur doppelt verketteten Liste CallbackList hinzugefügt, die im _OBJECT_TYPE-Objekt vorhanden ist, das den vom Callback betroffenen Objekttyp beschreibt (entweder ein Prozess, ein Thread oder ein Desktop). Leider werden diese Elemente durch eine Struktur beschrieben, die weder dokumentiert noch in Symbol-Dateien von Microsoft veröffentlicht ist. Die Untersuchung verschiedener ntoskrnl.exe-Versionen deutet jedoch darauf hin, dass sich die Struktur zwischen (mindestens) den Windows 10 Builds 10240 und 22000 (von 2015 bis 2022) nicht geändert hat.
Die genannte Struktur, die eine Objekt-Callback-Registrierung darstellt, ist die folgende:```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;
Die `OB_CALLBACK`-Struktur, die oben erwähnt wurde, ist ebenfalls undokumentiert und wird wie folgt definiert:```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;
Um EDR-registrierte Objekt-Callbacks zu deaktivieren, werden in
EDRSandblast drei Techniken implementiert;
allerdings ist derzeit nur eine aktiviert.
Enabled-Felds von OB_CALLBACK_ENTRYDies ist die Standardtechnik, die in EDRSandblast aktiviert ist. Um EDR-bezogene
Objekt-Callbacks zu erkennen und zu deaktivieren, wird die CallbackList-Liste in den
_OBJECT_TYPE-Objekten, die an die Process- und Thread-Typen gebunden sind, durchsucht.
Beide _OBJECT_TYPEs werden von öffentlichen globalen Symbolen im Kernel referenziert,
PsProcessType und PsThreadType.
Jedes Element der Liste wird als zur oben beschriebenen OB_CALLBACK_ENTRY-Struktur passend
angenommen (eine Annahme, die zum Zeitpunkt der Erstellung dieses Textes zumindest in allen
Windows 10-Builds zu gelten scheint). Funktionen, die in den PreOperation- und PostOperation-Feldern
definiert sind, werden lokalisiert, um zu prüfen, ob sie zu einem EDR-Treiber gehören,
und falls ja, werden Callbacks einfach durch Umschalten des Enabled-Flags deaktiviert.
Obwohl es eine ziemlich sichere Technik ist, hat sie den Nachteil, auf einer undokumentierten Struktur zu beruhen; um das Risiko einer unsicheren Manipulation dieser Struktur zu verringern, werden grundlegende Prüfungen durchgeführt, um zu validieren, dass einige Felder die erwarteten Werte haben:
Enabled ist entweder TRUE oder FALSE (nicht lachen, ein BOOL ist ein int, also könnte es alles andere als 1 oder 0 sein);Operations ist OB_OPERATION_HANDLE_CREATE, OB_OPERATION_HANDLE_DUPLICATE oder beides;ObjectType zeigt auf PsProcessType oder PsThreadType.CallbackList von Threads und ProzessenEine weitere Strategie, die nicht auf einer undokumentierten Struktur beruht (und daher theoretisch
robuster gegenüber NT-Kernel-Änderungen ist), ist das Entfernen der gesamten CallbackList
sowohl für Prozesse als auch für Threads. Das _OBJECT_TYPE-Objekt ist wie folgt:```C
struct _OBJECT_TYPE {
LIST_ENTRY TypeList;
UNICODE_STRING Name;
[...]
_OBJECT_TYPE_INITIALIZER TypeInfo;
[...]
LIST_ENTRY CallbackList;
}
Making the `Flink` and `Blink` pointers of the `CallbackList` `LIST_ENTRY` point to
the `LIST_ENTRY` itself effectively make the list empty. Since the `_OBJECT_TYPE` structure
is published in the kernel' symbols, the technique does not rely on hardcoded offsets/structures.
However, it has some drawbacks.
The first being not able to only disable callbacks from EDR; indeed, the technique affects
all object callbacks that could have been registered by "legitimate" software. It should
nevertheless be noted that object callbacks are not used by any pre-installed component
on Windows 10 (at the time of writing) so disabling them should not affect the machine
stability (even more so if the disabling is only temporary).
The second drawback is that process or thread handle operation are really frequent (nearly
continuous) in the normal functioning of the OS. As such, if the kernel write primitive used
cannot perform a `QWORD` write "atomically", there is a good chance that the
`_OBJECT_TYPE.CallbackList.Flink` pointer will be accessed by the kernel in the middle
of its overwriting. For instance, the MSI vulnerable driver `RTCore64.sys` can only perform
a `DWORD` write at a time, so 2 distinct IOCTLs will be needed to overwrite the pointer, between
which the kernel has a high probability of using it (resulting in a crash). On the other hand,
the vulnerable DELL driver `DBUtil_2_3.sys` can perform writes of arbitrary sizes in one
IOCTL, so using this method with it does not risk causing a crash.
#### Disabling object callbacks altogether
One last technique we found was to disable entirely the object callbacks support for thread
and processes. Inside the `_OBJECT_TYPE` structure corresponding to the process and
thread types resides a `TypeInfo` field, following the documented `_OBJECT_TYPE_INITIALIZER`
structure. The latter contains a `ObjectTypeFlags` bit field, whose `SupportsObjectCallbacks`
flag determines if the described object type (Process, Thread, Desktop, Token, File, etc.)
supports object callback registering or not. As previously stated, only Process, Thread and
Desktop object types supports these callbacks on a Windows installation at the time of writing.
Since the `SupportsObjectCallbacks` bit is checked by `ObpCreateHandle` or
`ObDuplicateObject` before even reading the `CallbackList` (and before executing
callbacks, of course), flipping the bit at kernel runtime effectively disable all object callbacks
execution.
The main drawback of the method is simply that *KPP* ("*PatchGuard*") monitors the integrity
of some (all ?) `_OBJECT_TYPE` structures, and triggers a [`0x109 Bug Check`](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/bug-check-0x109---critical-structure-corruption)
with parameter 4 being equal to `0x8`, meaning an object type structure has been altered.
However, performing the disabling / re-enabling (and "malicious" action in-between) quickly
enough should be enough to "race" *PatchGuard* (unless you are unlucky and a periodic
check is performed just at the wrong moment).
### EDR bypass through minifilters' callbacks unlinking
The Windows Filter Manager system allows an EDR to load a "minifilter" driver and
register callbacks in order to be notified of I/O operations, such as file opening,
reading, writing, etc.
Here is a quick sum-up of different internal structures used by the filter manager:
- The Filter Manager establishes a "frame" (`_FLTP_FRAME`) as its root structure;
- A "volume" structure (`_FLT_VOLUME`) is instanciated for each "disk" managed by the
Filter Manager (can be partitions, shadow copies, or special ones corresponding to
named pipes or remote file systems);
- To each registered minifilter driver corresponds a "filter" structure (`_FLT_FILTER`),
describing various properties such as its supported operations;
- These minifilters are not all attached to each volume; an "instance" (`_FLT_INSTANCE`)
structure is created to mark each of the
filter<->volume associations;
- Minifilters register callback functions that are to be executed before and/or after
specific operations (file open, write, read, etc.). These callbacks are described in
`_CALLBACK_NODE` structures, and can be accessed by different ways:
- An array of all `_CALLBACK_NODE`s implemented by an instance of a minifilter
can be found in the `_FLT_INSTANCE` structure; the array is indexed by the IRP
"major function" code, a constant representing the operations handled by the
callbacks (`IRP_MJ_CREATE`, `IRP_MJ_READ`, etc.).
- Also, all `_CALLBACK_NODE`s implemented by instances linked to a specific volume
are regrouped in linked lists, stored in the `_FLT_VOLUME.Callbacks.OperationLists`
array indexed by IRP major function codes.
These different structures are browsed by `EDRSandblast` to detect filters that are
associated with EDR-related drivers, and the callback nodes containing monitoring
functions are enumerated. To disable their effect, the nodes are unlinked from their
lists, making them temporarily invisible from the filter manager.
This way, during a specified period, the EDR can be completely unaware of any file
operations. A basic example would be the creation of an lsass memory dump file on disk,
that would not trigger any analysis from the EDR, and thus no detection based on the
file itself.
### EDR bypass through deactivation of the ETW Microsoft-Windows-Threat-Intelligence provider
The `ETW Microsoft-Windows-Threat-Intelligence` provider logs data about the
usages of some Windows API commonly used maliciously. This include the
`nt!MiReadWriteVirtualMemory` API, called by `nt!NtReadVirtualMemory` (which is
used to dump `LSASS` memory) and monitored by the `nt!EtwTiLogReadWriteVm`
function.
EDR products can consume the logs produced by the `ETW TI` provider through
services or processes running as, respectively,
`SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT` or
`PS_PROTECTED_ANTIMALWARE_LIGHT`, and associated with an `Early Launch Anti
Malware (ELAM)` driver.
As published by
[`slaeryan` in a `CNO Development Labs` blog post](https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider),
the `ETW TI` provider can be disabled altogether by patching, in kernel memory,
its `ProviderEnableInfo` attribute to `0x0`. Refer to the great aforementioned
blog post for more information on the technique.
Similarly to the Kernel callbacks removal, the necessary `ntoskrnl.exe` offsets
(`nt!EtwThreatIntProvRegHandleOffset`, `_ETW_REG_ENTRY`'s `GuidEntry`, and
`_ETW_GUID_ENTRY`'s `ProviderEnableInfo`) are computed in the
`NtoskrnlOffsets.csv` file for a number of the Windows Kernel versions.
### EDR bypass through userland hooking bypass
#### How userland hooking works
In order to easily monitor actions that are performed by processes, EDR products often
deploy a mechanism called *userland hooking*. First, EDR products register a kernel
callback (usually *image loading* or *process creation* callbacks, see above) that allows
them to be notified upon each process start.
When a process is loaded by Windows, and before it actually starts, the EDR is able to
inject some custom DLL into the process address space, which contains its monitoring
logic. While loading, this DLL injects "*hooks*" at the start of every function that is to
be monitored by the EDR. At runtime, when the monitored functions are called by the
process under surveillance, these hooks redirect the control flow to some supervision code
present in the EDR's DLL, which allows it to inspect arguments and return values of these
calls.
Most of the time, monitored functions are system calls (such as `NtReadVirtualMemory`,
`NtOpenProcess`, etc.), whose implementations reside in `ntdll.dll`. Intercepting calls to
`Nt*` functions allows products to be as close as possible to the userland / kernel-land
boundary (while remaining in userland), but functions from some higher-level DLLs may also
be monitored as well.
Bellow are examples of the same function, before and after beeing hooked by the EDR product:```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
(empty)```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
#### Hooks-Erkennung
Userland-Hooks haben die "Schwäche", dass sie sich im Userland-Speicher befinden, was bedeutet, dass sie
direkt beobachtbar und durch den überwachten Prozess modifizierbar sind. Um automatisch Hooks
im Prozessadressraum zu erkennen, besteht die Hauptidee darin, die Unterschiede zwischen
der ursprünglichen DLL auf der Festplatte und der Bibliothek im Speicher, die möglicherweise von
einem EDR verändert wurde, zu vergleichen. Um diesen Vergleich durchzuführen, werden von
EDRSandblast die folgenden Schritte befolgt:
* Die Liste aller geladenen DLLs wird mit Hilfe der `InLoadOrderModuleList` im
`PEB` aufgelistet (um die Verwendung von APIs zu vermeiden, die überwacht und verdächtig sein könnten).
* Für jede geladene DLL wird ihr Inhalt auf der Festplatte gelesen und ihre Header analysiert. Die
entsprechende Bibliothek im Speicher wird ebenfalls analysiert, um Sektionen, Exporte,
usw. zu identifizieren.
* Die Relokationen der DLL werden analysiert und unter Berücksichtigung der Basisadresse der
entsprechenden geladenen Bibliothek angewendet. Dadurch wird der Inhalt sowohl der In-Memory-
Bibliothek als auch der DLL von der Festplatte identisch (in Sektionen, wo Relokationen
angewendet werden), was den Vergleich zuverlässig macht.
* Exportierte Funktionen werden aufgelistet und die ersten Bytes der "In-Memory"- und "On-Disk"-
Versionen werden verglichen. Jeder Unterschied deutet auf eine Änderung hin, die nach dem Laden
der DLL vorgenommen wurde, und ist daher sehr wahrscheinlich ein EDR-Hook.
Hinweis: Der Prozess kann verallgemeinert werden, um Unterschiede überall in nicht beschreibbaren
Sektionen zu finden, und nicht nur am Anfang exportierter Funktionen, falls EDR-Produkte z. B.
beginnen, Hooks in der Mitte einer Funktion anzuwenden :) Daher wird dies vom Tool nicht
verwendet, wurde aber in `findDiffsInNonWritableSections` implementiert.
Um die Überwachung durch diese Hooks zu umgehen, sind mehrere Techniken
möglich, jede mit Vor- und Nachteilen.
#### Hook-Bypass mittels ... Unhooking
Die intuitivste Methode, um die Hook-basierte Überwachung zu umgehen, ist die Entfernung der
Hooks. Da sich die Hooks im Speicher befinden, der für den Prozess selbst erreichbar ist, kann
der Prozess zum Entfernen eines Hooks einfach:
* Die Berechtigungen auf der Seite, auf der sich der Hook befindet, ändern (RX -> RWX oder RW)
* Die ursprünglichen Bytes schreiben, die dank des DLL-Inhalts auf der Festplatte bekannt sind
* Die Berechtigungen wieder auf RX ändern
Dieser Ansatz ist recht einfach und kann verwendet werden, um alle erkannten Hooks auf einmal
zu entfernen. Wenn er von einem Offensivtool zu Beginn ausgeführt wird, kann der restliche Code
den Hook-Mechanismus völlig ignorieren und normal funktionieren, ohne überwacht zu werden.
Er hat jedoch zwei Hauptnachteile. Das EDR überwacht wahrscheinlich die Verwendung von
`NtProtectVirtualMemory`, daher ist es (zumindest konzeptionell) eine schlechte Idee, es zu verwenden,
um die Berechtigungen der Seite zu ändern, auf der die Hooks installiert wurden. Auch wenn ein
Thread vom EDR ausgeführt wird und regelmäßig die Integrität der Hooks überprüft, könnte dies
ebenfalls eine Erkennung auslösen.
Für Implementierungsdetails siehe den Codepfad der `unhook()`-Funktion, wenn `unhook_method`
`UNHOOK_WITH_NTPROTECTVIRTUALMEMORY` ist.
**Wichtiger Hinweis: Der Einfachheit halber ist diese Technik in EDRSandblast als Basistechnik
implementiert, die verwendet wird, um die anderen Bypass-Techniken zu *demonstrieren*; jede von ihnen
zeigt, wie man eine unüberwachte Version von `NtProtectVirtualMemory` erhält, führt aber danach
die gleiche Operation durch (Entfernen eines bestimmten Hooks).**
#### Hook-Bypass mittels eines benutzerdefinierten Trampolins
Um einen bestimmten Hook zu umgehen, ist es möglich, einfach "darüber zu springen" und den Rest der
Funktion wie gewohnt auszuführen. Zuerst müssen die ursprünglichen Bytes der überwachten Funktion,
die vom EDR überschrieben wurden, um den Hook zu installieren, aus der DLL-Datei wiederhergestellt
werden. In unserem vorherigen Codebeispiel wären dies die Bytes, die den folgenden
Anweisungen entsprechen:```assembly
mov r10, rcx
mov eax, 50h
Das Identifizieren dieser Bytes ist eine einfache Aufgabe, da wir in der Lage sind, einen sauberen Diff der Speicher- und Datenträgerversion der Bibliothek durchzuführen, wie zuvor beschrieben. Dann assemblieren wir eine Sprunganweisung, die den Kontrollfluss auf den Code unmittelbar nach dem Hook umleitet, an der Adresse `NtProtectVirtualMemory + sizeof(overwritten_instructions)````assembly jmp NtProtectVirtualMemory+8
Schließlich verketten wir diese Opcodes, speichern sie in neu ausführbarem Speicher und behalten einen Zeiger darauf. Dieses Objekt wird als "*trampoline*" bezeichnet und kann dann als Funktionszeiger verwendet werden, der strikt äquivalent zur ursprünglichen `NtProtectVirtualMemory`-Funktion ist.
Der Hauptvorteil dieser Technik, wie auch bei allen folgenden Techniken, ist, dass der Hook nie gelöscht wird, sodass alle Integritätsprüfungen, die vom EDR an den Hooks durchgeführt werden, bestanden werden sollten. Allerdings erfordert es die Zuweisung von zunächst beschreibbarem, dann ausführbarem Speicher, was typisch für eine Shellcode-Zuweisung ist und somit die Aufmerksamkeit des EDR auf sich zieht.
Für Implementierungsdetails überprüfen Sie den Codepfad der `unhook()`-Funktion, wenn `unhook_method` ist `UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE`. Bitte beachten Sie, dass die Technik nur in unserer Implementierung demonstriert wird und letztendlich dazu dient, Hooks aus dem Speicher zu **entfernen**, wie alle folgenden Techniken.
#### Hook-Umgehung unter Verwendung des eigenen EDR-Trampolins
Das EDR-Produkt muss, damit sein Hook funktioniert, irgendwo im Speicher die Opcodes speichern, die es entfernt hat. Schlimmstenfalls (*oder 'besser' aus Sicht des Angreifers*), um die ursprünglichen Anweisungen effektiv nutzen zu können, hat das EDR sich wahrscheinlich irgendwo ein *trampoline* zugewiesen, um die ursprüngliche Funktion auszuführen, nachdem es den Aufruf abgefangen hat.
Dieses Trampolin kann gesucht und als Ersatz für die gehookedte Funktion verwendet werden, ohne dass ausführbarer Speicher zugewiesen oder eine andere API außer `VirtualQuery` aufgerufen werden muss, die höchstwahrscheinlich nicht überwacht wird, da sie eine harmlose Funktion ist.
Um das Trampolin im Speicher zu finden, durchsuchen wir den gesamten Adressraum mit `VirtualQuery` nach committiertem und ausführbarem Speicher. Für jede solche Speicherregion scannen wir sie nach einer Sprunganweisung, die auf die Adresse nach den überschriebenen Anweisungen abzielt (in unserem vorherigen Beispiel `NtProtectVirtualMemory+8`). Das Trampolin kann dann verwendet werden, um die gehookedte Funktion aufzurufen, ohne den Hook auszulösen.
Diese Technik funktioniert überraschend gut, da sie fast alle Trampoline auf getesteten EDRs wiederherstellt. Für Implementierungsdetails überprüfen Sie den Codepfad der `unhook()`-Funktion, wenn `unhook_method` ist `UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE`.
#### Hook-Umgehung unter Verwendung einer doppelten DLL
Eine weitere einfache Methode, um Zugriff auf eine unüberwachte Version der `NtProtectVirtualMemory`-Funktion zu erhalten, besteht darin, eine doppelte Version der `ntdll.dll`-Bibliothek in den Prozessadressraum zu laden. Da zwei identische DLLs in denselben Prozess geladen werden können, sofern sie unterschiedliche Namen haben, können wir einfach die legitime `ntdll.dll`-Datei an einen anderen Ort kopieren, sie mit `LoadLibrary` laden (oder den Ladevorgang neu implementieren) und die Funktion beispielsweise mit `GetProcAddress` aufrufen.
Diese Technik ist sehr einfach zu verstehen und zu implementieren und hat eine gute Erfolgschance, da die meisten EDR-Produkte nach dem Start des Prozesses keine Hooks auf neu geladene DLLs setzen. Der größte Nachteil ist jedoch, dass das Kopieren von Microsoft-signierten Binärdateien unter einem anderen Namen oft von EDR-Produkten selbst als verdächtig angesehen wird.
Diese Technik ist dennoch in `EDRSandblast` implementiert. Für Implementierungsdetails überprüfen Sie den Codepfad der `unhook()`-Funktion, wenn `unhook_method` ist `UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY`.
#### Hook-Umgehung mittels direkter Syscalls
Um systemaufrufbezogene Funktionen zu nutzen, kann ein Programm Syscalls (in Assembler) neu implementieren, um die entsprechenden Betriebssystemfunktionen aufzurufen, ohne tatsächlich den Code in `ntdll.dll` zu berühren, der möglicherweise vom EDR überwacht wird. Dies umgeht vollständig jegliches Userland-Hooking, das an Syscall-Funktionen in `ntdll.dll` durchgeführt wird.
Dies hat dennoch einige Nachteile. Erstens erfordert dies, die Liste der Syscall-Nummern der benötigten Funktionen zu kennen, die sich mit jeder Windows-Version ändert. Dies wird jedoch dadurch gemildert, dass mehrere Heuristiken implementiert werden, die in allen bisherigen Versionen von Windows NT funktionieren (Sortieren der `Zw*`-Exports von `ntdll`, Suchen nach der `mov rax, #syscall_number`-Anweisung in der zugehörigen `ntdll`-Funktion usw.) und überprüft wird, dass alle das gleiche Ergebnis liefern (siehe `Syscalls.c` für weitere Details).
Auch Funktionen, die technisch gesehen keine Syscalls sind (z.B. `LoadLibraryX`/`LdrLoadDLL`), könnten ebenfalls überwacht werden und können nicht einfach mit einem Syscall neu implementiert werden.
Die direkte Syscalls-Technik ist in EDRSandblast implementiert. Wie bereits erwähnt, wird sie nur verwendet, um `NtProtectVirtualMemory` sicher auszuführen und alle erkannten Hooks zu entfernen.
Für Implementierungsdetails überprüfen Sie den Codepfad der `unhook()`-Funktion, wenn `unhook_method` ist `UNHOOK_WITH_DIRECT_SYSCALL`.
### Ausnutzung anfälliger Treiber
Wie bereits erwähnt, beruht jede Aktion, die einen Kernel-Speicher-Lese- oder Schreibzugriff erfordert, auf einem anfälligen Treiber, um diese Primitive bereitzustellen. In EDRSanblast kann die Unterstützung für einen neuen Treiber, der die Lese-/Schreib-Primitive bereitstellt, 'einfach' hinzugefügt werden, es müssen nur drei Funktionen implementiert werden:
* Eine `ReadMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)`-Funktion, die `Size` Bytes von der Kernel-Adresse `Address` in den Userland-Puffer `Buffer` kopiert;
* Eine `WriteMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)`-Funktion, die `Size` Bytes vom Userland-Puffer `Buffer` in die Kernel-Adresse `Address` kopiert;
* Eine `CloseDriverHandle_DRIVERNAME()`-Funktion, die sicherstellt, dass alle Handles zum Treiber geschlossen werden (vor dem Deinstallationsvorgang erforderlich, der derzeit treiberunabhängig ist).
Als Beispiel werden derzeit zwei Treiber von EDRSandblast unterstützt, `RTCore64.sys` (SHA256: `01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD`) und `DBUtils_2_3.sys` (SHA256: `0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5`). Der folgende Code in `KernelMemoryPrimitives.h` ist zu aktualisieren, wenn der verwendete anfällige Treiber geändert werden muss oder ein neuer implementiert wird.```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
Mehrere Techniken werden derzeit verwendet, um festzustellen, ob ein bestimmter Treiber oder Prozess zu einem EDR-Produkt gehört oder nicht.
Zunächst kann der Name des Treibers einfach zu diesem Zweck verwendet werden. In der Tat weist Microsoft bestimmte Nummern, sogenannte "Altitudes", für alle Treiber zu, die Callbacks im Kernel einfügen müssen. Dies ermöglicht eine deterministische Reihenfolge der Callback-Ausführung, unabhängig von der Registrierungsreihenfolge, sondern nur basierend auf der Treibernutzung. Eine Liste der (Hersteller von) Treibern, die bestimmte Altitudes reserviert haben, findet sich auf MSDN. Folglich wird von Microsoft eine nahezu umfassende Liste von Sicherheitstreibernamen, die mit Sicherheits- produkten verbunden sind, bereitgestellt, hauptsächlich in den Listen "FSFilter Anti-Virus" und "FSFilter Activity Monitor". Diese Listen von Treibernamen sind in EDRSandblast eingebettet, ebenso wie zusätzliche Beiträge.
Darüber hinaus sind EDR-Executables und -DLLs mehr als oft digital mit dem Signierungszertifikat des Herstellers signiert. Daher kann die Überprüfung des Signierers einer mit einem Prozess verbundenen ausführbaren Datei oder DLL schnell die Identifizierung von EDR-Produkten ermöglichen.
Auch müssen Treiber direkt von Microsoft signiert werden, um im Kernelraum geladen werden zu dürfen. Während der Hersteller des Treibers nicht direkt der Signierer des Treibers selbst ist, scheint es, dass der Name des Herstellers dennoch in einem Attribut der Signatur enthalten ist; diese Erkennungstechnik muss jedoch noch untersucht und implementiert werden.
Schließlich, wenn man auf ein EDR stößt, das EDRSandblast unbekannt ist, ist der beste Ansatz, das Tool im "Audit"-Modus auszuführen und die Liste der Treiber zu überprüfen, die Kernel-Callbacks registriert haben; dann kann der Treibername zur Liste hinzugefügt, das Tool neu kompiliert und erneut ausgeführt werden.
Der Local Security Authority (LSA) Protection-Mechanismus, der erstmals
in Windows 8.1 und Windows Server 2012 R2 eingeführt wurde, nutzt die Protected Process Light (PPL)-Technologie, um den Zugriff auf den LSASS-Prozess einzuschränken. Der PPL-Schutz
reguliert und beschränkt Operationen wie Speicherinjektion oder
Speicher-Dumping von geschützten Prozessen, selbst von einem Prozess, der das
SeDebugPrivilege-Privileg besitzt. Unter dem Prozessschutzmodell können nur
Prozesse mit höheren Schutzstufen Operationen an geschützten Prozessen durchführen.
Die _EPROCESS-Struktur, die vom Windows-Kernel verwendet wird, um einen Prozess
im Kernelspeicher darzustellen, enthält ein _PS_PROTECTION-Feld, das die Schutzstufe
eines Prozesses über seine Type- (_PS_PROTECTED_TYPE) und Signer-Attribute (_PS_PROTECTED_SIGNER)
definiert.
Durch Schreiben in den Kernelspeicher kann der EDRSandblast-Prozess seine eigene
Schutzstufe auf PsProtectedSignerWinTcb-Light erhöhen. Diese Stufe ist ausreichend, um
den Speicher des LSASS-Prozesses zu dumpen, da sie PsProtectedSignerLsa-Light "dominiert",
die Schutzstufe des LSASS-Prozesses, der mit dem RunAsPPL-Mechanismus läuft.
EDRSandBlast implementiert den Selbstschutz wie folgt:
NtQuerySystemInformation, um das geöffnete
Handle auf den aktuellen Prozess und die Adresse der EPROCESS-Struktur des aktuellen Prozesses
im Kernelspeicher zu finden._PS_PROTECTION-Feld des aktuellen
Prozesses im Kernelspeicher zu überschreiben. Die Offsets des _PS_PROTECTION-Feldes
relativ zur EPROCESS-Struktur (definiert durch die verwendete ntoskrnl-Version)
werden in der Datei NtoskrnlOffsets.csv berechnet.Microsoft Credential Guard ist eine virtualisierungsbasierte Isolationstechnologie,
eingeführt in Microsofts Windows 10 (Enterprise Edition), die den direkten
Zugriff auf die im LSASS-Prozess gespeicherten Anmeldeinformationen verhindert.
Wenn Credential Guard aktiviert ist, wird ein LSAIso-Prozess (LSA Isolated) im
Virtual Secure Mode erstellt, einer Funktion, die die Virtualisierungserweiterungen
der CPU nutzt, um zusätzliche Sicherheit der Daten im Speicher zu bieten. Der Zugriff auf
den LSAIso-Prozess ist selbst für einen Zugriff mit dem
Sicherheitskontext NT AUTHORITY\SYSTEM eingeschränkt. Bei der Verarbeitung eines Hashs führt der
LSA-Prozess einen RPC-Aufruf an den LSAIso-Prozess durch und wartet auf das
Ergebnis des LSAIso, um fortzufahren. Somit enthält der LSASS-Prozess keine
Geheimnisse, sondern speichert stattdessen LSA Isolated Data.
Wie in der ursprünglichen Forschung von N4kedTurtle ausgeführt: "Wdigest kann
auf einem System mit Credential Guard aktiviert werden, indem die Werte von
g_fParameter_useLogonCredential und g_IsCredGuardEnabled im Speicher gepatcht werden".
Die Aktivierung von Wdigest führt dazu, dass Klartext-Anmeldeinformationen bei neuen
interaktiven Anmeldungen im LSASS-Speicher gespeichert werden (ohne einen Neustart des
Systems zu erfordern). Weitere Einzelheiten zu dieser Technik finden Sie im
ursprünglichen Forschungsblogbeitrag.
EDRSandBlast macht den ursprünglichen PoC lediglich ein wenig opsec-freundlicher und
bietet Unterstützung für eine Reihe von wdigest.dll-Versionen (durch berechnete
Offsets für g_fParameter_useLogonCredential und g_IsCredGuardEnabled).
Um Kernel-Überwachungsumgehungen zuverlässig durchführen zu können, muss EDRSandblast genau wissen, wo im Kernelspeicher gelesen und geschrieben werden muss. Dies geschieht mithilfe von Offsets globaler Variablen innerhalb des Zielimages (ntoskrnl.exe, wdigest.dll) sowie Offsets bestimmter Felder in Strukturen, deren Definitionen von Microsoft in Symbol-Dateien veröffentlicht werden. Diese Offsets sind für jeden Build der Zielimages spezifisch und müssen mindestens einmal für eine bestimmte Plattformversion gesammelt werden.
Die Wahl, "hartcodierte" Offsets anstelle von Mustersuchen zu verwenden, um die Strukturen
und Variablen zu lokalisieren, die von EDRSandblast verwendet werden, ist dadurch gerechtfertigt, dass die undokumentierten APIs,
die für das Hinzufügen/Entfernen von Kernel-Callbacks verantwortlich sind, Änderungen unterliegen können und dass jeder
Versuch, Kernelspeicher an der falschen Adresse zu lesen oder zu schreiben, zu einem
Bug Check (Blue Screen of Death) führen kann (und oft wird). Ein Maschinenabsturz ist sowohl in
Red-Teaming- als auch in normalen Penetrationstest-Szenarien inakzeptabel, da eine abstürzende Maschine
für Verteidiger sehr sichtbar ist und alle Anmeldeinformationen verloren gehen, die zum Zeitpunkt des
Angriffs noch im Speicher waren.
Um Offsets für jede spezifische Version von Windows zu ermitteln, werden zwei Ansätze implementiert.
Die erforderlichen Offsets von ntoskrnl.exe und wdigest.dll können mit dem
bereitgestellten Python-Skript ExtractOffsets.py extrahiert werden, das auf radare2 und r2pipe
zurückgreift, um Symbole aus PDB-Dateien herunterzuladen und zu parsen und die benötigten Offsets daraus
zu extrahieren. Offsets werden dann in CSV-Dateien für die spätere Verwendung durch EDRSandblast gespeichert.
Um eine breite Palette von Windows-Builds sofort zu unterstützen, werden viele Versionen der
Binarys ntoskrnl.exe und wdigest.dll von
Winbindex referenziert und können automatisch heruntergeladen
(und ihre Offsets extrahiert) werden vom ExtractOffsets.py-Skript. Dies ermöglicht die Extraktion von Offsets
aus nahezu allen Dateien, die jemals in Windows-Update-Paketen veröffentlicht wurden (derzeit 450+
ntoskrnl.exe- und 30+ wdigest.dll-Versionen verfügbar und vorberechnet).
Eine zusätzliche Option wurde in EDRSandBlast implementiert, um dem Programm zu ermöglichen,
die benötigten .pdb-Dateien selbst vom Microsoft Symbol Server herunterzuladen, die
erforderlichen Offsets zu extrahieren und sogar die entsprechenden .csv-Dateien zu aktualisieren, falls vorhanden.
Die Verwendung der --internet-Option macht die Ausführung des Tools viel einfacher, birgt jedoch
ein zusätzliches OpSec-Risiko, da während des
Prozesses eine .pdb-Datei heruntergeladen und auf die Festplatte gelegt wird. Dies ist erforderlich für die
von dbghelp.dll verwendeten Funktionen zur Analyse der Symbol-Datenbank; eine vollständige In-Memory-PDB-Analyse könnte jedoch in Zukunft implementiert werden, um
diese Anforderung zu umgehen und den Fußabdruck des Tools zu verringern.
EDRSandblast unterstützt öffentlich mindestens 3 verwundbare Treiber: gdrv.sys (Standard),
RTCore64.sys und DBUtil_2_3.sys. Der tatsächlich verwendete Treiber wird vor der Kompilierung
des Tools festgelegt (siehe #define VULN_DRIVER <Treibername> in includes/KernelMemoryPrimitive.h). Eine Kopie
des verwundbaren Treibers sollte heruntergeladen und EDRSandblast für seine Kernel-Operationen
bereitgestellt werden.
Die Hashes der getesteten Treiber werden am Anfang jeder Datei Driver<name>.c erwähnt, die die
Kernelspeicher-Lese- und -Schreib-Primitiven implementiert, die von EDRSandblast verwendet werden. Mit diesen Hashes können Beispiele für Treiber
leicht im Internet gefunden werden, insbesondere auf https://www.loldrivers.io.
Hier ist die Liste der unterstützten verwundbaren Treiber zusammen mit Download-Links:
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>]
### Optionen```
-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 (nur x64) wurde mit Visual Studio 2019 erstellt (Windows SDK
Version: 10.0.19041.0 und Plateform Toolset: Visual Studio 2019 (v142)).
Beachten Sie, dass ExtractOffsets.py nur unter Windows getestet wurde.```
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.
## Erkennung
Aus Sicht des Verteidigers (EDR-Anbieter, Microsoft, SOC-Analysten, die die Telemetrie des EDR untersuchen, usw.) können mehrere Indikatoren verwendet werden, um diese Art von Techniken zu erkennen oder zu verhindern.
### Treiber-Whitelisting
Da jede Aktion, die das Tool im Kernel-Mode-Speicher ausführt, auf einem verwundbaren Treiber basiert, um beliebige Inhalte zu lesen/schreiben, sollten Treiberladeereignisse von EDR-Produkten (oder SOC-Analysten) stark überprüft werden und bei jedem ungewöhnlichen Treiberladen einen Alarm auslösen oder sogar bekannte verwundbare Treiber blockieren. Letzterer Ansatz wird sogar [von Microsoft selbst empfohlen](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/microsoft-recommended-driver-block-rules): Jedes Windows-Gerät mit aktiviertem HVCI (*Hypervisor-geschützte Codeintegrität*) enthält eine Treiber-Blockliste, und dies wird schrittweise zum Standardverhalten unter Windows werden (unter Windows 11 ist es bereits der Fall).
### Kernel-Speicher-Integritätsprüfungen
Da ein Angreifer immer noch einen unbekannten verwundbaren Treiber verwenden könnte, um dieselben Aktionen im Speicher auszuführen, könnte der EDR-Treiber regelmäßig überprüfen, ob seine Kernel-Callbacks noch registriert sind, entweder direkt durch Inspektion des Kernel-Speichers (wie dieses Tool) oder einfach durch Auslösen von Ereignissen (Prozesserstellung, Threaderstellung, Image-Laden usw.) und Prüfen, ob die Callback-Funktionen tatsächlich vom Executive-Kernel aufgerufen werden.
Als Randbemerkung könnte diese Art von Datenstruktur durch den kürzlich eingeführten [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/)-Mechanismus geschützt werden, der auf Virtual Based Security basiert, um das Kernel-Callbacks-Array ohne Aufruf der richtigen APIs nicht beschreibbar zu machen.
Die gleiche Logik könnte auf sensible ETW-Variablen wie `ProviderEnableInfo` angewendet werden, die von diesem Tool missbraucht werden, um die ETW-Threat-Intelligence-Ereignisgenerierung zu deaktivieren.
### Erkennung im Benutzermodus
Der erste Indikator, dass ein Prozess aktiv versucht, User-Land-Hooking zu umgehen, sind die Dateizugriffe auf jede DLL, die geladenen Modulen entspricht; bei normaler Ausführung muss ein Userland-Prozess selten DLL-Dateien außerhalb eines `LoadLibrary`-Aufrufs lesen, insbesondere `ntdll.dll`.
Um zu verhindern, dass API-Hooking umgangen wird, könnten EDR-Produkte regelmäßig überprüfen, ob Hooks im Speicher innerhalb jedes überwachten Prozesses nicht verändert wurden.
Schließlich könnten EDR-Produkte zur Erkennung von Hooking-Umgehungen (Missbrauch eines Trampolins, Verwendung direkter Syscalls usw.), die kein Entfernen der Hooks erfordern, möglicherweise auf Kernel-Callbacks zurückgreifen, die mit den missbrauchten Syscalls verbunden sind (z. B. `PsCreateProcessNotifyRoutine` für den `NtCreateProcess`-Syscall, `ObRegisterCallbacks` für den `NtOpenProcess`-Syscall usw.), und eine User-Mode-Call-Stack-Analyse durchführen, um festzustellen, ob der Syscall von einem normalen Pfad (`kernel32.dll` -> `ntdll.dll` -> Syscall) oder einem abnormalen (z. B. `program.exe` -> direkter Syscall) ausgelöst wurde.
## Danksagungen
- Kernel-Callbacks-Enumeration und -Entfernung:
https://github.com/br-sn/CheekyBlinder
- Kernel-Speicher-Lese-/Schreib-Primitiven durch den verwundbaren
`Micro-Star MSI Afterburner`-Treiber:
https://github.com/Barakat/CVE-2019-16098/
- Deaktivierung des ETW-Threat-Intelligence-Anbieters:
https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider
- Treiber installieren/deinstallieren: https://github.com/gentilkiwi/mimikatz
- Anfängliche Liste der EDR-Treibernamen:
https://github.com/SadProcessor/SomeStuff/blob/master/Invoke-EDRCheck.ps1
- Credential-Guard-Umgehung durch erneutes Aktivieren von `Wdigest` über `LSASS`-Speicher-
Patchen: https://teamhydra.blog/2020/08/25/bypassing-credential-guard/
## Autoren
[Thomas DIOT (Qazeer)](https://github.com/Qazeer/)
[Maxime MEIGNAN (themaks)](https://github.com/themaks)
## Danke an die Mitwirkenden
- [v1k1ngfr](https://github.com/v1k1ngfr): für die Umgehung der Treibersignaturerzwingung (via `g_CiOptions`-Patchen) und die Unterstützung des GDRV.sys-Treibers
- [Windy Bug](https://github.com/0mWindyBug): für eine KDP-kompatible Umgehung der Treibersignaturerzwingung (via *Callback-Swapping*) und ihren großen Beitrag zur Minifilter-Umgehungsfunktion
## Lizenz
CC BY 4.0 Lizenz - https://creativecommons.org/licenses/by/4.0/
| Unterstützter Treiber | Download-Link | SHA256 |
|---|
GDRV.sys | LOLDrivers-Link | 31f4cfb4c71da44120752721103a16512444c13c2ac2d857a7e6f13cb679b427 |
RTCore64.sys | LOLDrivers-Link | 01aa278b07b58dc46c84bd0b1b5c8e9ee4e62ea0bf7a695862444af32e87f1fd |
DBUtil_2_3.sys | LOLDrivers-Link | 0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5 |