Domain Hashes Live in Memory
Local account hashes sit in the SAM. Domain account hashes don’t, they get cached in LSASS memory when a domain user logs in.
That used to be a gift. Mimikatz’s whole sekurlsa::logonpasswords trick is just reading LSASS memory. As long as you’re SYSTEM, you can read any process’s memory, including LSASS, so domain hashes were free for the taking.
If you get domain creds out of LSASS, you can crack them or Pass-the-Hash with them, exactly like local ones. That’s a huge prize, which is why Microsoft built a wall around it.
The Problem: Hiding Memory from SYSTEM
Microsoft’s goal: make LSASS memory unreadable even to SYSTEM.
That sounds impossible. SYSTEM is the top of the normal privilege ladder; it can read anything in the operating system. So how do you hide memory from the kernel itself?
You go below it.
Virtualization-Based Security
Modern CPUs have hardware virtualization. Microsoft uses it to run a thin hypervisor beneath Windows, and splits the machine into two isolated worlds called Virtual Trust Levels:
- VTL0, the normal world: your regular Windows, the kernel, your processes, and an attacker who got SYSTEM. Everything you normally touch.
- VTL1, a separate locked secure world (Virtual Secure Mode): higher-privileged than even the VTL0 kernel, because the hypervisor enforces the boundary.
The mental model: even if you become king of VTL0 (SYSTEM, kernel-level), VTL1 is a different building you don’t have a key to. The hypervisor holds the only key.
What Credential Guard Does
With Credential Guard enabled, the secret-holding part of LSASS moves into an isolated trustlet in VTL1 called LSAISO (LSA Isolated). The normal LSASS.exe stays in VTL0 but no longer holds the hashes, it just talks to LSAISO across the boundary through a controlled channel.
The result: you get SYSTEM, you point Mimikatz at LSASS, and the cached domain hashes aren’t there. They’re in VTL1, which you can’t reach.
Two Things You Must Remember
| It only protects domain (non-local) credentials | Local accounts still hash into the SAM, so local NTLM hashes are still extractable. Credential Guard doesn’t touch them. |
| It’s a wall, but a specific one | It stops you reading stored domain secrets. It does nothing about credentials that haven’t been stored yet. |
That second point is the crack in the armor.
Beating It: Intercept, Don’t Dump
You can’t read the stored secret. So change the question: instead of “how do I dump a credential that’s already there,” ask “how do I catch one as it’s being typed in?”
When any user logs in, their plaintext password flows through Windows’ authentication plumbing, the SSPI (Security Support Provider Interface). The actual protocols (Kerberos, NTLM) are pluggable DLLs called SSPs that SSPI hands the credentials to.
Here’s the gap: SSPs receive the password in plaintext. They have to, they need it to authenticate. So if you register your own malicious SSP, every login hands you the plaintext.
Mimikatz does exactly this, in memory, no DLL dropped to disk:
mimikatz # misc::memssp From then on, every user who logs in (you wait, or coerce one with social engineering) has their cleartext password written to C:\Windows\System32\mimilsa.log.
Credential Guard locks the vault where stored secrets sit. SSP injection ignores the vault and pickpockets the password on its way in. No hash, no cracking, plaintext.
The Whole Arc
Step back and the entire Windows-credential story is one decision tree:
- Stored NTLM hash (local) → crack it, or just pass it
- Net-NTLMv2 hash (network, one-time) → can’t pass it, so crack or relay
- Domain hashes (LSASS memory) → Mimikatz reads them… unless Credential Guard isolates them in VTL1
- Credential Guard → can’t dump the secret, so inject an SSP and catch the plaintext at login
Each defense closes one door. Each attack walks through another. The credential is always somewhere, stored, in transit, or being typed, and somewhere is always reachable.