用于EDR繞過的.NET CLR日志篡改技術
介紹
近年來,已經發布了許多用于規避端點安全解決方案和來源的技術,例如 A/V、EDR 和日志記錄設施。為實現預期結果而部署的方法通常在復雜性和實施方面有所不同,但是,有效性通常是最終目標(當然,要考慮到潛在的權衡)。防御者可以利用操作系統的原生設施和支持框架來構建質量檢測。檢測潛在有趣的 .NET 行為的一種方法是監控 .NET 執行事件的公共語言運行時 (CLR) 使用日志(“UsageLogs”)。
這篇文章中,我們將確定防御者如何(可能)利用 .NET 使用日志進行檢測和取證響應,調查規避檢測日志監控的方法,并討論捕獲使用日志篡改行為的潛在監控機會。
使用 .NET CLR 使用日志檢測可疑活動
當執行 .NET 應用程序或將程序集注入另一個進程內存空間(由紅隊)時,加載 .NET 運行時以促進程序集代碼的執行并處理各種不同的 .NET 管理任務。由 CLR (crl.dll) 啟動的一項任務是,一旦程序集在(用戶)會話上下文中第一次完成執行,就創建一個以執行進程命名的使用日志文件。此日志文件包含 .NET 程序集模塊數據及其為 .NET本機映像自動生成 (auto-NGEN)提供信息文件的目的。
在進程退出之前,CLR 通常會寫入以下文件路徑之一(盡管可能還有其他路徑):
<系統驅動器>:\Users\<用戶>\AppData\Local\Microsoft\CLR_<版本>_(arch)\UsageLogs<系統驅動器>:\Windows\<System32|SysWOW6$=4>\config\systemprofile\AppData\Local\Microsoft\CLR_<版本>_(arch)\UsageLogs
例如,我們可以看到powershell.exe.log使用日志是在“優雅地”終止 powershell.exe 進程之前首次創建的:


從DFIR 和威脅搜尋的角度來看,分析使用日志對于調查目的非常有機會,正如MENASEC 應用研究團隊在這篇出色的博客文章中所概述的那樣。從端點監控的角度來看,端點檢測和響應解決方案 ('EDR') 可能會監控使用日志文件創建事件,以識別已加載 .NET CLR 的可疑或不太可能的進程。例如,Olaf Hartong ( @olafhartong ) 維護著令人難以置信的Sysmon-Modular項目,并慷慨地提供了一個監視使用日志的規則配置.NET 2.0 活動和有風險的 LOLBIN 的活動。紅隊人員當然可以預期許多商業供應商正在以類似的方式監控使用日志(例如,捕獲 Cobalt Strike 的execute-assembly)。
在深入探討規避技術之前,讓我們簡要討論一下.NET 中的配置
.NET CLR 配置旋鈕快速入門,在為 .NET Framework 維護大量有價值的文檔并隨后發布開源 .NET Core 的同時,Microsoft 提供了對 .NET 生態系統功能組件內部運作的寶貴(顯式和隱式)洞察。一般來說,.NET 是一個非常強大且功能強大的開發平臺和運行時框架,用于構建和運行 .NET 托管應用程序。.NET 的一個強大功能(特別是在 Windows 上)是能夠調整 .NET 公共語言運行時 (CLR) 的配置和行為以用于開發和/或調試目的。這可以通過由CLRConfig檢索的環境變量、注冊表設置和/或配置文件/屬性設置控制的.NET CLR配置旋鈕來實現。
濫用配置旋鈕并不是一個新概念。其他研究人員已經探索了利用旋鈕設置執行任意代碼和/或逃避防御控制的各種技術。最近的一些示例包括 Adam Chester ( @_xpn_ ) 使用ETWEnabled CLR 配置旋鈕來禁用Windows 事件跟蹤 (ETW)和 Paul La?né ( @am0nsec ) 使用GCName CLR 配置旋鈕來指定自定義垃圾收集器 (DLL)用于加載任意代碼和繞過應用程序控制解決方案。當然,Casey Smith (@subTee) 用于探索 .NET 的所有內容,包括COR_PROFILER用于防御規避/UAC 繞過的非托管代碼加載和Ghost Loader AppDomainManager注入技術(如@netbiosX進一步描述)。
調整 .NET 配置旋鈕注冊表設置以繞過 CLR 使用日志文件的創建
有趣的是,可以通過在注冊表中設置NGenAssemblyUsageLog CLR 配置旋鈕或通過配置環境變量來控制 .NET 使用日志的輸出位置(如下一節所述)。通過簡化為期望值指定任意值(例如假輸出位置或垃圾數據),不會創建 .NET 執行上下文的使用日志文件。NGenAssemblyUsageLog CLR 配置旋鈕字符串值可以在以下注冊表項中設置:
HKCU\SOFTWARE\Microsoft\.NETFrameworkHKLM\SOFTWARE\Microsoft\.NETFramework
在 HKCU 配置單元中配置值將應用于活動用戶上下文并影響日志輸出,否則日志輸出將記錄到:
<SystemDrive>:\Users\<user>\AppData\Local\Microsoft\CLR_<version>_(arch)\ UsageLogs 目錄和/或 Microsoft Office Hub 路徑。在 HKLM 配置單元中配置值將應用于系統上下文并影響日志輸出,
否則日志輸出將記錄到:
<SystemDrive>:\Windows\<System32|SysWOW64>\config\systemprofile\AppData\Local\Microsoft\CLR_<version> _(arch)\UsageLogs 目錄路徑。讓我們通過一個簡單的示例來演示預期和被篡改的行為
以下源代碼編譯為名為“test.exe”的 64 位 NET 應用程序:

在執行應用程序之前,請注意此測試機器上的UsageLogs目錄是空的。該目錄可以很好地填充在生產或測試機器上。

執行后,會出現一個簡單的消息框:

檢查UsageLogs目錄后,會創建一個名為test.exe.log的文件,其中包含程序集模塊信息:

接下來,讓我們從UsageLogs目錄中刪除 test.exe.log 文件以演示篡改行為:

在重新執行 .NET 應用程序之前,讓我們使用以下命令驗證HKCU中是否存在 .NETFramework 注冊表(子)鍵:
reg query "HKCU\SOFTWARE\Microsoft\.NETFramework"

在這種情況下,注冊表項存在并且不包含其他值或子項。(注意:如果.NETFramework鍵不存在,可以創建)。接下來,將NGenAssemblyUsageLog配置旋鈕字符串值添加到 .NETFramework 鍵并驗證更改:
reg.exe add "HKCU\SOFTWARE\Microsoft\.NETFramework" /f /t REG_SZ /v "NGenAssemblyUsageLog" /d "NothingToSeeHere"

程序再次執行:

和預期的一樣,在查看目標UsageLogs目錄的內容后, text.exe.log 文件并沒有出現:

所以您可能會問——當您為NGenAssemblyUsageLog名稱提供任意值時,CLR 會做什么?好吧,它實際上只是將任意字符串插入到“正確”構造的路徑中。例如,如果我們將路徑數據設置為“eeeee”并執行一個 .NET 應用程序,CLR 會將字符串值插入到構造的路徑中:

由于找不到路徑,使用日志不會寫入磁盤。如以下屏幕截圖所示,部分UsageLogs路徑后綴是硬編碼并從clr.dll中提取的:

調整 .NET 配置旋鈕環境變量以規避 CLR 使用日志文件的創建
CLR 配置旋鈕也可以通過設置帶有COMPlus_前綴的環境變量來配置。在以下示例中,COMPlus_NGenAssemblyUsageLog在命令提示符中設置為任意值(例如“zzzz”)。調用 PowerShell(一個 .NET 應用程序)時,COMPlus_NGenAssemblyUsageLog環境變量從父 cmd.exe 進程繼承:

退出 PowerShell 后,我們注意到使用日志文件 (powershell.exe.log) 從未在UsageLogs目錄中創建:

以在啟動子進程時注入COMPlus_ETWEnabled環境變量。修改程序中的一些變量后,可以使用相同的欺騙技術來禁用使用日志輸出,如以下代碼片段所示:

編譯并執行程序后,PowerShell.exe 將啟動,
并將COMPlus_NGenAssemblyUsageLog環境變量設置為任意值“zz”:


正如預期的那樣,退出 PowerShell 會話后永遠不會創建使用日志:
注意:修改后的環境變量欺騙 POC 可以在這里找到。

通過強制進程終止中斷 CLR 使用日志輸出操作
.NET 配置旋鈕提供了一種優雅的方式來影響日志流。但是,有一些方法可以中斷使用日志創建過程,而無需進行配置更改。這些方法對中斷流程和程序工作流程構成更大的風險。當進程“優雅”退出時會生成使用日志。當程序集完成執行過程時會發生這種情況,例如使用隱式或顯式返回語句或在 (C#) 托管代碼中使用Environment.Exit()方法時:


但是,如果進程被強制終止,Usage Log 進程將被中斷并且永遠不會寫入磁盤。例如,Process.Kill()方法可用于實現所需的結果(有丟失數據或 shell的風險):


通過模塊卸載中斷 CLR 使用日志輸出操作
在另一個有趣但有風險的測試場景中,篡改加載的模塊 (DLL) 可通過破壞進程的穩定性并導致其過早退出來破壞 CLR 使用日志的創建。為此,我們利用了 .NET 委托函數指針和由 The Wover ( @TheRealWover)和 b33f ( @FuzzySec ) 編寫的強大的DInvoke庫。對于測試用例,為FreeLibrary( )聲明了一個委托函數指針Win32 API 函數被調用以從正在運行的 .NET 托管進程中卸載模塊。刪除單個模塊或較少的模塊組合可能會達到相同的效果,但是,我們將卸載幾個 .NET 模塊以增加使進程不穩定以強制終止和中斷使用日志創建的機會(注意:我們正在挑選.NET 模塊,但也可以卸載其他 DLL)
要成功卸載模塊,我們必須首先使用 DInvoke 的GetLibraryAddress()獲取指向FreeLibrary()函數的庫地址的指針。然后,我們使用來自 .NET 'Interop' 服務的GetDelegateForFunctionPointer()方法將函數指針轉換為FreeLibrary() API 方法的可調用委托。接下來,我們通過使用 DInvoke 的GetPebLdrModuleEntry()方法在 .NET 進程的進程執行塊 (PEB)中搜索每個模塊的基地址引用來獲取每個已加載模塊 (DLL)的句柄。最后,我們稱FreeLibrary使用每個模塊的句柄委托函數以將其從內存中卸載。此測試用例中的 POC 代碼如下所示:

編譯并執行代碼后,Usage Log 文件創建過程中斷(如預期的那樣):

防御考慮
繼續監控使用日志文件和目錄。為使用日志的創建和修改實施分析/簽名/檢測。盡管這里展示了可疑的攻擊技術,但這種檢測仍然非常有價值。攻擊性操作員在執行他們的 .NET 工具時不會總是考慮使用日志篡改。
查找通常不會加載 CLR 以創建使用日志的(不規則的)非托管二進制文件和腳本主機的日志實例。利用 Olaf Hartong ( @olafhartong ) 的 Sysmon-Modular規則配置和/或此 Elastic Security規則查詢作為開始使用規則集的基準。此外,Samir ( @SBousseaden ) 為使用 .NET 工具監控 WinRM 橫向移動提供了出色的檢測提示。
此外,審計和監視刪除使用日志文件的嘗試,因為攻擊性操作員可能會從磁盤中刪除使用日志文件以掩蓋他們的蹤跡。注意:這是 MENASEC博客文章中提到的 tradecraft 。
監視可疑的 .NET 運行時負載。如果部署了使用日志規避,則識別可疑的 .NET CLR 運行時負載可能是一種有趣的補償檢測機制。加載 CLR 的非托管進程(例如MS Office)。可能是妥協的指標。
監控 CLR 配置旋鈕的添加或修改。Roberto Rodriguez ( @Cyb3rWard0g ) 撰寫了一篇精彩的文章,用于檢測 [COMPLUS_]ETWEnabled 配置旋鈕調整行為,其中包括 SACL 審計建議、Sysmon 配置設置、Sigma 規則和 Yara 規則。可以應用相同的方法來檢測 [COMPLUS_]NGenAssemblyUsageLog 配置旋鈕修改。(復制的)建議摘要包括以下內容:
尋找在
HKCU\Software\Microsoft\.NETFramework
HKLM\Software\Microsoft\.NETFramework
注冊表項中添加NGenAssemblyUsageLog字符串。正如 Roberto 所指出的,當啟用審計對象訪問策略并且審計目標鍵的鍵寫入/設置值事件時,會生成事件 ID 4657:

在系統環境變量中尋找 COMPlus_的前綴,以及適用的臨時環境變量——例如進程命令行、轉錄日志等。
監控過程模塊篡改。在大多數組織中,監視“可疑”進程終止事件可能并不實用。但是,從正在運行的進程中卸載 DLL 可能是一個有趣的檢測機會。正如 spotheplanet ( @spotheplanet ) 在這篇文章中所描述的,可以使用 ETW Microsoft-Windows-Kernel-Process提供程序來跟蹤模塊卸載。