深入剖析 Sekurlsa::Wdigest:原理、利用与检测
1. 核心概念与背景
WDigest 是 Windows 身份验证体系中的一个安全支持提供者 (SSP),其职责之一是在用户交互式登录(如控制台登录、runas 命令、锁屏登录)后,于内存中缓存用户的明文凭证。
自 Windows Server 2008 R2 / Windows 8.1 起,微软默认禁用了 WDigest 的明文缓存功能。这是通过一个名为 UseLogonCredential 的注册表项控制的。当其值为 0(默认值)时,系统将不再在内存中存储可用密钥解密的明文密码,导致传统工具如 Mimikatz 的 sekurlsa::wdigest 或 sekurlsa::logonpasswords 命令抓取到的密码字段显示为 (null)。
尽管如此,深入理解其内部机制,我们仍可以强制系统重新启用缓存或直接提取密钥进行解密。
2. WDigest 凭证缓存机制详解
2.1 缓存触发与存储
-
触发函数 (
SpAcceptCredentials)
当用户进行交互式登录时,认证过程最终会调用wdigest.dll中的SpAcceptCredentials函数。这是凭证被处理并决定是否缓存的入口点。 -
存储链表 (
l_LogSessList)
WDigest 将登录会话信息(包括用户名、域名和加密后的密码)存储在一个全局链表结构中。该链表在内存中的符号名为wdigest!l_LogSessList。Mimikatz 等工具的核心就是定位并遍历此链表。 -
密码加密函数 (
LogSessHandlerPasswdSet)
在SpAcceptCredentials内部,会调用LogSessHandlerPasswdSet函数。此函数负责获取明文密码并使用系统密钥对其进行加密,然后将加密后的密文存入会话结构体中。
2.2 加密算法与密钥
密码的加密操作并非在 wdigest.dll 内完成,而是通过调用 lsasrv.dll 中的 LsaEncryptMemory 函数实现的。
- 加密算法选择:系统会根据待加密数据(密码)缓冲区的长度动态选择算法:
- 如果长度能被 8 整除,使用 AES 加密。
- 否则,使用 3DES 加密。
- 加密密钥:用于加密的密钥在 LSASS 进程启动时由
lsasrv!LsaInitializeProtectedMemory函数初始化,并作为全局变量存储在lsasrv.dll的内存空间中。共有三个关键组件:hAesKey: BCrypt 格式的 AES 密钥句柄。h3DesKey: BCrypt 格式的 3DES 密钥句柄。Initialization Vector (IV): 初始化向量。
这些密钥是 系统级 的,在同一台机器上的所有用户和登录会话中都是固定的。
3. 密钥提取技术 (Grab Key)
要在默认设置下获取明文密码,首先需要提取出加密密钥。
3.1 密钥结构分析
密钥在内存中以特定的结构体形式存在,可以通过搜索特征码 (Signature) 或模式 (Pattern) 来定位。
-
3DES 密钥 (
h3DesKey):- 它是一个
_KIWI_BCRYPT_KEY81结构体。 - 通常包含一个可识别的标签 (Tag),例如
MSSK。 - 实际密钥数据 (
hardkey) 位于结构体中的特定偏移处,前 4 个字节指明了密钥的长度(例如0x18字节)。
- 它是一个
-
AES 密钥 (
hAesKey) 和 IV:- 它们通常存储在内存中相邻近的位置。
- 可以通过找到
hAesKey后,在其附近搜索特定字节模式来定位IV。 - 注意:不同版本的 Windows(如 Win10, Server 2016, Win11)中,这些模式的内存偏移可能发生变化。硬编码偏移(如 XPN 代码中的
IV_OFFSET)在跨系统时可能失效,需要进行动态模式匹配或偏移计算。
3.2 提取流程
- 获取 LSASS 进程的内存读写权限。
- 在
lsasrv.dll的内存区域中搜索特征码,定位hAesKey和h3DesKey。 - 根据找到的密钥结构,解析并读取实际的密钥字节数据。
- 通过相对偏移或模式匹配,在
hAesKey附近找到IV。
改进方法:相较于完全硬编码,更可靠的方法是扫描内存,寻找密钥结构固有的字节模式或标签,从而动态确定偏移量,增强代码在不同系统版本间的兼容性。
4. 强制启用明文缓存 (Bypass via g_fParameter_UseLogonCredential)
除了提取密钥解密,另一种更直接的方法是强制系统重新启用明文缓存。
4.1 关键控制变量
在 wdigest.dll 中,存在一个关键的全局布尔变量:g_fParameter_UseLogonCredential。它直接决定了 SpAcceptCredentials 函数的行为:
0: 禁用缓存明文凭证(默认)。1: 启用缓存明文凭证。
4.2 绕过原理
在 SpAcceptCredentials 函数中,存在一个逻辑判断(变量名可能逆向不同,如 v11):
- 如果
g_fParameter_UseLogonCredential为0且 系统未启用 CredGuard,则一个内部标志会被设置为“不保存密码”。 - 后续会调用
LogSessHandlerNoPasswordInsert之类的函数,跳过密码保存步骤。
利用方法:直接修改 LSASS 进程中 wdigest.dll 镜像内的 g_fParameter_UseLogonCredential 变量值,将其从 0 改为 1。
4.3 操作与效果
- 定位变量:通过在
SpAcceptCredentials函数中逆向分析,或直接在内核中搜索wdigest.dll的特定字节模式,找到g_fParameter_UseLogonCredential在内存中的地址。 - 修改变量:向该内存地址写入值
1。 - 触发认证:必须让目标用户进行一次新的交互式登录(例如,使用
runas命令切换用户、锁屏后重新登录、或通过计划任务触发一次登录)。仅修改变量而无需新登录,无法获取现有登录会话的密码。 - 抓取密码:登录成功后,新的登录会话凭证将以明文形式缓存于
l_LogSessList中,此时再使用 Mimikatz 即可直接抓取到明文密码。
此方法效果等同于设置注册表 UseLogonCredential 为 1 并重启,但通过内存修补实现了无痕、即时生效的绕过。
5. 检测与防御
5.1 攻击检测
-
LSASS 内存访问监控:
- 监控任何非系统组件对 LSASS 进程的
PROCESS_VM_READ操作(尝试读取内存提取密钥)。 - 监控任何非系统组件对 LSASS 进程的
PROCESS_VM_WRITE | PROCESS_VM_OPERATION操作(尝试修改g_fParameter_UseLogonCredential等变量)。 - 使用 Sysmon (Event ID 10) 或 EDR 功能记录可疑进程对 LSASS 的访问。
- 监控任何非系统组件对 LSASS 进程的
-
SSP 加载监控:
- 监控
lsass.exe加载的异常 DLL(恶意的 SSP DLL)。 - 检查注册表
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa下的Security Packages值,防止恶意 SSP 被安装。
- 监控
5.2 防御建议
- 启用 Credential Guard:这是最有效的防御措施。它利用虚拟化安全(Virtualization-Based Security, VBS)将密钥和哈希隔离到安全内核中,使得传统方法无法从 LSASS 进程内存中读取到这些敏感信息。
- 限制 LSASS 访问权限:使用操作系统自带策略或 EDR 工具,严格限制用户级程序调试和读取 LSASS 内存的能力。
- 保持系统更新:微软会持续通过更新修补这些技术利用所依赖的内部函数和结构。
- 强制使用高强度认证:使用智能卡等硬件认证方式,避免在内存中缓存密码哈希或明文。
6. 参考资源
- XPN 的博客与代码:Exploring Mimikatz - Part 1 及其附带的 WDigest 提取代码 是学习此技术的经典资料。
- 改进代码示例:本文提到的改进代码可参考 Cen4enCen/WdigestGrab。