在 Windows 系统的安全防护体系中,驱动程序强制签名(Driver Signature Enforcement,DSE)是一项重要的安全机制,它用于防止攻击者将未签名的驱动程序加载到内核中,从而保护内核免受恶意代码的攻击。然而,随着攻击者技术的不断发展,针对 DSE 的绕过技术也逐渐出现。同时,微软推出的基于虚拟化的安全(Virtualization-Based Security,VBS)技术试图加强对内核的保护,但在未结合管理程序代码完整性(Hypervisor-Protected Code Integrity,HVCI)的情况下,仍然存在被绕过的可能。本文将详细介绍在没有开启 HVCI 的 VBS 环境下进行 DSE 绕过。在高版本的 Windows 系统中,DSE 由 ci.dll 内核模块实现,该模块中存在一个名为g_CiOptions的全局变量,将该变量设置为0可以完全禁用 DSE。但在 Windows 10 引入 VBS 机制后,这种直接修改变量的方法失效了,于是我们就看到了下面这一幕:VBS 是 Windows 10 和 11 系统中默认启用的安全机制,它提供了一个受管理程序保护的环境,运行第二个 “安全内核”,传统的 Ring-0 级别的内核无法触及该环境。VBS 通过内核数据保护(Kernel Data Protection,KDP)技术来保护g_CiOptions变量。我们使用 Ghidra 导入 ci.dll,找到CipInitialize函数,我们可以看到有许多函数回调例程,我们点击CiInitializePolicy如图所示我们在CiInitializePolicy里一直向下翻找,可以看到g_CiOptions全局变量的地址传给了MmProtectDriverSection函数,如图所示该函数的具体用法可以参考 MSDN,下面我就简单的列一下函数原型该函数使用 VSM 管理的 SLAT 表(二级地址转换)保护内核中数据段的物理内存。而参数AddressWithinSection是指向要保护有效数据的内存地址,且在受保护期间,无法删除此保护。要绕过 DSE,我们需要找到合适的位置进行补丁,我们可以在 CI!CipImageGetImageHash处下个断点,然后加载驱动,输出如下:从调试结果可知,加载任意驱动程序后,系统会进入 ci.dll 并调用 CiValidateImageHeader函数执行签名验证。因此,我们可在 CiValidateImageHeader函数起始位置写入一条机器指令,使其直接返回STATUS_SUCCESS,以此绕过签名验证流程。需注意的是,内核空间中无法直接向可执行内存写入数据,需先修改虚拟地址对应的页表项(PTE),将目标内存区域设置为可写状态,再执行代码热修补操作。通过 Ghidra 对 RTCore64.sys 漏洞驱动进行逆向分析,可提取出其内核内存读、写接口,如下图所示:相关接口定义如下:调用漏洞驱动读写接口的实现代码基于上述接口,可编写如下代码调用漏洞驱动的内核内存读、写功能:寻找CiValidateImageHeader函数的地址由于 Windows 的版本差异,CiValidateImageHeader函数的特征码可能不同,我们需要找一个相对稳定的特征码和相对稳定的搜索模式进行匹配。在这期间,我花费了大量时间去分析不同版本 Windows 系统中的 ci.dll 文件,尝试了多种特征码组合,最终才找到一个相对稳定的特征码和匹配模式。具体实现代码如下:该函数的工作原理如下:通过修改 PTE 项进行热修补操作找到 CiValidateImageHeader 函数后,我们需要修改其对应的页表项(PTE)权限,然后实施热修补。这是整个绕过过程的核心步骤,具体实现如下:获取 MiGetPteAddress 函数信息我们需要反汇编 MiGetPteAddress 函数逻辑来找到计算参数和 PTE 基址。从之前的分析中,我们知道该函数的汇编代码如下:从代码中可以看到:在我们的实现中,通过以下代码获取这些关键信息:通过反汇编分析,我们可以准确提取出计算 PTE 地址所需的参数,这是实现内存权限修改的关键步骤。计算 CiValidateImageHeader 函数的 PTE 地址可以借助 WinDbg 的!pte [Address]指令来确定 PTE 地址是否正确。修改 PTE 权限实施热修补写完要马上恢复原始状态,否则会触发 PatchGuard 蓝屏最终效果成功执行代码后,系统的DSE保护机制将被临时绕过,此时可以加载未签名的驱动程序。执行效果截图如下:防护策略HVCI 通过 Hypervisor 利用 SLAT/EPT/NPT 提供的硬件虚拟化能力,在比普通内核更高的特权级别(Ring -1) 上管控内存页权限,普通内核态代码无法修改 Hypervisor 维护的页表。在启用HVCI的环境下可执行的内存页会被标记为只读且可执行(禁止写入),可写入的内存页(如数据区)会被标记为可写但不可执行。当我们尝试在启用 HVCI 的 VBS 环境下使用这种方法绕过 DSE,就会发现:在启用VBS(基于虚拟化的安全)但未开启HVCI(管理程序代码完整性)的环境下,通过代码热修补技术可绕过DSE(驱动签名强制)。在VBS环境下,g_CiOptions全局变量被内核数据保护(KDP)技术保护,无法直接修改。因此,我们采用了一种间接的方法:通过修改CiValidateImageHeader函数,使其在执行签名验证前直接返回STATUS_SUCCESS,从而绕过整个签名验证流程。漏洞驱动利用:利用RTCore64.sys漏洞驱动实现对内核内存的读写操作,这是整个绕过过程的基础。内存映射:通过映射内核镜像和ci.dll镜像到用户空间,便于分析和定位关键函数。特征码搜索:使用特征码搜索技术定位MiGetPteAddress和CiValidateImageHeader等关键函数,提高了代码在不同Windows版本上的兼容性。PTE操作:通过分析MiGetPteAddress函数,提取计算PTE地址所需的参数和基址,实现对页表项的精准定位和权限修改。热修补技术:在修改页表项权限后,对CiValidateImageHeader函数进行热修补,写入xor rax, rax; ret指令使其直接返回STATUS_SUCCESS。HVCI限制:此方法仅在未开启HVCI的VBS环境下有效。当HVCI开启时,内核代码会被进一步保护,无法通过此方法修改。蓝屏风险:在修改页表项权限后,需要在用户空间等待用户输入后恢复原始值,否则会导致 PatchGuard 蓝屏。NTSTATUS MmProtectDriverSection(
[in] PVOID AddressWithinSection,
[in] SIZE_T Size,
[in] ULONG Flags
0: kd> bu CI!CipImageGetImageHash
0: kd> g
Breakpoint 0 hit
CI!CipImageGetImageHash:
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-290702.htm