athenasec16/CVE-2026-29923

GitHub: athenasec16/CVE-2026-29923

该项目针对 pstrip64.sys 驱动程序中的 CVE-2026-29923 漏洞提供了完整的利用分析与本地提权概念验证代码。

Stars: 5 | Forks: 1

# CVE-2026-29923 - 通过 pstrip64.sys 进行本地提权攻击 ## **免责声明:** 本代码仅供教育和防御性研究目的提供。编写本代码旨在加深对内核漏洞利用的理解,并帮助防御者抵御类似漏洞。严禁未经授权、非法或恶意使用本项目。 ### 描述 **哈希值:** ab01485bb7c8bc1a9c86096eeea6d31d8fad557bf4d44072b46373d2203faa6e **驱动名称:** pstrip64.sys **CVE:** CVE-2026-29923 “自带易受攻击驱动程序”(BYOVD)攻击是一种古老但极其有效的方式,攻击者通过使用操作系统仍然官方信任的旧版驱动程序来绕过现代 Windows 安全防护。一旦加载该驱动程序,攻击者就会利用其缺陷,从而弥合标准无权限进程与完全系统级控制之间的差距。 本周早些时候,`pstrip64.sys` 驱动程序中披露了一个新漏洞,被追踪为 `CVE-2026-29923`。这篇博客文章详细分析了漏洞利用的整个生命周期:从最初的漏洞研究和概念验证 的开发,到为保护环境的防御者提供可行的缓解策略。 ## `pstrip64.sys` 驱动程序是一个与 EnTech Taiwan PowerStrip(最高版本 3.90.736)相关的旧版内核模式组件。虽然其合法用途是启用高级显卡显示调整,但其深厚的系统权限使其成为攻击者极具吸引力的目标。 ### 漏洞 当该漏洞首次披露时,我首先分析了其 `DriverEntry` 函数。这是内核驱动程序的主要初始化例程,用于创建 `\Device\PSTRIP64` 设备对象,并通过 `\DosDevices\PSTRIP64` 符号链接将其暴露给用户模式应用程序。更重要的是,它配置了驱动程序的调度表。立即引起我注意的是索引 14 (`IRP_MJ_DEVICE_CONTROL`) 处的条目,它将所有用户提供的 IOCTL 请求直接路由到 `sub_11340` 处理程序函数,这是我们主要关注的区域。 IDA DriverEntry `sub_11340` 函数充当主要的 IOCTL 调度程序,解释来自用户模式的请求。 在所有暴露的 IOCTL 中,`0x80002008` 无疑是最有趣的。虽然默认情况处理轻微的 I/O 端口交互,但 `0x80002008` 充当通往 `sub_11000` 的网关。通过将 `SystemBuffer` 直接传递给该函数。 IDA ioctl 这个 `sub_11000` 例程就是确凿的证据。首先,它使用 `HalTranslateBusAddress` 获取我们提供的用户地址并将其转换为有效的系统物理地址。然后,它打开 `\Device\PhysicalMemory` 并使用 `ZwMapViewOfSection` 进行映射。通过将目标进程句柄硬编码为 `(HANDLE)0xFFFFFFFFFFFFFFFFLL`(代表 `ZwCurrentProcess()`),驱动程序将此物理内存直接映射到我们调用进程的虚拟地址空间中。关键是,它随后将这个新映射的虚拟地址写回 `SystemBuffer` 以返回给用户,正式授予我们的应用程序一个用于读写物理内存的直接指针。 IDA MapViewofSection ## 在完全理解漏洞并建立物理读写原语后,我已经掌握了所需的所有拼图。现在,是时候开始编写 Proof of Concept 了。 ### 概念验证 **注意:** 此 PoC 专门在 `Windows 10 22H2` 环境中开发和测试。由于该漏洞利用依赖于原始物理内存操作,因此内核结构偏移量和物理内存边界目前是针对我的设置硬编码的。要在您自己的机器上测试此漏洞,您必须更新 Windows 内核偏移量并调整物理地址扫描范围,以匹配您的特定操作系统版本和 RAM 配置。 我漏洞利用的第一步是与驱动程序建立通信。我通过在驱动程序的符号链接(`\\.\PSTRIP64`)上调用 `CreateFileA` 来实现这一点。一旦我获得有效的句柄,我就需要一种干净的方式来滥用我之前分析的 `0x80002008` IOCTL。我创建了一个名为 `MapPhysicalMemory()` 的包装函数。此函数使用目标物理地址和我想要读取的内存块长度来填充我的自定义 `PSTRIP_MAP_REQUEST` 结构体。 然后,我通过 `DeviceIoControl` 将此结构体直接发送到驱动程序。如果成功,驱动程序会将该物理内存直接映射到我的用户模式应用程序中,并在 `OutputResult` 字段中返回虚拟基址。现在我可以将此返回的地址转换为标准的 C++ 指针,从而获得对系统物理 RAM 的原始、无权限访问。 在我的物理读/写原语完全运行后,我的目标是找到包含进程权限的内核数据结构。在 Windows 中,每个运行的进程都由一个 `EPROCESS` 结构体表示。 Windows 使用一个称为池标签 的特定 4 字节标识符在内核池中分配 `EPROCESS` 结构体。对于进程,此标签是字符串 `Proc`(十六进制为 `0x636F7250`)。通过扫描系统的物理 RAM,我可以搜索这个确切的字符串。 我的漏洞利用循环遍历从 `0x10000000` 到 `0x140000000` 的物理内存空间,以 2MB 的块(`STEP_SIZE = 0x200000`)映射内存。我将每个映射的块转换为原始字节数组,并以 16 字节为一块(`sizeof(_POOL_HEADER)`)进行扫描。 然而,仅仅在物理内存中找到 `Proc` 标签是不够的。内存是杂乱的,该标签可能是已终止进程的残留伪影,或者只是恰好匹配十六进制值的随机数据。如果我盲目地假设每个 `Proc` 标签都是有效的 `EPROCESS` 结构体并开始修改内存,我将立即导致 BSOD。 为了确保稳定性,我必须使用启发式方法验证该结构体。首先,我计算 `EPROCESS` 结构体的开始位置(该位置距离池标签稍微有点偏移)。从那里,我检查运行进程的一些已知常量: * **PriorityClass:** 我验证此值是否为 `0x2`(正常优先级)。 * **ProcessLock:** 我确保此值为 `0x0`。 * **ImageFileName:** 我检查进程名称的第一个字符是否是有效的、可打印的 ASCII 字符。 如果所有这些启发式检查都通过,我可以非常有信心我正在查看一个有效的、活动的进程。然后,我读取其唯一进程 ID (PID)。如果 PID 与我自己的漏洞利用进程匹配,我将保存其令牌指针的物理地址。如果 PID 是 `4`(Windows `System` 进程),我将提取并保存其高权限令牌的实际值。 最后,我将保存的进程令牌指针的物理地址对齐到最近的 4KB 边界,并最后一次使用 `MapPhysicalMemory()` 仅映射该特定页面。 接下来,我导航到确切的偏移量并用 System 令牌值覆盖我的令牌。瞬间,Windows 内核将我的漏洞利用进程视为 `NT AUTHORITY\SYSTEM`。 ## 取消映射页面以确保系统稳定性后,我只需调用 `CreateProcessA` 来生成 `cmd.exe`。因为我当前的进程已提升,所以新的命令提示符继承了这些顶级权限,成功完成了攻击! ### 注意事项 **注意:** 我在早期调试阶段发现的一个关键细节是驱动程序如何处理映射指针。通过执行 `SystemBuffer->LowPart = (unsigned int)BaseAddress;`,驱动程序在返回之前将 64 位虚拟基址转换为 32 位值。这种截断丢失了地址的高位,导致我在 64 位漏洞利用中尝试取消引用它时立即发生访问冲突。为了干净地绕过此问题,我只是将我的用户模式 PoC 编译为 32 位应用程序,确保返回的指针保持完全有效。 IDA MapViewofSection - Copy **注意:** 在我的初始测试中,遇到了一个有趣的边缘情况:我的 PoC 成功地在内存中定位了我的漏洞利用进程,但未能找到 `System` 进程(`PID 4`)。 为了理解原因,我需要直接检查物理内存。我连接了一个内核调试器 (WinDbg) 并使用命令检索 `System` 进程的虚拟地址和目录基址。然后我使用 `!vtop` 将该虚拟地址转换为 RAM 中的确切物理地址。 windbg kernel debbuger system physical address windbg kernel debbuger db system address 我切换回附加到我的 PoC 的用户模式调试器。我在内存扫描循环上设置了一个条件断点,指示它在 `MapPhysicalMemory()` 函数抓取包含 `System` 进程物理地址的 2MB 块的那一刻暂停执行。 windbg breakpoint 一旦命中断点,我就开始手动检查映射内存的原始字节。正是在这里,我发现了关于 Windows 内核池分配的一个关键细节。 windbg eprocessBase offset 88000 当 Windows 为进程分配内存时,它以 `_POOL_HEADER`(包含我们的 `Proc` 标签)开始,后跟 `_OBJECT_HEADER`,最后是 `EPROCESS` 结构体本身。对于标准用户模式应用程序,这些标头包含额外的跟踪数据,这意味着实际的 `EPROCESS` 结构体在池标签之后开始 `0x80` 字节。 offest for use mode process 然而,检查 `System` 进程的内存揭示了不同的布局。`System` 进程缺少一些标准跟踪标头。从 `Proc` 标签到 `EPROCESS` 结构体开始的偏移量只有 `0x40` 字节! windbg db 88000 offset windbg db 88000 - 0x40 offset offset for system process 修复很简单。我更新了我的 PoC,通过在遇到 `Proc` 标签时循环遍历可能的偏移量数组(`0x40` 和 `0x80`)来处理两种池标头大小。 ## posibileoffset ### 缓解与检测 网络安全是攻击者和防御者之间一场无休止的猫鼠游戏。虽然攻击者不断寻找易受攻击的驱动程序,但现代安全产品和蓝队有多种可靠的方法来检测和阻止这种确切的操作。 阻止 BYOVD(自带易受攻击驱动程序)攻击的最有效方法是首先阻止驱动程序加载。 * 防御者应确保 pstrip64.sys 的哈希值已添加到其阻止列表中。 * 此外,组织应通过 Windows Defender Application Control (WDAC) 强制执行 Microsoft 的易受攻击驱动程序阻止列表,并启用 Hypervisor-Protected Code Integrity (HVCI) 以严格限制可以加载哪些内核组件。 * 监控新服务创建事件,查找意外的内核模式驱动程序安装。 如果驱动程序已加载,安全产品仍然可以在令牌操作阶段检测到漏洞利用。 * 高级监控器监控令牌中的异常。标准用户模式进程在没有合法身份验证链的情况下将其初始主令牌提升到 `NT AUTHORITY\SYSTEM` 是一个巨大的危险信号。 ## * 此外,安全团队可以创建规则,以便在低完整性或中等完整性进程生成高权限子进程(例如 `cmd.exe`)时进行检测,特别是当父进程没有理由以 `SYSTEM` 身份运行时。 ### 演示 ![poc_demo](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/09521e7d0a020029.gif)
标签:0day挖掘, Bring Your Own Vulnerable Driver, Bro, BYOVD, CSV导出, CVE-2026-29923, DriverEntry, IOCTL, IRP_MJ_DEVICE_CONTROL, Linux, POC, PowerStrip, pstrip64.sys, Web报告查看器, Windows驱动, 内核安全, 内核模式, 协议分析, 子域名枚举, 本地提权, 权限提升, 符号链接, 系统安全, 防御研究, 驱动漏洞