Angriffe auf ETW-Anbieter mittels direkter Kernelobjektmanipulation (DKOM).

Seitenansicht eines Mannes, der auf den Computerbildschirm schaut, während er spät in der Nacht arbeitet

Autor

Ruben Boonen

CNE Capability Lead, Adversary Services

IBM X-Force

In diesem Beitrag analysieren die Offensive-Hacker von IBM Security X-Force Red, wie Angreifer mit erhöhten Berechtigungen ihren Zugriff nutzen können, um Post-Ausbeutungs-Funktionen des Windows-Kernels zu nutzen. In den letzten Jahren haben öffentliche Berichte zunehmend gezeigt, dass auch weniger erfahrene Angreifer diese Technik einsetzen, um ihre Ziele zu erreichen. Es ist daher von Bedeutung, dass wir diese Fähigkeit in den Fokus rücken und mehr über ihre potenziellen Auswirkungen erfahren. Insbesondere werden wir in diesem Beitrag untersuchen, wie Kernel-Post-Ausbeutung dazu genutzt werden kann, ETW-Sensoren zu beeinträchtigen, und dies mit Malware-Beispielen in Verbindung bringen, die im letzten Jahr in freier Wildbahn identifiziert wurden.

Einführung

Im Laufe der Zeit haben sich die Sicherheitsvorkehrungen und die Telemetrie zur Erkennung von Sicherheitslücken unter Windows erheblich verbessert. In Kombination mit gut konfigurierten EDR-Lösungen (Endpoint Detection & Response) können diese Fähigkeiten eine nicht zu vernachlässigende Hürde für die Nachbearbeitung von Sicherheitslücken darstellen. Angreifer müssen ständig Taktiken, Techniken und Verfahren (TTPs) entwickeln und iterieren, um Erkennungsheuristiken zu vermeiden. Im Adversary Simulation Team bei IBM Security X-Force stehen wir vor dem gleichen Problem. Unser Team hat die Aufgabe, fortschrittliche Bedrohungsfunktionen in einigen der größten und robustesten Umgebungen zu simulieren. Die Kombination aus komplexen, fein abgestimmten Sicherheitslösungen und gut ausgebildeten Teams im Security Operations Center (SOC) kann für das Handwerk eine große Belastung darstellen. In einigen Fällen wird die Verwendung einer bestimmten TTP innerhalb von drei bis vier Monaten vollständig überflüssig (in der Regel in Verbindung mit bestimmten Technologie-Stacks).

Angreifer können sich dafür entscheiden, die Codeausführung im Windows-Kernel zu nutzen, um einige dieser Schutzmaßnahmen zu manipulieren oder eine Reihe von Sensoren auf Benutzerebene vollständig zu umgehen. Die erste veröffentlichte Demonstration einer solchen Funktion erfolgte 1999 im Phrack Magazine. In den vergangenen Jahren gab es eine Reihe gemeldeter Fälle, in denen Bedrohungsakteure Kernel-Rootkits für die Post-Exploitation verwendet haben. Einige ältere Beispiele sind die Derusbi-Familie und das Lamberts Toolkit.

Traditionell waren diese Arten von Funktionen meist auf fortgeschrittene TAs beschränkt. In den letzten Jahren haben wir jedoch gesehen, dass immer mehr Commodity-Angreifer Bring Your Own Vulnerable Driver (BYOVD) Ausbeutung-Primitive nutzen, um Aktionen am Endpunkt zu erleichtern. In einigen Fällen waren diese Techniken recht primitiv und auf einfache Aufgaben beschränkt, es gab jedoch auch leistungsfähigere Demonstrationen.

Ende September 2022 veröffentlichten Forscher von ESET ein Whitepaper über eine solche Kernel-Funktion, die von der Lazarus TA bei einer Reihe von Angriffen auf Unternehmen in Belgien und den Niederlanden zum Zweck der Datenexfiltration eingesetzt wurde. Dieses Dokument beschreibt eine Reihe von DKOM-Primitiven (Direct Kernel Object Manipulation), die die Nutzlast verwendet, um die Telemetrie von Betriebssystemen, Antivirenprogrammen und EDR-Lösungen zu verschleiern. Die öffentlich zugänglichen Forschungsergebnisse zu diesen Techniken sind sehr begrenzt. Ein umfassenderes Verständnis der Kernel-Post-Exploitation-Techniken ist für die Verteidigung von entscheidender Bedeutung. Ein häufig vorgebrachtes, klassisches und naives Argument lautet, dass ein Angreifer mit erhöhten Berechtigungen alles tun kann. Warum sollten wir also in diesem Szenario Fähigkeiten modellieren? Das ist eine schwache Argumentation. Verteidiger müssen verstehen, über welche Fähigkeiten ein Angreifer verfügt, wenn er sich erhöht hat, welche Datenquellen zuverlässig bleiben (und welche nicht), welche Eindämmungsoptionen es gibt und wie fortschrittliche Techniken erkannt werden können (auch wenn die Fähigkeiten zur Durchführung dieser Erkennungen nicht vorhanden sind). In diesem Beitrag konzentriere ich mich speziell auf das Patchen von Strukturen der Kernel-Ereignisverfolgung für Windows (ETW), um Anbieter entweder unwirksam oder nicht funktionsfähig zu machen. Ich werde einige Hintergrundinformationen zu dieser Technik bereitstellen, analysieren, wie ein Angreifer Kernel-ETW-Strukturen manipulieren kann, und auf einige der Mechanismen zum Auffinden dieser Strukturen eingehen. Abschließend werde ich erläutern, wie diese Technik von Lazarus in ihrer Nutzlast implementiert wurde.

Die neuesten Tech-News – von Experten bestätigt

Bleiben Sie mit dem Think-Newsletter über die wichtigsten – und faszinierendsten – Branchentrends in den Bereichen KI, Automatisierung, Daten und mehr auf dem Laufenden. Weitere Informationen finden Sie in der IBM Datenschutzerklärung.

Vielen Dank! Sie haben sich angemeldet.

Ihr Abonnement wird auf Englisch geliefert. In jedem Newsletter finden Sie einen Abmeldelink. Hier können Sie Ihre Abonnements verwalten oder sich abmelden. Weitere Informationen finden Sie in unserer IBM Datenschutzerklärung.

ETW DKOM

ETW ist eine in das Windows-Betriebssystem integrierte Hochgeschwindigkeits-Tracing-Funktion. Es ermöglicht das Protokollieren von Ereignissen und Systemaktivitäten durch Anwendungen, Treiber und das Betriebssystem und bietet detaillierte Einblicke in das Systemverhalten für Debugging, Leistungsanalysen und Sicherheitsdiagnosen.

In diesem Abschnitt werde ich einen allgemeinen Überblick über Kernel ETW und die zugehörige Angriffsfläche geben. Dies wird dazu beitragen, ein besseres Verständnis der Mechanismen bei der Manipulation von ETW-Anbietern und der damit verbundenen Auswirkungen dieser Manipulationen zu erlangen.

Angriffsfläche des Kernel ETW

Forscher von Binarly hielten auf der BHEU 2021 einen Vortrag, in dem sie die allgemeine Angriffsfläche von ETW unter Windows erörterten. Eine Übersicht des Bedrohungsmodells ist unten abgebildet.

Flussdiagramm zur Darstellung der Bedrohungsmodellierung ETW
Abbildung 1 – Veni, No Vidi, No Vici: Angriffe auf ETW-Blind-EDR-Sensoren (binär)

In diesem Beitrag konzentrieren wir uns auf die Angriffsfläche im Kernel-Bereich.

ein Diagramm zur Darstellung und Beschreibung von Angriffen auf ETW-Anbieter im Kernelmodus
Abbildung 2 – Veni, No Vidi, No Vici: Angriffe auf ETW-Blind-EDR-Sensoren (Binär)

Dieser Beitrag befasst sich ausschließlich mit Angriffen der ersten Angriffskategorie aus „Abbildung 2“, bei denen die Ablaufverfolgung entweder deaktiviert oder in irgendeiner Weise verändert wird.

Bitte beachten Sie, dass bei der Betrachtung undurchsichtiger Strukturen unter Windows stets zu berücksichtigen ist, dass diese Änderungen unterliegen und sich tatsächlich häufig zwischen verschiedenen Windows-Versionen ändern. Dies ist besonders wichtig, wenn Sie Kernel-Daten überschreiben, da Fehler wahrscheinlich zu einem Blue Screen of Death (BSoD) führen können. Seien Sie vorsichtig!

Initialisierung

Kernel-Provider werden mit nt!EtwRegister registriert, einer Funktion, die von ntoskrnl exportiert wird. Eine dekompilierte Version der Funktion ist unten zu sehen.

Screenshot des Codes für die Dekompilierung von nt!EtwRegister
Abbildung 3 – nt!EtwRegister-Dekompilierung

Die vollständige Initialisierung erfolgt innerhalb der inneren Funktion EtwpRegisterKMProvider, aber es gibt zwei wichtige Erkenntnisse:

  • Die ProviderId ist ein Zeiger auf einen 16-Byte-GUID. Dieser GUID ist betriebssystemübergreifend statisch und kann daher zur Identifizierung des Providers verwendet werden, der gerade initialisiert wird.
  • Das RegHandle ist eine Speicheradresse, die bei einem erfolgreichen Aufruf einen Zeiger auf eine _ETW_REG_ENTRY-Struktur empfängt. Diese Datenstruktur und einige ihrer verschachtelten Eigenschaften bieten Möglichkeiten, den ETW-Provider gemäß den Untersuchungen von Binarly zu manipulieren.

Lassen Sie uns kurz die Strukturen auflisten, die Binarly auf der Folie in Abbildung 2 hervorgehoben hat.

ETW_REG_ENTRY

Eine vollständige 64-Bit-Auflistung der _ETW_REG_ENTRY-Struktur ist unten dargestellt. Weitere Einzelheiten finden Sie hier im Blog von Geoff Chappell. Diese Struktur kann auch im Rahmen des Vergilius Project weiter untersucht werden.

// 0x70 Bytes (sizeof)
// Win11 22H2 10.0.22621.382
struct _ETW_REG_ENTRY
{
    struct _LIST_ENTRY RegList;                           //0x0
    struct _LIST_ENTRY GroupRegList;                      //0x10
    struct _ETW_GUID_ENTRY* GuidEntry;                    //0x20
    struct _ETW_GUID_ENTRY* GroupEntry;                   //0x28
    union
    {
        struct _ETW_REPLY_QUEUE* ReplyQueue;              //0x30
        struct _ETW_QUEUE_ENTRY* ReplySlot[4];            //0x30
        struct
        {
            VOID* Caller;                                 //0x30
            ULONG SessionId;                              //0x38
        };
    };
    union
    {
        struct _EPROCESS* Process;                        //0x50
        VOID* CallbackContext;                            //0x50
    };
    VOID* Callback;                                       //0x58
    USHORT Index;                                         //0x60
    union
    {
        USHORT Flags;                                     //0x62
        struct
        {
            USHORT DbgKernelRegistration:1;               //0x62
            USHORT DbgUserRegistration:1;                 //0x62
            USHORT DbgReplyRegistration:1;                //0x62
            USHORT DbgClassicRegistration:1;              //0x62
            USHORT DbgSessionSpaceRegistration:1;         //0x62
            USHORT DbgModernRegistration:1;               //0x62
            USHORT DbgClosed:1;                           //0x62
            USHORT DbgInserted:1;                         //0x62
            USHORT DbgWow64:1;                            //0x62
            USHORT DbgUseDescriptorType:1;                //0x62
            USHORT DbgDropProviderTraits:1;               //0x62
        };
    };
    UCHAR EnableMask;                                     //0x64
    UCHAR GroupEnableMask;                                //0x65
    UCHAR HostEnableMask;                                 //0x66
    UCHAR HostGroupEnableMask;                            //0x67
    struct _ETW_PROVIDER_TRAITS* Traits;                  //0x68
};

ETW_GUID_ENTRY

Einer der eingebetteten Einträge innerhalb _ETW_REG_ENTRY ist GuidEntry, eine _ETW_GUID_ENTRY Struktur. Weitere Informationen zu dieser undokumentierten Struktur finden Sie im Blog von Geoff Chappell hier und im Vergilius Project.

// 0x1a8 bytes (sizeof)
// Win11 22H2 10.0.22621.382
struct _ETW_GUID_ENTRY
{
    struct _LIST_ENTRY GuidList;                          //0x0
    struct _LIST_ENTRY SiloGuidList;                      //0x10
    volatile LONGLONG RefCount;                           //0x20
    struct _GUID Guid;                                    //0x28
    struct _LIST_ENTRY RegListHead;                       //0x38
    VOID* SecurityDescriptor;                             //0x48
    union
    {
        struct _ETW_LAST_ENABLE_INFO LastEnable;          //0x50
        ULONGLONG MatchId;                                //0x50
    };
    struct _TRACE_ENABLE_INFO ProviderEnableInfo;         //0x60
    struct _TRACE_ENABLE_INFO EnableInfo[8];              //0x80
    struct _ETW_FILTER_HEADER* FilterData;                //0x180
    struct _ETW_SILODRIVERSTATE* SiloState;               //0x188
    struct _ETW_GUID_ENTRY* HostEntry;                    //0x190
    struct _EX_PUSH_LOCK Lock;                            //0x198
    struct _ETHREAD* LockOwner;                           //0x1a0
};

TRACE_ENABLE_INFO

Schließlich ist einer der eingebetteten Einträge innerhalb von _ETW_GUID_ENTRY ProviderEnableInfo, eine Struktur vom Typ _TRACE_ENABLE_INFO. Weitere Informationen zu den Elementen dieser Datenstruktur finden Sie in der offiziellen Dokumentation von Microsoft und im Vergilius Project. Die Einstellungen in dieser Struktur beeinflussen direkt den Betrieb und die Funktionen des Providers.

// 0x20 Bytes (sizeof)
// Win11 22H2 10.0.22621.382
Struct _TRACE_ENABLE_INFO
{
    ULONG IsEnabled;                                       //0x0
    UCHAR Level;                                           //0x4
    UCHAR Reserved1;                                       //0x5
    USHORT LoggerId;                                       //0x6
    ULONG EnableProperty;                                  //0x8
    ULONG Reserved2;                                       //0xc
    ULONGLONG MatchAnyKeyword;                             //0x10
    ULONGLONG MatchAllKeyword;                             //0x18
};

Nutzung des Registrierungshandles verstehen

Auch wenn ein theoretischer Hintergrund gut ist, ist es immer am besten, konkrete Beispiele zu betrachten, um ein umfassendes Verständnis für ein Thema zu gewinnen. Sehen wir uns ein kurzes Beispiel an. Die meisten entscheidenden Kernel-ETW-Provider werden innerhalb von nt!EtwpInitialize initialisiert, welches nicht exportiert wird. Ein Blick in diese Funktion offenbart etwa fünfzehn Provider.

Screenshot des Codes für die partielle Dekompilierung von nt!EtwpInitialize
Abbildung 4 – Partielle Dekompilierung von nt!EtwpInitialize

Nehmen wir den Eintrag Microsoft-Windows-Threat-Intelligence (EtwTi) als Beispiel, können wir den globalen Parameter ThreatIntProviderGuid überprüfen, um den GUID für diesen Provider wiederherzustellen.

Screenshot des Codes für den GUID des EtwTi-Providers
Abbildung 5 – EtwTi-Provider-GUID

Eine Online-Suche nach diesem GUID zeigt sofort, dass wir den richtigen Wert wiederherstellen konnten (f4e1897c-bb5d-5668-f1d8-040f4d8dd344).

Sehen wir uns ein Beispiel an, in dem der Registrierungshandle-Parameter EtwThreatIntProvRegHandle verwendet wird, und analysieren wir, wie er verwendet wird. Eine Stelle, an der auf das Handle verwiesen wird, ist nt!EtwTiLogDriverObjectUnLoad. Aus dem Namen dieser Funktion lässt sich erahnen, dass sie dazu dient, Ereignisse zu generieren, wenn ein Treiberobjekt vom Kernel entladen wird.

Screenshot des Codes für die Dekompilierung von nt!EtwTiLogDriverUnload
Abbildung 6 – Dekompilierung von nt!EtwTiLogDriverUnload

Die Funktionen nt!EtwEventEnabled und nt!EtwProviderEnabled werden hier beide aufgerufen, wobei das Registrierungshandle als eines der Argumente übergeben wird. Sehen wir uns eine dieser Unterfunktionen genauer an, um besser zu verstehen, was hier vor sich geht.

Screenshot des Codes für die Dekompilierung von nt!EtwProviderEnable
Abbildung 7 – Dekompilierung von nt!EtwProviderEnable

Zugegebenermaßen ist das etwas schwer zu verstehen. Die Zeigerarithmetik ist jedoch nicht von besonderer Bedeutung. Konzentrieren wir uns stattdessen darauf, wie diese Funktion das Registrierungshandle verarbeitet. Es scheint, dass die Funktion eine Reihe von Eigenschaften der Struktur _ETW_REG_ENTRY und ihrer Unterstrukturen wie beispielsweise die Eigenschaft GuidEntry validiert.

struct _ETW_REG_ENTRY
{
    …
    struct _ETW_GUID_ENTRY* GuidEntry;                    //0x20
    …
}

Und die Eigenschaft GuidEntry->ProviderEnableInfo .

struct _ETW_GUID_ENTRY
{
    …
    struct _TRACE_ENABLE_INFO ProviderEnableInfo;         //0x60
    …
}

Anschließend durchläuft die Funktion ähnliche, ebenenbasierte Prüfungen. Schließlich gibt die Funktion true oder false zurück, um anzuzeigen, ob ein Provider für die Ereignisprotokollierung auf einer bestimmten Ebene und mit einem bestimmten Schlüsselwort aktiviert ist. Weitere Einzelheiten finden Sie in der offiziellen Dokumentation von Microsoft.

Wir können sehen, dass die Integrität dieser Strukturen für den Betrieb des Anbieters von großer Bedeutung ist, wenn auf einen Anbieter über dessen Registrierungshandle zugegriffen wird. Umgekehrt könnte ein Angreifer, wenn er in der Lage wäre, diese Strukturen zu manipulieren, den Kontrollfluss des Aufrufers beeinflussen, um Ereignisse aus der Aufzeichnung zu entfernen oder zu eliminieren.

Angriff auf Registrierungshandles

Wenn wir uns die von Binarly angegebene Angriffsfläche ansehen und uns auf unsere Lichtanalyse stützen, können wir einige Strategien vorschlagen, um die Ereigniserfassung zu stören.

  • Ein Angreifer kann den Zeiger _ETW_REG_ENTRY auf NULL setzen. Alle Funktionen, die auf das Registrierungshandle verweisen, würden dann davon ausgehen, dass der Provider nicht initialisiert wurde.
  • Ein Angreifer kann den Zeiger _ETW_REG_ENTRY->GuidEntry->ProviderEnableInfo auf NULL setzen. Dies sollte die Erfassungsfunktionen des Providers effektiv deaktivieren, da ProviderEnableInfo ein Zeiger auf eine Struktur _TRACE_ENABLE_INFO ist, die beschreibt, wie der Provider funktionieren soll.
  • Ein Angreifer kann Eigenschaften der  _ETW_REG_ENTRY->GuidEntry->ProviderEnableInfo Datenstruktur überschreiben, um die Konfiguration des Providers zu manipulieren.
    • IsEnabled: Setzen Sie diesen Wert auf 1, um den Empfang von Ereignissen vom Provider zu aktivieren oder um die Einstellungen für den Empfang von Ereignissen vom Provider anzupassen. Setzen Sie den Wert auf 0, um den Empfang von Ereignissen vom Provider zu deaktivieren.
    • Level: Ein Wert, der das maximale Level der Ereignisse angibt, die der Anbieter schreiben soll. Der Anbieter schreibt in der Regel ein Ereignis, wenn die Stufe des Ereignisses kleiner oder gleich diesem Wert ist und zusätzlich die Kriterien MatchAnyKeyword und MatchAllKeyword erfüllt.
    • MatchAnyKeyword: 64-Bit-Bitmaske von Schlüsselwörtern, die die Kategorien der Ereignisse bestimmen, die der Provider schreiben soll. Der Provider schreibt in der Regel ein Ereignis, wenn die Schlüsselwortbits des Ereignisses mit einem der in diesem Wert festgelegten Bits übereinstimmen oder wenn für das Ereignis keine Schlüsselwortbits festgelegt sind und es zusätzlich die Kriterien Level und MatchAllKeyword erfüllt.
    • MatchAllKeyword: 64-Bit-Bitmaske von Schlüsselwörtern, die die Ereignisse einschränkt, die der Anbieter schreiben soll. Der Anbieter schreibt in der Regel ein Ereignis, wenn die Schlüsselwortbits des Ereignisses mit allen in diesem Wert festgelegten Bits übereinstimmen oder wenn für das Ereignis keine Schlüsselwortbits festgelegt sind und zusätzlich die Kriterien „Level“ und „MatchAnyKeyword“ erfüllt sind.

Kernel-Suche, Tradecraft

Wir haben jetzt eine gute Vorstellung davon, wie ein DKOM-Angriff auf ETW aussehen könnte. Nehmen wir an, der Angreifer verfügt über eine Schwachstelle, die ihm eine Lese-/Schreibberechtigung für den Kernel gewährt, so wie es die Malware Lazarus in diesem Fall durch das Laden eines anfälligen Treibers tut. Was fehlt, ist eine Möglichkeit, diese Registrierungshandles zu finden.

Ich werde zwei Haupttechniken skizzieren, um diese Handles zu finden, und die Variante davon zeigen, die Lazarus in ihrer Kernel-Payload verwendet.

Mittlere Integritätsstufe (MedIL) KASLR-Umgehung

Zunächst sollte man darauf hinweisen, dass Kernel ASLR zwar existiert, dies aber keine Sicherheitsgrenze für lokale Angreifer darstellt, wenn diese Code auf MedIL-Niveau oder höher ausführen können. Es gibt viele Möglichkeiten, Kernel-Pointer zu leaken, die nur in Sandbox- oder LowIL-Szenarien eingeschränkt sind. Als Hintergrundinformation können Sie einen Blick in „I Got 99 Problems But a Kernel Pointer Ain't One“ von Alex Ionescu werfen; viele dieser Techniken sind auch heute noch anwendbar.

Das Tool der Wahl ist hier ntdll!NtQuerySystemInformation mit der Klasse SystemModuleInformation:

internal static UInt32 SystemModuleInformation = 0xB;

[DllImport(“ntdll.dll”)]
internal static extern UInt32 NtQuerySystemInformation(
    UInt32 SystemInformationClass,
    IntPtr SystemInformation,
    UInt32 SystemInformationLength,
    ref UInt32 ReturnLength);

Diese Funktion gibt die aktuelle Basisadresse aller im Kernel-Bereich geladenen Module zurück. An diesem Punkt ist es möglich, diese Module auf der Festplatte zu analysieren und Rohdatei-Offsets in relative virtuelle Adressen umzuwandeln und umgekehrt.

public static UInt64 RvaToFileOffset(UInt64 rva, List<SearchTypeData.IMAGE_SECTION_HEADER> sections)
{
    foreach (SearchTypeData.IMAGE_SECTION_HEADER section in sections)
    {
        if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize)
        {
            return (rva – section.VirtualAddress + section.PtrToRawData);
        }
    }
    return 0;
}

public static UInt64 FileOffsetToRVA(UInt64 fileOffset, List<SearchTypeData.IMAGE_SECTION_HEADER> sections)
{
    foreach (SearchTypeData.IMAGE_SECTION_HEADER section in sections)
    {
        if (fileOffset >= section.PtrToRawData && fileOffset < (section.PtrToRawData + section.SizeOfRawData))
        {
            return (fileOffset – section.PtrToRawData) + section.VirtualAddress;
        }
    }
    return 0;
}

Ein Angreifer kann diese Module auch mithilfe von Standard-Ladebibliotheks-API-Aufrufen (z. B. ntdll!LdrLoadDll) in seinen Benutzerprozess laden. Auf diese Weise vermeiden Sie Komplikationen bei der Konvertierung von Datei-Offsets in RVAs und zurück. Aus Sicht der operativen Sicherheit (OpSec) ist dies jedoch nicht ideal, da dadurch mehr Erkennungstelemetrie generiert werden kann.

Methode 1: Gadget-Ketten

Wo immer möglich, bevorzuge ich diese Technik, da sie die Portabilität von Lecks über verschiedene Modulversionen hinweg verbessert, weil diese weniger von Patch-Änderungen betroffen sind. Der Nachteil ist, dass Sie auf eine Gadget-Kette angewiesen sind, die für das Objekt existiert, das Sie leaken möchten.

Sehen wir uns die ETW-Registrierungshandles am Beispiel von Microsoft-Windows-Threat-Intelligence an. Nachfolgend sehen Sie den vollständigen Aufruf von nt!EtwRegister.

Screenshot des Codes für die vollständige CALL-Disassemblierung von nt!EtwRegister
Abbildung 8 – Vollständige CALL-Disassemblierung von nt!EtwRegister

Hier wollen wir den Zeiger auf das Registrierungshandle EtwThreatIntProvRegHandle verlieren. Wie in param_4 in der ersten Zeile von Abbildung 8 geladen. Dieser Zeiger wird innerhalb des Abschnitts .data des Kernel-Moduls zu einem globalen Wert aufgelöst. Da dieser Aufruf in einer nicht exportierten Funktion erfolgt, können wir ihre Adresse nicht direkt erfahren. Stattdessen müssen wir prüfen, wo diese globale Variable referenziert wird und ob sie in einer Funktion verwendet wird, deren Adresse leaken kann.

Screenshot des Codes für nt!EtwThreatIntProvRegHandle-Referenzen
Abbildung 9 – nt!EtwThreatIntProvRegHandle-Referenzen

Eine kurze Erkundung einiger dieser Einträge zeigt schnell einen Kandidaten in nt!KeInsertQueueApc.

Screenshot des Codes für die partielle Dekompilierung von nt!KeInsertQueueApc
Abbildung 10 – Partielle Dekompilierung von nt!KeInsertQueueApc

Dies ist aus mehreren Gründen ein hervorragender Kandidat:

  • Nt! KeInsertQueueApc ist eine exportierte Funktion. Das bedeutet, dass wir seine Live-Adresse mithilfe einer KASLR-Umgehung leaken können. Dann können wir unsere Kernel-Sicherheitslücke nutzen, um Daten an dieser Adresse zu lesen.
  • „Global“ wird am Anfang der Funktion verwendet. Das ist sehr hilfreich, da wir dadurch höchstwahrscheinlich keine komplexe Logik zum Parsen von Anweisungen entwickeln müssen, um sie zu finden.

Ein Blick auf das Assembly zeigt das folgende Layout.

Screenshot des Codes für die Teildisassemblierung von nt!KeInsertQueueApc
Abbildung 11 – Partielle Disassemblierung von nt!KeInsertQueueApc

Das Weitergeben dieses Registrierungs-Handles ist dann unkompliziert. Wir lesen eine Reihe von Bytes unter Ausnutzung unserer Schwachstelle aus und suchen nach der ersten Anweisung mov R10, um den relativen virtuellen Offset der globalen Variablen zu berechnen. Die Berechnung würde in etwa so aussehen:

Int32 pOffset = Marshal.ReadInt32((IntPtr)(pBuff.ToInt64() + i + 3));
hEtwTi = (IntPtr)(pOffset + i + 7 + oKeInsertQueueApc.pAddress.ToInt64());

Mit dem Registrierungshandle ist es dann möglich, auf die Datenstruktur _ETW_REG_ENTRY zuzugreifen.

Im Allgemeinen können solche Gadget-Ketten verwendet werden, um eine Vielzahl von Kernel-Datenstrukturen zu leaken. Es ist jedoch anzumerken, dass es nicht immer möglich ist, solche Gadget-Ketten zu finden, und dass Gadget-Ketten manchmal mehrere komplexe Stufen aufweisen können. Eine mögliche Gadget-Kette zum Auslesen von Konstanten aus dem Seitenverzeichniseintrag (PDE) könnte beispielsweise wie folgt aussehen.

MmUnloadSystemImage -> MiUnloadSystemImage -> MiGetPdeAddress

Tatsächlich ergab eine oberflächliche Analyse der ETW-Registrierungshandles, dass die meisten keine geeigneten Gadget-Ketten haben, die wie oben beschrieben verwendet werden können.

Methode 2: Speicherscan

Die andere wichtige Möglichkeit, diese ETW-Registrierungshandles zu leaken, ist die Verwendung von Speicherscans, entweder aus dem Live-Kernel-Speicher oder aus einem Modul auf der Festplatte. Beachten Sie, dass beim Scannen von Modulen auf der Festplatte Datei-Offsets in RVAs konvertiert werden können.

Dieser Ansatz besteht darin, eindeutige Byte-Muster zu identifizieren, nach diesen Mustern zu suchen und schließlich einige Operationen an den Offsets der Musterübereinstimmung durchzuführen. Betrachten wir nt!EtwpInitialize noch einmal genauer, um dies besser zu verstehen:

Screenshot des Codes für die partielle Dekompilierung von nt!EtwpInitialize
Abbildung 12 – Partielle Dekompilierung von nt!EtwpInitialize

Alle fünfzehn Aufrufe von nt!EtwRegister sind größtenteils in dieser Funktion zusammengefasst. Die Hauptstrategie besteht darin, ein eindeutiges Muster zu finden, das vor dem ersten Aufruf von nt!EtwRegister auftritt, und ein zweites Muster, das nach dem letzten Aufruf von nt!EtwRegister auftritt. Das ist nicht allzu kompliziert. Ein Trick, mit dem sich die Portabilität verbessern lässt, besteht darin, einen Musterscanner zu erstellen, der mit Platzhalter-Byte-Zeichenfolgen umgehen kann. Diese Aufgabe bleibt dem Leser überlassen.

Sobald ein Start- und Stoppindex identifiziert wurde, können Sie alle Anweisungen dazwischen einsehen.

  • Potenzielle CALL-Anweisungen können anhand des Opcodes für CALL identifiziert werden, der 0xe8 lautet.
  • Anschließend wird ein Lesevorgang der Größe DWORD verwendet, um den relativen Offset der potenziellen CALL-Anweisung zu berechnen.
  • Dieser Offset wird dann zur relativen Adresse des CALL addiert und um fünf (die Größe der Assembler-Anweisung) erhöht.
  • Schließlich kann dieser neue Wert mit nt!EtwRegister verglichen werden, um alle gültigen CALL-Positionen zu finden.

Sobald alle CALL-Anweisungen gefunden wurden, ist es möglich, rückwärts zu suchen und die Funktionsargumente zu extrahieren, zunächst den GUID, der den ETW-Provider identifiziert, und dann die Adresse des Registrierungshandles. Mit diesen Informationen sind wir in der Lage, gezielte DKOM-Angriffe auf die Registrierungshandles durchzuführen, um den Betrieb der identifizierten Provider zu beeinträchtigen.

Lazarus ETW-Patching

Ich habe mir eine Probe der im ESET-Whitepaper erwähnten FudModle DLL besorgt und sie analysiert. Diese DLL lädt einen signierten, anfälligen Dell-Treiber (aus einer inline XOR-codierten Ressource) und steuert dann den Treiber, um viele Kernelstrukturen zu patchen, um die Telemetrie auf dem Host einzuschränken.

Screenshot des Codes für den Lazarus FudModule-Hash
Abbildung 13 – Lazarus FudModule Hash

Als letzten Teil dieses Beitrags möchte ich die Strategie von Lazarus zur Suche nach Kernel-ETW-Registrierungshandles noch einmal zusammenfassen. Es handelt sich um eine Variante der oben beschriebenen Scanmethode.

Zu Beginn der Suchfunktion löst Lazarus nt!EtwRegister auf und verwendet diese Adresse, um den Scan zu starten

Screenshot des Codes für die partielle ETW-Suchdekompilation des Lazarus FudModule
Abbildung 14 – Partielle ETW-Dekompilierung der Lazarus FudModule-Suche

Diese Entscheidung ist etwas ungewöhnlich, da sie davon abhängt, wo diese Funktion existiert und wo die Funktion aufgerufen wird. Die relative Position einer Funktion in einem Modul kann von Version zu Version variieren, da neuer Code eingeführt, entfernt oder geändert werden kann. Aufgrund der Art und Weise, wie Module kompiliert werden, ist jedoch zu erwarten, dass Funktionen eine relativ stabile Reihenfolge beibehalten. Man kann davon ausgehen, dass es sich hierbei um eine Optimierung der Suchgeschwindigkeit handelt.

Bei der Suche nach Verweisen auf nt!EtwRegister in ntoskrnl zeigt sich, dass mit dieser Methode nicht viele Einträge übersehen werden. Lazarus hat möglicherweise auch zusätzliche Analysen durchgeführt, um festzustellen, dass die fehlenden Einträge nicht wichtig sind oder aus anderen Gründen nicht gepatcht werden müssen. Die fehlenden Einträge sind unten hervorgehoben. Durch die Anwendung dieser Strategie kann Lazarus während des Scans 0x7b1de0 Bytes überspringen, was bei einem langsamen Scanner eine nicht unerhebliche Menge sein kann.

Screenshot des Codes für Instanzen von Aufrufen an nt!EtwRegister
Abbildung 15 – Beispiele für Aufrufe von nt!EtwRegister

Zusätzlich werden beim Start des Scans die ersten fünf Treffer übersprungen, bevor mit der Aufzeichnung der Registrierungshandles begonnen wird. Ein Teil der Suchfunktion ist unten dargestellt.

Screenshot des Codes für die partielle Dekompilierung der ETW-Suche von Lazarus FudModule
Abbildung 16 – Partielle ETW-Dekompilierung der Lazarus FudModule-Suche

Der Code ist etwas schwer verständlich, aber wir verstehen die wichtigsten Punkte der Handlung. Der Code sucht nach Aufrufen von nt!EtwRegister, extrahiert den Registrierungshandle, konvertiert diesen Handle mithilfe eines KASLR-Bypasses in die Live-Adresse und speichert den Zeiger in einem Array, das zu diesem Zweck innerhalb einer Malware-Konfigurationsstruktur (bei der Initialisierung zugewiesen) reserviert ist.

Sehen wir uns zum Schluss noch einmal an, wie Lazarus diese Provider deaktiviert.

Screenshot des Codes für die Registrierungshandles von Lazarus FudModule NULL ETW
Abbildung 17 – Registrierungshandles für Lazarus FudModule NULL ETW

Das macht größtenteils Sinn. Was Lazarus hier tut, ist, die zuvor erwähnte globale Variable zu leaken und dann den Zeiger an dieser Adresse mit NULL zu überschreiben. Dadurch wird der Verweis auf die Datenstruktur _ETW_REG_ENTRY effektiv gelöscht, sofern diese vorhanden ist.

Ich bin aus mehreren Gründen nicht ganz zufrieden mit den gezeigten Methoden:

  • Die Nutzlast erfasst keine Provider-GUIDs, sodass sie keine intelligenten Entscheidungen darüber treffen kann, ob sie den Registrierungshandle des Providers überschreiben soll oder nicht.
  • Die Entscheidung, das Scannen mit einem Offset innerhalb von ntoskrnl zu beginnen, erscheint fragwürdig, da der Offset des Scans je nach Version von ntoskrnl variieren kann.
  • Die ersten fünf Matches willkürlich zu überspringen, erscheint ebenso fragwürdig. Es mag strategische Gründe für diese Entscheidung geben, aber ein besserer Ansatz besteht darin, zunächst alle Provider zu sammeln und dann mithilfe einer programmatischen Logik die Ergebnisse zu filtern.
  • Das Überschreiben des Zeigers auf _ETW_REG_ENTRY sollte funktionieren, aber diese Technik ist etwas offensichtlich. Es wäre besser, Eigenschaften von _ETW_REG_ENTRY, _ETW_GUID_ENTRY oder _TRACE_ENABLE_INFO zu überschreiben.

Ich habe diese Technik für die Wissenschaft neu implementiert, jedoch einige Anpassungen an der Vorgehensweise vorgenommen.

  • Um alle 0xe8 Bytes in ntoskrnl zu finden, wird ein geschwindigkeitsoptimierter Suchalgorithmus verwendet.
  • Anschließend wird eine Nachbearbeitung durchgeführt, um zu bestimmen, welche davon gültige CALL-Anweisungen sind und welche Ziele sie erreichen.
  • Nicht alle Aufrufe von nt!EtwRegister sind nützlich, da die Funktion manchmal mit einem dynamischen Argument für das Registrierungshandle aufgerufen wird. Aus diesem Grund ist eine zusätzliche Logik erforderlich, um die verbleibenden Aufrufe zu filtern.
  • Schließlich werden alle GUIDs in ihre für Menschen lesbare Form aufgelöst und die Registrierungshandles aufgelistet.

Insgesamt ist die oben beschriebene Technik nach den vorgenommenen Anpassungen eindeutig die beste Methode, um diese Art von Aufzählung durchzuführen. Da die Suchzeit bei optimierten Algorithmen vernachlässigbar ist, ist es sinnvoll, das gesamte Modul auf der Festplatte zu scannen und anschließend eine zusätzliche Nachbearbeitungslogik zu verwenden, um die Ergebnisse herauszufiltern.

Auswirkungen von ETW DKOM

Es ist ratsam, kurz zu bewerten, wie schwerwiegend die Auswirkungen eines solchen Angriffs sein könnten. Wenn Providerdaten reduziert oder vollständig eliminiert werden, kommt es zu einem Informationsverlust, aber gleichzeitig melden nicht alle Provider sicherheitsrelevante Ereignisse.

Eine Untergruppe dieser Anbieter ist jedoch sicherheitssensibel. Das offensichtlichste Beispiel dafür ist Microsoft Windows-Threat-Intelligence (EtwTi), eine zentrale Datenquelle für Microsoft Defender Advanced Threat Protection (MDATP), das jetzt Defender for Endpoint heißt (das ist alles sehr verwirrend). Bitte beachten Sie, dass der Zugang zu diesem Anbieter stark eingeschränkt ist. Nur Early Launch Anti Malware(ELAM)-Treiber können sich bei diesem Anbieter registrieren. Ebenso müssen die Prozesse im Benutzerland, die diese Ereignisse empfangen, einen geschützten Status (ProtectedLight / Antimalware) haben und mit demselben Zertifikat wie der ELAM-Treiber signiert sein.

Mit EtwExplorer lässt sich besser nachvollziehen, welche Arten von Informationen dieser Provider signalisieren kann.

Screenshot der ETW Explorer-Dateiseite
Abbildung 18 – ETW Explorer

Das XML-Manifest ist zu umfangreich, um es hier vollständig wiederzugeben, aber unten ist ein Ereignis aufgeführt, um einen Eindruck davon zu vermitteln, welche Arten von Daten mit DKOM unterdrückt werden können.

Screenshot des Codes für das partielle XML-Manifest von ETWTI
Abbildung 19 – Partielles XML-Manifest von EtwTi

Zusammenfassung

Der Kernel war und ist nach wie vor ein wichtiger, umkämpfter Bereich, in dem Microsoft und Drittanbieter Anstrengungen unternehmen müssen, um die Integrität des Betriebssystems zu gewährleisten. Datenkorruption im Kernel ist nicht nur ein Merkmal der Nachausnutzung, sondern auch ein zentraler Bestandteil der Entwicklung von Kernel-Exploits. Microsoft hat in diesem Bereich bereits erhebliche Fortschritte erzielt, insbesondere durch die Einführung von Virtualization Based Security (VBS) und einer seiner Komponenten, Kernel Data Protection (KDP).

Die Nutzer des Windows-Betriebssystems müssen ihrerseits sicherstellen, dass sie diese Fortschritte nutzen, um potenziellen Angreifern möglichst hohe Kosten aufzuerlegen. Windows Defender Application Control (WDAC) kann verwendet werden, um sicherzustellen, dass VBS-Sicherheitsvorkehrungen getroffen wurden und dass Richtlinien vorhanden sind, die das Laden potenziell gefährlicher Treiber verhindern.

Diese Bemühungen sind umso wichtiger, als wir zunehmend beobachten, dass Commodity-TAs BYOVD-Angriffe nutzen, um DKOM im Kernel-Bereich durchzuführen.

 

Mixture of Experts | 12. Dezember, Folge 85

KI entschlüsseln: Wöchentlicher Nachrichtenüberblick

Schließen Sie sich unserer erstklassigen Expertenrunde aus Ingenieuren, Forschern, Produktführern und anderen an, die sich durch das KI-Rauschen kämpfen, um Ihnen die neuesten KI-Nachrichten und Erkenntnisse zu liefern.

Zusätzliche Referenzen

  • Veni, No Vidi, No Vici: Angriffe auf ETW-Blind-EDR-Sensoren (BHEU 2021 Folien) – hier
  • Veni, No Vidi, No Vici: Angriffe auf ETW-Blind-EDR-Sensoren (BHEU 2021 Video) – hier
  • Windows-Sicherheit fördern (BlueHat Shanghai 2019) – hier
  • Eine „einfache“ Sicherheitslücke ausnutzen – in maximal 35 einfachen Schritten! – hier
  • Eine „einfache“ Sicherheitslücke ausnutzen – Teil 1.5 – Das Informationsleck – hier
  • Einführung in Threat-Intelligence ETW – hier
  • TelemetrySourcerer – hier
  • WDAC Policy Wizard – hier

Erfahren Sie hier mehr über X-Force Red. Vereinbaren Sie hier eine kostenlose Beratung mit X-Force.

Weiterführende Lösungen
Sicherheitslösungen für Unternehmen

Transformieren Sie Ihr Sicherheitsprogramm mit Lösungen vom größten Anbieter von Unternehmenssicherheit.

Cybersicherheitslösungen entdecken
Cybersicherheit-Services

Transformieren Sie Ihr Unternehmen und verwalten Sie Risiken mit Beratungsleistungen im Bereich Cybersicherheit sowie Cloud- und Managed-Security-Services.

    Mehr über Cybersicherheitsservices
    Cybersicherheit mit künstlicher Intelligenz (KI)

    Verbessern Sie die Geschwindigkeit, Genauigkeit und Produktivität von Sicherheitsteams mit KI-gestützten Cybersicherheits-Lösungen.

    KI für Cybersicherheit erkunden
    Machen Sie den nächsten Schritt

    Ganz gleich, ob Sie nach Lösungen für Datensicherheit, Endpunktmanagement oder Identitäts- und Zugriffsverwaltung (IAM) suchen – unsere Experten helfen Ihnen bei der Etablierung eines hohen Sicherheitsstatus. Transformieren Sie Ihr Unternehmen und managen Sie Risiken mit einem globalen Branchenführer für Beratungsleistungen im Bereich Cybersicherheit sowie Cloud- und Managed-Security-Services.

    Cybersicherheitslösungen entdecken Entdecken Sie Cybersicherheitsservices