我于2026年3月7日向微软报告了该问题。然而,大约20天前,游戏破解社区已经逆向并公开讨论了它。就我而言,我并未关注他们的项目,对此毫不知情。很明显,这是两个完全独立且不相关的项目。
既然 Defender 已经留了一把刀在厨房,何必自备刀具?
KslDump 仅使用微软签名的组件从 PPL 保护的 LSASS 中提取凭据。无需部署漏洞,无需加载驱动程序。整个攻击链随 Windows Defender 预装。微软通过将 MmCopyMemory 置零修补了当前运行版本(wd\KslD.sys),但将旧的可利用版本(drivers\KslD.sys)留在了磁盘上。攻击者无需携带任何东西——只需将服务指向微软忘记清理的旧版本即可。
https://github.com/user-attachments/assets/78dfce25-56b3-4eb8-9de2-7c183601e597
KslD.sys 是微软 Defender 附带的内核驱动程序。它由微软签名,作为受信任的内核模块加载,并暴露了一个用户态可访问的设备对象 \\.\KslD。
该驱动程序接受 IOCTL 0x222044,其中包含多个子命令,为任何能打开设备句柄的进程提供不受限制的内核和物理内存访问。
| 子命令 | 能力 | 影响 |
|---|---|---|
| 2 | 将 CR3、IDTR 及其他 CPU 控制寄存器返回给用户态 | 即时绕过 KASLR |
| 12 | 调用 MmCopyMemory(),地址和大小由攻击者控制 | 任意内核/物理内存读取 |
访问设备句柄的唯一关卡是存储在驱动程序服务项注册表键(AllowedProcessName)中的进程名字符串。该值:
差别仅在于 CCommand::Initialize 中的一行代码:
// 82 KB 版本(已修补)—— 故意清除指针:
v3 = MmGetSystemRoutineAddress(L"MmCopyMemory");
if (v3 >= 0) {
*(a1 + 24) = 0; // ← 置为 NULL —— 子命令 12 失效
}
// 333 KB 版本(可利用)—— 存储指针:
ptr = MmGetSystemRoutineAddress(L"MmCopyMemory");
if (ptr) {
*(this + 0x18) = ptr; // ← 保留指针 —— 子命令 12 可用
}
Defender 平台更新似乎将修补后的 82 KB 版本放入了 drivers\wd\ 并将 ImagePath 指向它,而旧的 333 KB 版本仍保留在 drivers\ 中。在测试系统上,旧二进制文件从未被移除。利用方法只需将 ImagePath 切换回可利用版本并重启服务。两个二进制文件均由微软签名且被操作系统信任。
微软的公开文档显示,KB4052623 提供了 Defender 平台更新,包括将 Defender 驱动程序移至 System32\drivers\wd 的历史性改动,同时 Windows 服务通过 NTFS 硬链接保留 WinSxS 支持的组件存储文件,并仅在清理时移除被取代的组件版本。在测试系统上,这解释了为何较新的 82 KB KslD.sys 可通过 Defender 平台更新路径到达,而较旧的 333 KB System32\drivers\KslD.sys 仍作为当前 CBS 支持的组件存储副本存在,直到被更新的 CBS 版本取代。
微软维护着一个易受攻击驱动程序黑名单(DriverSiPolicy.p7b),专门用于防止 BYOVD 攻击。该黑名单通过 HVCI 强制执行,阻止已知易受攻击的已签名驱动程序加载。
根据微软自己的文档:
“易受攻击驱动程序黑名单旨在帮助强化系统,防止非微软开发的驱动程序在整个 Windows 生态系统中加载”
微软自己的驱动程序被设计排除在黑名单之外。
根本原因很简单:MmCopyMemory 不尊重 PPL。
PPL(保护进程轻量级)旨在通过阻止对 LSASS 的 OpenProcess 和 ReadProcessMemory 调用来防止凭据窃取。但 PPL 仅保护用户态 API 路径。它对内核态物理内存读取没有管辖权。
KslD.sys 为用户态代码提供了直达 MmCopyMemory() 的路径——这是微软自己用于按物理或虚拟地址复制内存的内核 API。该驱动程序执行:
结果:一个微软签名的驱动程序开箱即用地提供了完整的 PPL 绕过。
该漏洞的核心是子命令 12——一个不受限制的 MmCopyMemory() 封装:
IOCTL: 0x222044
输入: struct {
DWORD SubCmd; // 12
DWORD Reserved; // 0
QWORD Address; // 目标 VA 或 PA
QWORD Size; // 读取字节数
DWORD Flags; // 1 = 物理, 2 = 虚拟
DWORD Padding;
}
输出: 原始内存内容(最多 Size 字节)
物理读取(Flags = 1)是关键原语。物理内存访问不受进程保护级别、EPROCESS 标志或任何用户态 API 限制的约束——这正是绕过 PPL 的方式。
虚拟读取(Flags = 2)直接读取内核虚拟地址,适用于遍历内核结构(EPROCESS、ntoskrnl 导出)而无需手动页表转换。
┌──────────────────────────────────────────────────────────────────┐
│ KslDump 攻击流程 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 1. 注册表编辑 ImagePath ← 可利用的 333KB KslD.sys *│
│ │ AllowedProcessName ← 我们的进程 │
│ │ sc stop/start KslD │
│ ▼ │
│ 2. KASLR 绕过 子命令 2 → CR3 + IDTR │
│ │ IDT → 最低 ISR → ntoskrnl 基址 │
│ ▼ │
│ 3. 内核遍历 PsInitialSystemProcess → SYSTEM EPROC │
│ │ ActiveProcessLinks → 查找 lsass.exe │
│ │ 从 EPROCESS+0x28 读取 lsass DTB │
│ ▼ (全部通过子命令12, flags=2) │
│ │
│ 4. 物理读取 使用 lsass DTB 进行页表遍历 │
│ │ MmCopyMemory() 读取 lsass 页面 │
│ │ *** 绕过 PPL *** │
│ ▼ (子命令12, flags=1) │
│ │
│ 5. 密钥提取 通过 PEB → LDR 查找 lsasrv.dll │
│ │ 扫描 .text 查找 LSA 密钥签名 │
│ │ 跟踪 BCRYPT 链 → AES + 3DES + IV │
│ ▼ │
│ 6. 凭据导出 遍历 LogonSessionList │
│ 解密 MSV1_0 凭据 │
│ → NT 哈希 │
│ │
└──────────────────────────────────────────────────────────────────┘
cryptography 包(pip install cryptography)C:\Windows\System32\drivers\KslD.sys)该攻击无需第三方驱动程序、无需未签名代码、无需漏洞。一切都是微软签名、微软交付且已存在于系统上的。易受攻击的驱动程序就躺在它自己的补丁旁边,被排除在本应防止此类攻击的黑名单之外。
此漏洞已向微软安全响应中心(MSRC)报告。他们以以下理由将其关闭为**「不是漏洞」**:
“所描述的攻击依赖于预先存在的管理员权限。未提供证据表明这些权限是如何获得的。假设攻击者已具有管理员或root权限但并未展示获取这些权限的漏洞的报告被视为影响较低,因为具有此类权限的攻击者已经可以执行更严重的操作。”
未分配CVE编号。未发布修复补丁。
本工具仅供授权安全测试和研究目的使用。请仅在你拥有所有权或获得明确书面许可的系统上使用。未经授权访问计算机系统是非法的。作者对滥用行为不承担任何责任。