深入Sekurlsa::Widgest
字数 3688 2025-09-23 19:27:46

深入剖析 Sekurlsa::Wdigest:原理、利用与检测

1. 核心概念与背景

WDigest 是 Windows 身份验证体系中的一个安全支持提供者 (SSP),其职责之一是在用户交互式登录(如控制台登录、runas 命令、锁屏登录)后,于内存中缓存用户的明文凭证。

Windows Server 2008 R2 / Windows 8.1 起,微软默认禁用了 WDigest 的明文缓存功能。这是通过一个名为 UseLogonCredential 的注册表项控制的。当其值为 0(默认值)时,系统将不再在内存中存储可用密钥解密的明文密码,导致传统工具如 Mimikatz 的 sekurlsa::wdigestsekurlsa::logonpasswords 命令抓取到的密码字段显示为 (null)

尽管如此,深入理解其内部机制,我们仍可以强制系统重新启用缓存或直接提取密钥进行解密。

2. WDigest 凭证缓存机制详解

2.1 缓存触发与存储

  1. 触发函数 (SpAcceptCredentials)
    当用户进行交互式登录时,认证过程最终会调用 wdigest.dll 中的 SpAcceptCredentials 函数。这是凭证被处理并决定是否缓存的入口点。

  2. 存储链表 (l_LogSessList)
    WDigest 将登录会话信息(包括用户名、域名和加密后的密码)存储在一个全局链表结构中。该链表在内存中的符号名为 wdigest!l_LogSessList。Mimikatz 等工具的核心就是定位并遍历此链表。

  3. 密码加密函数 (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 提取流程

  1. 获取 LSASS 进程的内存读写权限。
  2. lsasrv.dll 的内存区域中搜索特征码,定位 hAesKeyh3DesKey
  3. 根据找到的密钥结构,解析并读取实际的密钥字节数据。
  4. 通过相对偏移或模式匹配,在 hAesKey 附近找到 IV

改进方法:相较于完全硬编码,更可靠的方法是扫描内存,寻找密钥结构固有的字节模式或标签,从而动态确定偏移量,增强代码在不同系统版本间的兼容性。

4. 强制启用明文缓存 (Bypass via g_fParameter_UseLogonCredential)

除了提取密钥解密,另一种更直接的方法是强制系统重新启用明文缓存

4.1 关键控制变量

wdigest.dll 中,存在一个关键的全局布尔变量:g_fParameter_UseLogonCredential。它直接决定了 SpAcceptCredentials 函数的行为:

  • 0: 禁用缓存明文凭证(默认)。
  • 1: 启用缓存明文凭证。

4.2 绕过原理

SpAcceptCredentials 函数中,存在一个逻辑判断(变量名可能逆向不同,如 v11):

  1. 如果 g_fParameter_UseLogonCredential0 系统未启用 CredGuard,则一个内部标志会被设置为“不保存密码”。
  2. 后续会调用 LogSessHandlerNoPasswordInsert 之类的函数,跳过密码保存步骤。

利用方法:直接修改 LSASS 进程中 wdigest.dll 镜像内的 g_fParameter_UseLogonCredential 变量值,将其从 0 改为 1

4.3 操作与效果

  1. 定位变量:通过在 SpAcceptCredentials 函数中逆向分析,或直接在内核中搜索 wdigest.dll 的特定字节模式,找到 g_fParameter_UseLogonCredential 在内存中的地址。
  2. 修改变量:向该内存地址写入值 1
  3. 触发认证必须让目标用户进行一次新的交互式登录(例如,使用 runas 命令切换用户、锁屏后重新登录、或通过计划任务触发一次登录)。仅修改变量而无需新登录,无法获取现有登录会话的密码
  4. 抓取密码:登录成功后,新的登录会话凭证将以明文形式缓存于 l_LogSessList 中,此时再使用 Mimikatz 即可直接抓取到明文密码。

此方法效果等同于设置注册表 UseLogonCredential1 并重启,但通过内存修补实现了无痕、即时生效的绕过。

5. 检测与防御

5.1 攻击检测

  1. LSASS 内存访问监控

    • 监控任何非系统组件对 LSASS 进程的 PROCESS_VM_READ 操作(尝试读取内存提取密钥)。
    • 监控任何非系统组件对 LSASS 进程的 PROCESS_VM_WRITE | PROCESS_VM_OPERATION 操作(尝试修改 g_fParameter_UseLogonCredential 等变量)。
    • 使用 Sysmon (Event ID 10) 或 EDR 功能记录可疑进程对 LSASS 的访问。
  2. SSP 加载监控

    • 监控 lsass.exe 加载的异常 DLL(恶意的 SSP DLL)。
    • 检查注册表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa 下的 Security Packages 值,防止恶意 SSP 被安装。

5.2 防御建议

  1. 启用 Credential Guard:这是最有效的防御措施。它利用虚拟化安全(Virtualization-Based Security, VBS)将密钥和哈希隔离到安全内核中,使得传统方法无法从 LSASS 进程内存中读取到这些敏感信息。
  2. 限制 LSASS 访问权限:使用操作系统自带策略或 EDR 工具,严格限制用户级程序调试和读取 LSASS 内存的能力。
  3. 保持系统更新:微软会持续通过更新修补这些技术利用所依赖的内部函数和结构。
  4. 强制使用高强度认证:使用智能卡等硬件认证方式,避免在内存中缓存密码哈希或明文。

6. 参考资源


深入剖析 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 的访问。 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 。