Attacchi DKOM (Direct Kernel Object Manipulation) sui provider ETW.

Immagine di profilo di un uomo che guarda lo schermo del computer mentre lavora a tarda notte

Autore

Ruben Boonen

CNE Capability Lead, Adversary Services

IBM X-Force

In questo post, gli hacker offensivi di IBM® Security X-Force Red analizzano come, con privilegi elevati, gli aggressori possono utilizzare il loro accesso per mettere in scena le funzionalità post-sfruttamento del kernel di Windows. Negli ultimi anni, i resoconti pubblici hanno sempre più dimostrato che gli aggressori meno sofisticati stanno utilizzando questa tecnica per raggiungere i loro obiettivi. È quindi importante mettere in luce questa capacità e imparare di più sul suo potenziale impatto. In particolare, in questo post, valuteremo come il kernel post-sfruttamento possa essere usato per accecare i sensori ETW e collegarli ai campioni di malware identificati in natura lo scorso anno.

Intro

Con il tempo, le mitigazioni della sicurezza e la telemetria di rilevamento su Windows sono migliorate sostanzialmente. Se abbinate a soluzioni di rilevamento e risposta degli endpoint (EDR) ben configurate, queste funzionalità possono rappresentare una barriera non trascurabile al prossimo sfruttamento. Gli aggressori devono affrontare un costo costante per sviluppare e iterare tattiche, tecniche e procedure (TTP) per evitare euristiche di rilevamento. Nel team Adversary Simulation di IBM Security X-Force ci troviamo ad affrontare lo stesso problema. Il nostro team ha il compito di simulare funzionalità di gestione delle minacce avanzate in alcuni degli ambienti più grandi e più protetti. La combinazione di soluzioni di sicurezza complesse e ottimizzate e di team di Security Operazioni Center (SOC) ben formati può rivelarsi molto impegnativa per le attività commerciali. In alcuni casi, l'uso di un TTP specifico diventa completamente obsoleto nell'arco di tre-quattro mesi (di solito legato a stack tecnologici).

Gli aggressori possono scegliere di utilizzare l'esecuzione del codice nel kernel di Windows per manomettere alcune di queste protezioni o per evitare del tutto diversi sensori user-land. La prima dimostrazione pubblicata di tale funzionalità risale al 1999 sulla rivista Phrack Magazine. Negli anni successivi sono stati segnalati diversi casi in cui gli attori delle minacce (TA) hanno utilizzato rootkit del kernel per scopi di post-sfruttamento. Alcuni esempi più antichi includono la Derusbi Family e il Lamberts Toolkit.

Tradizionalmente queste funzionalità sono state per lo più limitate ai TA avanzati. Negli ultimi anni, tuttavia, abbiamo assistito un maggior numero di aggressori di commodity utilizzare primitive di sfruttamento BYOVD ( Bring Your Own Vulnerable Driver) per facilitare le azioni sull'endpoint. In alcuni casi, queste tecniche sono state piuttosto primitive, limitate a compiti semplici, ma ci sono state anche dimostrazioni più efficaci.

Alla fine di settembre 2022, i ricercatori di ESET hanno pubblicato un white-paper su tale funzionalità di kernel utilizzata dai TA Lazarus in numerosi attacchi contro entità in Belgio e nei Paesi Bassi, con scopi di esfiltrazione dei dati. Questo documento illustra una serie di primitive Direct Kernel Object Manipulation (DKOM) che il payload utilizza per nascondere la telemetria OS/AV/EDR. La ricerca pubblica disponibile su queste tecniche è scarsa. Acquisire una comprensione più approfondita della tecnica di post-sfruttamento del kernel è fondamentale per la difesa. Un'argomentazione classica e un po' ingenua che si sente spesso è che un aggressore con privilegi elevati può fare qualsiasi cosa, quindi perché modellare le funzionalità in questo scenario? Questa è una posizione debole. I difensori devono capire quali funzionalità ha a disposizione un aggressore quando i suoi privilegi vengono elevati, quali fonti di dati rimangono affidabili (e quali no), quali opzioni di contenimento esistono e in che modo le tecniche più avanzate potrebbero essere rilevate (anche se non esistono funzionalità per eseguire tali rilevamenti). In questo post mi concentrerò specificamente sulla patch delle strutture kernel Event Tracing for Windows (ETW) per rendere i provider inefficaci o non operativi. Fornirò alcune informazioni di base su questa tecnica, analizzerò il modo in cui un aggressore può manipolare le strutture ETW del kernel e mi addentrerò in alcuni meccanismi di individuazione di queste strutture. Infine, esaminerò come questa tecnica è stata implementata da Lazarus nel suo payload.

Le ultime notizie nel campo della tecnologia, supportate dalle analisi degli esperti

Resta al passo con le tendenze più importanti e interessanti del settore relative ad AI, automazione, dati e oltre con la newsletter Think. Leggi l' Informativa sulla privacy IBM.

Grazie per aver effettuato l'iscrizione!

L'abbonamento sarà fornito in lingua inglese. Troverai un link per annullare l'iscrizione in tutte le newsletter. Puoi gestire i tuoi abbonamenti o annullarli qui. Per ulteriori informazioni, consulta l'Informativa sulla privacy IBM.

ETW DKOM

ETW è una struttura di tracciamento ad alta velocità integrata nel sistema operativo Windows. Consente la registrazione degli eventi e delle attività del sistema da parte delle applicazioni, dei driver e del sistema operativo, fornendo una visibilità dettagliata del comportamento del sistema per il debugging, l'analisi delle prestazioni e la diagnostica della sicurezza.

In questa sezione fornirò una panoramica di alto livello di Kernel ETW e della sua superficie di attacco associata. Ciò sarà utile per comprendere meglio i meccanismi coinvolti nella manipolazione dei provider ETW e gli effetti associati a tali manipolazioni.

Superficie di attacco del kernel ETW

I ricercatori di Binarly hanno tenuto una conferenza al BHEU 2021, in cui si è discussa la superficie di attacco generale dell'ETW su Windows. Una panoramica del modello di minaccia è mostrata di seguito.

diagramma di flusso che mostra la modellazione delle minacce ETW
Figura 1 – Veni, non vidi, non vici: attacchi ai sensori rilevamento e risposta degli endpoint (EDR) ciechi ETW (Binarly)

In questo post, ci concentriamo sulla superficie di attacco spaziale del kernel.

grafico che mostra e descrive gli attacchi ai provider ETW in modalità kernel
Figura 2 – Veni, non vidi, non vici: attacchi ai sensori EDR ciechi ETW (Binarly)

Questo post prende in considerazione solo gli attacchi che rientrano nella prima categoria di attacchi mostrata nella "Figura 2", in cui il tracciamento è disabilitato o alterato in qualche modo.

Come nota precauzionale, quando si prendono in considerazione le strutture opache su Windows è sempre importante ricordare che queste sono soggette a modifiche e, di fatto, cambiano frequentemente nelle diverse versioni del sistema operativo. Ciò è particolarmente importante quando si danneggiano i dati del Kernel, poiché gli errori molto probabilmente daranno luogo a una schermata blu (BSoD), quindi attenzione!

Inizializzazione

I provider del kernel vengono registrati utilizzando nt!EtwRegister, una funzione esportata da ntoskrnl. Una versione decompilata della funzione può essere vista di seguito.

schermata del codice per la decompilazione di nt!EtwRegister
Figura 3 – Decompilazione di nt!EtwRegister

L'inizializzazione completa avviene all'interno della funzione EtwpRegisterKMProvider, ma qui ci sono due indicazioni principali:

  • Il ProviderID è un puntatore a un GUID a 16 byte. Questo GUID è statico su tutti i sistemi operativi, quindi può essere utilizzato per identificare il provider che viene inizializzato.
  • Il RegHandle è un indirizzo di memoria che riceve un puntatore a una struttura _ETW_REG_ENTRY in una chiamata riuscita. Questa struttura dati e alcune delle sue proprietà annidate offrono vie per manipolare il fornitore ETW secondo la ricerca di Binarly.

Elenchiamo brevemente le strutture evidenziate da Binarly nella diapositiva della Figura 2.

ETW_REG_ENTRY

Di seguito è mostrata una lista completa a 64 bit della struttura _ETW_REG_ENTRY. Ulteriori dettagli sono disponibili sul blog di Geoff Chappell qui. Questa struttura può essere ulteriormente esplorata nel Vergilius Project.

// 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

Una delle voci annidate all'interno di _ETW_REG_ENTRY è GuidEntry, una struttura _ETW_GUID_ENTRY. Ulteriori informazioni su questa struttura non documentata si trovano sul blog di Geoff Chappell qui e sul 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

Infine, una delle voci annidate all'interno di _ETW_GUID_ENTRY è ProviderEnableInfo, che è una struttura _TRACE_ENABLE_INFO. Per maggiori informazioni sugli elementi di questa struttura dati, puoi fare riferimento alla documentazione ufficiale di Microsoft e al Vergilius Project. Le impostazioni di questa struttura influiscono direttamente sulle operazioni e sulle funzionalità del provider.

// 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
};

Comprendere l'uso del handle di registrazione

Sebbene una certa base teorica sia utile, è sempre meglio analizzare esempi concreti di utilizzo per comprendere meglio un argomento. Consideriamo brevemente un esempio. La maggior parte dei provider Kernel ETW critici è inizializzata all'interno di nt! EtwpInitialize, che non viene esportato. L'analisi di questa funzione rivela circa quindici fornitori.

Schermata del codice per la decompilazione parziale di nt!EtwpInitialize
Figura 4 – Decompilazione parziale di nt!EtwpInitialize

Prendendo come esempio la voce Microsoft-Windows-Threat-Intelligence (EtwTi), possiamo controllare il parametro globale ThreatIntProviderGuid per recuperare il GUID di questo provider.

Schermata del codice per EtwTi Provider GUID
Figura 5 – GUID del provider EtwTi

La ricerca di questo GUID online rivelerà immediatamente che siamo riusciti a recuperare il valore corretto (f4e1897c-bb5d-5668-f1d8-040f4d8dd344).

Esaminiamo un caso in cui viene utilizzato il parametro di registrazione degli handle, EtwThreatIntProvRegHandle, e analizziamo come viene utilizzato. Un luogo in cui si fa riferimento all'handle è nt!EtwTiLogDriverObjectUnLoad. Dal nome di questa funzione, possiamo intuire che essa è destinata a generare eventi quando un oggetto driver viene scaricato dal Kernel.

Schermata del codice per la decompilazione di nt!EtwTiLogDriverUnload
Figura 6 – Decompilazione di nt!EtwTiLogDriverUnload

Le funzioni nt!EtwEventEnabled e nt!EtwProviderEnabled sono entrambe chiamate qui, passando l'handle di registrazione come uno degli argomenti. Esaminiamo una di queste sotto-funzioni per capire meglio cosa sta accadendo.

Schermata del codice per la decompilazione di nt!EtwProviderEnable
Figura 7 – Decompilazione di nt!EtwProviderEnable

Certo, è un po' difficile da seguire. Tuttavia, l'aritmetica dei puntatori non è particolarmente importante. Invece, concentriamoci su come questa funzione elabora l'handle di registrazione. Sembra che la funzione convalidi una serie di proprietà della struttura _ETW_REG_ENTRY e delle sue sottostrutture, come la proprietà GuidEntry.

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

E la proprietà GuidEntry->ProviderEnableInf.

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

La funzione passa poi a controlli simili basati su livelli. Infine, la funzione restituisce true o false per indicare se un provider è abilitato alla registrazione degli eventi a un livello e una parola chiave specificati. Maggiori dettagli sono disponibili nella documentazione ufficiale di Microsoft.

Possiamo vedere che quando un fornitore viene accessibile tramite il suo handle di registrazione, l'integrità di quelle strutture diventa molto importante per il funzionamento del fornitore. Al contrario, se un aggressore fosse in grado di manipolare quelle strutture, potrebbe influenzare il flusso di controllo del chiamante per eliminare o cancellare eventi dalla registrazione.

Attaccare gli handle di registrazione

Ripensando alla superficie di attacco dichiarata da Binarly e basandoci sulla nostra analisi superficiale, possiamo ipotizzare alcune strategie per interrompere la raccolta di eventi.

  • Un aggressore può rendere NULL il puntatore _ETW_REG_ENTRY. Qualsiasi funzione che faccia riferimento all'handle di registrazione presumerebbe quindi che il provider non sia stato inizializzato.
  • Un aggressore può rendere NULL il puntatore _ETW_REG_ENTRY->GuidEntry->ProviderEnableInfo. Questo dovrebbe effettivamente disabilitare le funzionalità del provider, in quanto ProviderEnableInfo è un puntatore a una struttura _TRACE_ENABLE_INFO che delinea il funzionamento del provider.
  • Un aggressore può sovrascrivere le proprietà della struttura dati _ETW_REG_ENTRY->GuidEntry->ProviderEnableInfo per modificare la configurazione del provider.
    • IsEnabled: impostare su 1 per abilitare la ricezione di eventi dal provider o per regolare le impostazioni utilizzate durante la ricezione di eventi dal provider. Imposta a 0 per disabilitare la ricezione degli eventi dal provider.
    • Livello: valore che indica il livello massimo di eventi che si desidera che il provider scriva. Il provider in genere scrive un evento se il livello dell'evento è inferiore o uguale a questo valore, oltre a soddisfare i criteri MatchAnyKeyword e MatchAllKeyword.
    • MatchAnyKeyword: bitmask a 64 bit di parole chiave che determinano le categories di eventi che vuoi che il fornitore scriva. Il provider in genere scrive un evento se i bit delle parole chiave dell'evento corrispondono a uno dei bit impostati in questo valore o se l'evento non ha bit di parole chiave impostati, oltre a soddisfare i criteri Level e MatchAllKeyword.
    • MatchAllKeyword: maschera di bit a 64 bit di parole chiave che limita gli eventi che vuoi che il fornitore scriva. Il provider scrive tipicamente un evento se i bit della parola chiave dell'evento corrispondono a tutti i bit impostati in questo valore o se l'evento non ha bit della parola chiave impostati, oltre a soddisfare i criteri Livello e MatchAnyKeyword.

Tradecraft di ricerca kernel

Ora abbiamo una buona idea di come si presenta un attacco DKOM su ETW. Supponiamo che l'aggressore abbia una vulnerabilità che concede una primitiva di lettura/scrittura del kernel, come fa il malware Lazarus in questo caso caricando un driver vulnerabile. Ciò che manca è un modo per trovare questi handle di registrazione.

Descriverò due tecniche principali per trovare questi handle e mostrerò la variante di una di queste tecniche utilizzata da Lazarus nel payload del kernel.

Bypass KASLR di livello di integrità medio (MedIL)

Innanzitutto, potrebbe essere prudente spiegare che, sebbene esista Kernel ASLR, questo non costituisce un boundary per gli aggressori locali se riescono a eseguire codice a livello MedIL o superiore. Esistono molti modi per far trapelare i puntatori del kernel, che sono limitati solo agli scenari sandbox o LowIL. Per un po' di background, dai un'occhiata a I Got 99 Problems But a Kernel Pointer Ain't One di Alex Ionescu, molte di queste tecniche sono ancora applicabili oggi.

Lo strumento migliore è ntdll!NtQuerySystemInformation con la classe SystemModuleInformation:

internal static UInt32 SystemModuleInformation = 0xB;

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

Questa funzione restituisce l'indirizzo base live di tutti i moduli caricati nello spazio Kernel. A quel punto, è possibile analizzare quei moduli su disco e convertire gli offset grezzi dei file in indirizzi virtuali relativi e viceversa.

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;
}

Un aggressore può anche caricare questi moduli nel proprio processo user-land utilizzando chiamate API standard della libreria di caricamento (ad esempio, ntdll!LdrLoadDll). In questo modo si eviteranno complicazioni nella conversione degli offset dei file in RVA e viceversa. Tuttavia, dal punto di vista della sicurezza operativa (OpSec) questo non è l'ideale poiché può generare più telemetria di rilevamento.

Metodo 1: catene di gadget

Quando possibile, questa è la tecnica che preferisco perché rende le perdite più trasferibili tra le versioni dei moduli, in quanto sono meno influenzate dalle modifiche delle patch. Lo svantaggio è che si dipende da una catena di gadget esistente per l'oggetto che si desidera divulgare.

Considerando gli handle di registrazione ETW, prendiamo come esempio Microsoft-Windows-Threat-Intelligence. Di seguito puoi vedere la chiamata completa a nt!EtwRegister.

Schermata del codice per il disassemblaggio di nt!EtwRegister full CALL
Figura 8 - Disassemblaggio di nt!EtwRegister full CALL

Qui vogliamo far trapelare il puntatore all'handle di registrazione, EtwThreatIntProvRegHandle. Qui lo vediamo caricato in param_4 sulla prima riga della Figura 8. Questo puntatore si risolve in un globale all'interno della sezione .data del modulo Kernel. Poiché questa chiamata avviene in una funzione non esportata, non possiamo divulgare direttamente il suo indirizzo. Invece, dobbiamo guardare dove questo globale è riferito e vedere se viene utilizzato in una funzione i cui indirizzi possono essere permeati.

schermata del codice per i riferimenti nt!EtwThreatIntProvRegHandle
Figura 9 – Riferimenti di nt!EtwThreatIntProvRegHandle

Esplora alcune di queste voci rivela rapidamente un candidato in nt!KeInsertQueueApc.

screenshot del codice per la decompilazione parziale di nt!KeInsertQueueApc
Figura 10 – Decompilazione parziale di nt!KeInsertQueueApc

Questo è un ottimo candidato per diversi motivi:

  • nt!KeInsertQueueApc è una funzione esportata. Questo significa che possiamo far trapelare il suo indirizzo live usando un bypass KASLR. Poi possiamo utilizzare la nostra vulnerabilità del kernel per leggere i dati a quell'indirizzo.
  • Il globale viene utilizzato all'inizio della funzione. Questo è molto utile perché significa che probabilmente non dovremo costruire una logica complessa di analisi delle istruzioni per trovarla.

Osservando l'assemblaggio si nota il seguente layout.

schermata del codice per lo smontaggio parziale di nt!KeInsertQueueApc
Figura 11 – Disassemblaggio parziale di nt!KeInsertQueueApc

La divulgazione di questo handle di registrazione diventa quindi semplice. Abbiamo letto una serie di byte utilizzando la nostra vulnerabilità e cerchiamo la prima istruzione mov R10 per calcolare l'offset virtuale relativo della variabile globale. Il calcolo sarà più o meno questo:

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

Con l'handle di registrazione, è quindi possibile accedere alla struttura dati _ETW_REG_ENTRY.

In generale, queste catene di gadget possono essere utilizzate per far trapelare una varietà di strutture dati del kernel. Tuttavia, vale la pena sottolineare che non è sempre possibile trovare tali catene di gadget e talvolta possono avere più fasi complesse. Ad esempio, una possibile catena di gadget per divulgare le costanti delle voci della directory di pagina (PDE) potrebbe apparire così.

MmUnloadSystemImage -> MiUnloadSystemImage -> MiGetPdeAddress

In effetti, una rapida analisi degli handle di registrazione di ETW ha rivelato che la maggior parte non dispone di catene di gadget adeguate che possano essere utilizzate come descritto sopra.

Metodo 2: scansione della memoria

L'altra opzione principale per far trapelare questi handle di registrazione ETW è quella di utilizzare la scansione della memoria, sia dalla memoria live del Kernel che da un modulo su disco. Ricorda che, quando si scansionano moduli su disco, è possibile convertire gli offset dei file in RVA.

Questo approccio consiste nell'identificare pattern di byte unici, scansionare tali pattern e infine eseguire alcune operazioni agli offset della corrispondenza del pattern. Diamo un'altra occhiata a nt!EtwpInitialize per capire meglio:

Schermata del codice per la decompilazione parziale di nt!EtwpInitialize
Figura 12 – Decompilazione parziale di nt!EtwpInitialize

Tutte le quindici chiamate a nt!EtwRegister sono per lo più raggruppate in questa funzione. La strategia principale è trovare un modello unico che appare prima della prima chiamata a nt!EtwRegister e un secondo modello che appare dopo l'ultima chiamata a nt!EtwRegister. Non è troppo complesso. Un trucchetto che può essere utilizzato per migliorare la portabilità è quello di creare un pattern scanner in grado di gestire le stringhe di byte wild card. Questo è un compito per il lettore.

Una volta identificati un indice di inizio e uno di fine, è possibile esaminare tutte le istruzioni intermedie.

  • Le potenziali istruzioni CALL possono essere identificate in base all'opcode per CALL, che è 0xe8.
  • A seguire, viene utilizzata una lettura di dimensioni DWORD per calcolare l'offset relativo della potenziale istruzione CALL.
  • Questo offset viene quindi aggiunto all'indirizzo relativo della CALL e incrementato di cinque (la dimensione dell'istruzione assembly).
  • Infine, questo nuovo valore può essere confrontato con nt!EtwRegister per trovare tutte le posizioni CALL valide.

Una volta trovate tutte le istruzioni CALL, è possibile cercare all'indietro ed estrarre gli argomenti della funzione, prima il GUID che identifica il provider ETW e in secondo luogo, l'indirizzo dell'handle di registrazione. Grazie a queste informazioni, siamo in grado di eseguire attacchi DKOM informati sugli handle di registrazione per influenzare le Operazioni dei provider identificati.

Patch Lazarus ETW

Ho ottenuto un campione della DLL FudModle menzionata nel whitepaper ESET e l'ho analizzata. Questa DLL carica un driver Dell vulnerabile firmato (da una risorsa codificata inline XOR) e poi pilota il driver per patchare molte strutture del Kernel al fine di limitare la telemetria sull'host.

Schermata del codice per l'hash Lazarus FUDModule
Figura 13 – Hash Lazarus FudModule

Come parte finale di questo post, voglio esaminare la strategia che Lazarus utilizza per trovare gli handle di registrazione Kernel ETW. È una variante del metodo di scansione di cui abbiamo parlato sopra.

All'inizio della funzione di ricerca, Lazarus risolve nt!EtwRegister e usa questo indirizzo per avviare la scansione

Schermata del codice per la ricerca parziale ETW di Lazarus FudModule
Figura 14 – Decompilazione parziale della ricerca ETW di Lazarus FudModule

Questa decisione è un po' strana, perché si basa sul luogo in cui esiste quella funzione rispetto a quello in cui la funzione viene chiamata. La posizione relativa di una funzione in un modulo può variare da versione a versione, poiché nuovo codice può essere introdotto, rimosso o modificato. Tuttavia, a causa del modo in cui vengono compilati i moduli, ci si aspetta che le funzioni mantengano un ordine relativamente stabile. Si presume che si tratti di un'ottimizzazione della velocità di ricerca.

Cercando i riferimenti a nt!EtwRegister in ntoskrnl, sembra che non siano molte le voci mancate con questa tecnica. Lazarus potrebbe anche aver eseguito ulteriori analisi per stabilire che le voci mancanti non sono importanti o che non necessitano di essere corrette. Le voci mancanti sono evidenziate di seguito. Adottando questa strategia, Lazarus si può saltare 0x7b1de0 byte durante la scansione, che può essere una quantità non banale se lo scanner è lento.

Schermata del codice per le istanze delle chiamate a nt!EtwRegister
Figura 15 – Istanze di chiamata a nt!EtwRegister

Inoltre, quando si avvia la scansione, le prime cinque corrispondenze vengono saltate prima di iniziare a registrare gli handle di registrazione. Parte della funzione di ricerca è mostrata di seguito.

Screenshot del codice per la decompilazione parziale della ricerca ETW di Lazarus FudModule
Figura 16 – Decompilazione parziale della ricerca ETW di Lazarus FudModule

Il codice è un po' ottuso, ma riusciamo a capire i punti salienti della trama. Il codice cerca le chiamate verso nt!ETWRegister, estrae l'handle di registrazione, converte questo handle nell'indirizzo live utilizzando un bypass KASLR e memorizza il puntatore in un array impostato a tale scopo all'interno di una struttura di configurazione del malware (allocata all'inizializzazione).

Infine, diamo un'occhiata a cosa fa Lazarus per disabilitare questi fornitori.

Schermata del codice per gli handle di registrazione NULL ETW di Lazarus FudModule
Figura 17 – Handle di registrazione Lazarus FudModule NULL ETW

Questo ha senso: quello che fa Lazarus qui è far trapelare la variabile globale che abbiamo visto prima e poi sovrascrivere il puntatore a quell'indirizzo con NULL. In questo modo si cancella di fatto il riferimento alla struttura dati _ETW_REG_ENTRY, se esistente.

Non sono del tutto soddisfatto delle tecniche di lavorazione illustrate per alcuni motivi:

  • Il payload non cattura i GUID dei fornitori, quindi non può prendere decisioni intelligenti su se debba o meno sovrascrivere l'handle di registrazione del provider.
  • La decisione di iniziare la scansione da un offset all'interno di ntoskrnl sembra discutibile, perché l'offset della scansione può variare a seconda della versione di ntoskrnl.
  • Saltare arbitrariamente le prime 5 partite sembra altrettanto discutibile. Questa decisione potrebbe avere motivazioni strategiche, ma un approccio migliore è quello di raccogliere prima tutti i provider e poi utilizzare una logica programmatica per filtrare i risultati.
  • Sovrascrivere il puntatore a _ETW_REG_ENTRY dovrebbe funzionare, ma questa tecnica è un po' ovvia. Sarebbe meglio sovrascrivere le proprietà di _ETW_REG_ENTRY o _ETW_GUID_ENTRY o _TRACE_ENABLE_INFO.

Ho reimpiegato questa tecnica per la scienza, ma ho fatto qualche correzione al tradecraft.

  • Per trovare tutti i byte 0xe8 in ntoskrnl, viene utilizzato un algoritmo di ricerca ottimizzato per la velocità.
  • A seguire, viene effettuata una post-elaborazione per determinare quali di queste sono istruzioni CALL valide e le rispettive destinazioni.
  • Non tutte le chiamate a nt!EtwRegister sono utili, perché a volte la funzione viene chiamata con un argomento dinamico per l'handle di registrazione. Per questo motivo, è necessaria una logica aggiuntiva per filtrare le chiamate rimanenti.
  • Infine, tutti i GUID vengono risolti nella loro forma leggibile e vengono enumerati gli handle di registrazione.

Nel complesso, dopo gli aggiustamenti, la tecnica di cui sopra è chiaramente il modo migliore per eseguire questo tipo di enumerazione. Poiché il tempo di ricerca è trascurabile con algoritmi ottimizzati, ha senso scansionare l'intero modulo su disco e poi usare una logica post-scansione aggiuntiva per filtrare i risultati.

L'impatto del DKOM ETW

È prudente valutare brevemente l'impatto di un simile attacco. Quando i dati dei fornitori vengono ridotti o eliminati del tutto, si verifica una perdita di informazioni, ma allo stesso tempo non tutti i fornitori segnalano eventi sensibili alla sicurezza.

Tuttavia, alcuni sottoinsiemi di questi provider sono sensibili alla sicurezza. L'esempio più ovvio è Microsoft-Windows-Threat-Intelligence (EtwTi), che è una fonte di dati fondamentale per Microsoft Defender Advanced Threat Protection (MDATP), ora chiamato Defender for Endpoint (sì, è tutto molto confuso). È bene notare che l'accesso a questo provider è fortemente limitato, solo i driver Early Launch Anti Malware (ELAM) possono registrarsi su questo fornitore. Allo stesso modo, i processi user-land che ricevono questi eventi devono avere uno stato protetto (ProtectedLight/Antimalware) e essere firmati con lo stesso certificato del driver ELAM.

Utilizzando EtwExplorer è possibile farsi un'idea migliore di quali tipi di informazioni questo fornitore può segnalare.

Schermata della pagina del file di ETW Explorer
Figura 18 – ETW Explorer

Il manifesto XML è troppo grande per poterlo includere interamente qui, ma un evento è mostrato qui sotto per dare un'idea dei tipi di dati che possono essere soppressi utilizzando DKOM.

Schermata del codice per il manifesto parziale XML di EtwTi
Figura 19 – Manifesto parziale XML di EtwTi

Conclusioni

Il kernel è stato e continua a essere un'area importante e contestata in cui Microsoft e i fornitori terzi devono impegnarsi per salvaguardare l'integrità del sistema operativo. La corruzione dei dati nel Kernel non è solo una caratteristica del post-sfruttamento, ma anche un componente centrale nello sviluppo dell'utilizzare del Kernel. Microsoft ha fatto molti progressi in questo ambito con l'introduzione della Virtualizzazione Based Security (VBS) e di uno dei suoi componenti come Kernel Data Protection (KDP).

Gli utenti del sistema operativo Windows, a loro volta, devono assicurarsi di utilizzare al meglio questi progressi per imporre il maggior costo possibile ai potenziali aggressori. È possibile utilizzare Windows Defender Application Control (WDAC) per garantire che siano implementate misure di sicurezza VBS e che esistano criteri che impediscano il caricamento di driver potenzialmente pericolosi.

Questi sforzi sono ancora più importanti man mano che vediamo sempre più TA commodity utilizzare attacchi BYOVD per eseguire DKOM nello spazio Kernel.

 

Mixture of Experts | 12 dicembre, episodio 85

Decoding AI: Weekly News Roundup

Unisciti al nostro gruppo di livello mondiale di ingegneri, ricercatori, leader di prodotto e molti altri mentre si fanno strada nell'enorme quantità di informazioni sull'AI per darti le ultime notizie e gli ultimi insight sull'argomento.

Riferimenti aggiuntivi

  • Veni, No Vidi, No Vici: Attacks on ETW Blind EDR Sensors (slide BHEU 2021) – qui
  • Veni, No Vidi, No Vici: Attacks on ETW Blind EDR Sensors (video BHEU 2021) – qui
  • Advancing Windows Security (BlueHat Shanghai 2019) – qui
  • Exploiting a “Simple” Vulnerability – In 35 Easy Steps or Less! – qui
  • Exploiting a “Simple” Vulnerability – Part 1.5 – The Info Leak – qui
  • Introduction to Threat Intelligence ETW – qui
  • TelemetrySourcerer – qui
  • WDAC Policy Wizard – qui

Scopri di più su X-Force Red qui. Prenota una consulenza gratuita con X-Force qui.

Soluzioni correlate
Soluzioni di sicurezza aziendale

Trasforma il tuo programma di sicurezza con le soluzioni offerte dal più grande provider di sicurezza aziendale.

Esplora le soluzioni di cybersecurity
Servizi di cybersecurity

Trasforma il tuo business e gestisci i rischi con la consulenza sulla cybersecurity, il cloud e i servizi di sicurezza gestiti.

    Scopri i servizi di sicurezza informatica
    Cybersecurity dell'intelligenza artificiale (AI)

    Migliora la velocità, l'accuratezza e la produttività dei team di sicurezza con soluzioni di cybersecurity basate sull'AI.

    Esplora la cybersecurity dell'AI
    Fai il passo successivo

    Che tu abbia bisogno di soluzioni di sicurezza dei dati, di gestione degli endpoint, o di gestione delle identità e degli accessi (IAM), i nostri esperti sono pronti a collaborare con te per farti raggiungere un solido livello di sicurezza.Trasforma il tuo business e gestisci i rischi con un leader a livello globale nel campo della consulenza per la cybersecurity, del cloud e dei servizi di sicurezza gestiti.

    Esplora le soluzioni di cybersecurity Scopri i servizi di cybersecurity