As part of my "coming back to Windows security" phase, I've decided to write a short blogpost about the significance and methodology of dumping lsass.
While these are not novel techniques, I think summarizing ideas here would make sense, especially if you're stuck at 2-3 methods that are all monitored.
The Local Security Authority Subsystem Service (LSASS) is a critical Windows process (lsass.exe) responsible for enforcing the system's security policy.
It handles authentication, password changes, user logins, token creation, and more, runs as SYSTEM and is tightly integrated into Windows’ trust model.
In lsass.exe memory, you can find authentication credentials for users who have logged into the system.
Those includes NTLM hashes, Kerberos tickets (TGTs, service tickets), and plaintext credentials (in some cases).
That makes lsass act as a treasure trove for lateral movement and privilege escalation.
Stealing credentials from LSASS allows attackers to:
- Perform
pass-the-hash (PtH)orpass-the-ticket (PtT). - Access network resources without cracking passwords.
- Move laterally without triggering brute-force alarms.
There are a few mitigations against dumping lsass memory, but for now - let's discuss a vanilla OS with no mitigations in place.
We will therefore decibe naive ways of dumping lsass.
Generally speaking, dumping lsass requires two things:
- Getting a process
HANDLE, e.g. via a call to the OpenProcess API. - Reading the process memory.
In some cases, we might be "lazy" and use other utilities (mostly in the forms of executables or libraries) that'd do the work for us.
While that is probably the least stelthy technique out there, it's quite effective.
The Windows Task Manager (taskmgr.exe) is a GUI application that allows one to select a process and naively dump it:

The good thing here is that taskmgr.exe already runs on a target box ("living-off-the-land"), as well as doing everything for us - it opens a handle to lsass.exe and dumps its contents.
The bad news is that it's a GUI application, but it could be automated:
- Save the current foreground window for restoration.
- Run
taskmgr.exeand get its process ID (available from a call to CreateProcessW API. - Wait a bit and find the Task Manager window via the FindWindowW API. Interestingly, it's quite easy to find since that Window has a class name "TaskManagerWindow".
- Send keystrokes to the Task Manager window via the SendInput API. We can inject the keystrokes that spell out "Local Security Authority", followed by the special "menu" keyboard input (
VK_APPS), and then the letter "C", which will dump lsass. - Use the EnumWindows API to get the window that shows the filename of the dump file and fetch its contents. Fetching the contents themselves could be dona via the SendMessageW API with the Windows Message
WM_GETTEXT. - Kill
taskmgr.exeand restore the foreground window from step #1. - Use the dump file and delete after use.
This might look similar to an AutoHotkey implementation - but this is very stitched to lsass dumping.
Moving forward, the DLL comsvcs.dll exposes a rundll32 interface for its MiniDump export.
It's as simple as running a commandline:
"%WINDIR%\System32\rundll32.exe" "%WINDIR%\System32\comsvcs.dll" MiniDump [PID] [PATH] full
Where PID is the lsass.exe process ID and PATH is a placeholder for the dump path.
One annoying thing I discovered was that you cannot quote the PATH placeholder, so it cannot contain spaces.
This minor annoyance could be avoided by converting the path we want to a Short Path - via the GetShortPathNameW API.
Another simple technique that requires a child process is using ProcDump from the SysInternals suite.
One can download it directly (from https://download.sysinternals.com/files/Procdump.zip) or dump and and run it.
I mention procdump specifically because it's used by threat actors, as well as very prevalent.
This technique mainly focuses on the MiniDumpWriteDump API.
Up till this point, we used child processes, but now we're going to use our own code to perform lsass.exe dumping.
To use the MiniDumpWriteDump API, we need a HANDLE to lsass.exe.
Assuming we got such a handle, we do the following:
- Create a new file and get its handle.
- Invoke the MiniDumpWriteDump API on the
lsass.exehandle and the file handle.
However, there is a variant that doesn't require writing to disk at all - we can save the entire dump to a memory buffer!
To achieve that, we:
- Create a buffer with some initial size.
- Create a structure of type
MINIDUMP_CALLBACK_INFORMATIONand specify a callback function (and context). - Invoke the MiniDumpWriteDump API on the
lsass.exehandle and the callback pointer. - The callback accumulates the data from the minidump argument upon receivng a
IoWriteAllCallbackcallback type. Ths might require us to dynamically enlarge the buffer with theHeapReAllocAPI.
Now, how do we get a handle to lsass.exe to begin with?
There are two variants that we will consider for now:
This is the most direct and "normal" way of fetching the lsass.exe process handle:
- Finding the process ID of
lsass.exewith CreateToolhelp32Snapshot API, as well as Process32FirstW and Process32NextW API calls. - Use the OpenProcess API. For doing a Minidump, we will require the
PROCESS_QUERY_INFORMATIONandPROCESS_VM_READaccess flags.
Since OpenProcess is usually heavily monitored by security products, there is a sneakier option - duplicating an existing handle to lsass.exe some other process might have.
Similarly to the OpenProcess approach - we fetch the lsass.exe PID the usual way (CreateToolhelp32Snapshot and friends).
However, here our approach would be finding an existing handle to lsass.exe with sufficient access flags:
- We call ntdll!NtQuerySystemInformation with the information class
SystemHandleInformation(defined as 16) with a sufficiently large buffer. As of now, that information class is officially undocumented (but can be found here). - We treat the buffer as a SYSTEM_HANDLE_INFORMATION pointer (again officially undocumented).
- For each handle, we skip if it belongs to the
lsass.exeprocess ID itself, to our own process or the SYSTEM process ID (4). - We call
ntdll!NtDuplicateObjectto duplicate the handle. In this call we also specify the required access flags. - We call
ntdll!NtQueryObjectwith typeOBJECT_TYPE_INFORMATION_CLASSto get the duplicated handle type and make sure it's aprocesshandle type. - We call the QueryFullProcessImageNameW API and validate it's indeed
lsass.exe(note it's a full name so extra parsing is required). - If all checks pass - we got a handle to
lsass.exethat we've just duplicated! Otherwise, we clean up used resources and try a different handle.
Note a running OS is not guaranteed to have an open lsass.exe by some process with sufficient access flags - if that's the case we can always revert to running OpenProcess as usual.
However, all the native APIs (and especially the handle duplication) make it way stealtier than calling OpenProcess directly.
Similarly to the Minidump approach, we get a handle to lsass.exe by means of OpenProcess or handle duplication, but this time also with PROCESS_DUP_HANDLE access flag.
Then, we call the PssCaptureSnapshot API which returns us a pseudo-handle of type HPSS.
As the name suggests, that handle captures a "snapshot" of the lsass.exe state (includng memory), and then could be used just like a normal handle to ReadProcessMemory or MiniDumpWriteDump.
The approach doesn't seem advantageous, but it's very reliable due to how it captures a snapshot (as opposed to direct ReadProcessMemory which has an inherent race condition, for example).
This technique relies on a Windows mechanism that can invoke a callback once a process exits, as well as fooling the system into thinking the process indeed exited. To implement this technique, we do the following:
- Get an
lsass.exehandle (either viaOpenProcessor by handle duplication). - Write to the registry under the Image File Execution Options (IFEO) key
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe. Value name isGlobalFlagwhich is of typeDWORD, and we set it to the value of0x200, which corresponds to enabling the Silent Process Exit monitoring feature. - Write to the registry again, this time under
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe, the value is aDWORDwith the nameReportingModeand the value of2, which instructs the OS to perform a full memory dump. - Set the value
LocalDumpFolderunderHKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exeto point to a folder we'd like to dump the memory to, and set the valueDumpType(which is aDWORD) to be2(local memory dump). - Call
ntdll!RtlReportSilentProcessExit, which is an undocumented function that would report that a silent process exit should occur for a given process handle (lsass.exein our case).
At that point, our dump file has been created at the folder we assigned, and we can delete all added registry entries.
This very simple technique basically reads all of lsass.exe memory via the ReadProcessMemory API.
To iterate all memory regions, one can use the VirtualQueryEx API and retrieve a region's size and protections.
Most juicy stuff (credential material) will be in readable-writable (RW) regions, as it's allocated dynamically.
Originally done by Deep Instinct, this method requires running as SYSTEM (can be validated using the GetTokenInformation API if necessary).
Just as before, we fetch a handle to lsass.exe with either OpenProcess or duplication, and then dumps using Windows Error Reporting (WER)!
To do this, we report an exception to WER via ALPC:
- We get some thread of
lsass.exe(can be done with theCreateToolhelp32SnapshotAPI) - and save its thread ID. - We create two events that we will pass to the ALPC message: a "recovery event" and a "completion event".
- We create a memory-mapped file that will contain a structure that'd maintain exception information (requires writable memory). That structure contains information for WER, including the events that we created, as well as exception information, process ID, the failing thread ID and so on.
- We make sure WER service starts using Windows Notification Facility (WNF) using
ntdll!NtUpdateWnfStateDataandntdll!EtwEventWriteNoRegistration. - We wait for WER to be available using
ntdll!NtWaitForSingleObjecton\KernelObjects\SystemErrorPortReady. - We connect to ALPC port
\WindowsErrorReportingServicePortviantdll!NtAlpcConnectPortand then send to message viantdll!NtAlpcSendWaitReceivePort. - The dump will be under
%LocalAppData%\CrashDumps- we take the freshest dump.
Here is a nice summary of the techniques, including pros and cons:
| Method | Doesn't require child process | Doesn't Require further tooling | Can avoid touching disk | Remarks |
|---|---|---|---|---|
| Task manager | ❌ | ✅ | ❌ | Might have reliability issues (GUI) |
| Rundll32-comsvcs minidump | ❌ | ✅ | ❌ | --- |
| Procdump | ❌ | ❌ | ❌ | Might write to registry (EULA) |
| Minidump API | ✅ | ✅ | ✅ | --- |
| PssCaptureSnapshot API | ✅ | ✅ | ✅ | --- |
| Shtinikering | ✅ | ✅ | ❌ | Requires running as SYSTEM |
| SilentProcessExit | ✅ | ✅ | ❌ | Writes to registry |
| Whole memory dump | ✅ | ✅ | ✅ | Might have reliability issues (racy) |
In terms of modern builtin mitigations, two immidiately come to mind:
A mitigation called LSA protection makes lsass.exe a Protected Process Light (PPL). It's configurable via registry: HKLM\SYSTEM\CurrentControlSet\Control\Lsa, value name RunAsPPL of type DWORD - a value of 1 means lsass.exe is PPL.
As a reminder, one cannot get a protected process handle (unless the protection flags for OpenProcess are only to query information) unless the caller is also a protected process.
Thus, calling OpenProcess from a non-protected process context fails. That also means duplicating a handle would not work, as the only processes that will have an lsass.exe handle with sufficient permissions are protected processes too.
LSA protection can be bypassed by turning lsass.exe into non-PPL - from userland it requires a reboot, but a kernel write primitive can simply mark the process as non-PPL (via the EPROCESS structure in the kernel). Indeed a popular approach is bringing a vulnerable driver and running it.
Another option is to get arbitrary code to run as PPL (known as a PPL bypass).
Lastly, booting into recovery \ safe mode works, but that requires a reboot.
Credential Guard is a part of Microsoft's Virtualization Based Security (VBS).
In a nutshell, you can imagine your OS to run side-by-side with another OS that is super hardened (Secure Kernel), with no means of reading memory between the two (similar concept to ARM TrustZone).
Microsoft terminology states that your normal OS runs in Virtual Trust Level (VTL) number 0 - VTL0, and the secure OS runs in VTL1, both are segregated by Hyper-V (the Microsoft hypervisor).
When it comes to Credential Guard, lsass.exe lives in VTL0 and the counterpart in VTL1 is called lsaiso.exe - lsass.exe and lsaiso.exe communicate via ALPC.
NTLM hashes, Kerberos TGTs and domain secrets (such as DPAPI master keys) live in VTL1 and never leave VTL1 memory, thus dumping lsass.exe or reading from its memory directly is meaningless.
Hyper-V vulnerabilities are a possibility but expensive, as well as Bootkits.
A more feasible option would be abusing VTL0 memory - short-term secrets might still exist in the VTL0 lsass.exe memory.
Lastly, downgrade attacks are still possible (disabling VBS or Hyper-V) and require reboots. Those also have other complications as they invalidate Secure Boot.
Other options include other avenues (keylogging etc.) but I classify those as completely seperate techniques for getting credentials.
In this blogpost we've mentioned several popular trends in gaining credentials from lsass.exe.
I've shared pros and cons for each technique, coded them all and then presented modern mitigations and how one might bypass them.
I have not mentioned security solutions, but obviously security solutions monitor lsass.exe dumps quite heavily.
I intend to explain more about other types of credentials you might find on Windows machines - in a future blogpost.
Stay tuned!
Jonathan Bar Or