Pass-the-Hash: authenticating with an NTLM hash you never cracked
Why an NTLM hash is password-equivalent, how pass-the-hash works with Impacket, NetExec and Mimikatz, and the controls that actually stop lateral movement.
The first time pass-the-hash clicks, it feels like cheating. You dumped a hash off one box, you never cracked it, and yet you are now running commands as that user on a different box. No plaintext anywhere in the chain.
That works because of what an NTLM hash actually is in the protocol. NTLM authentication never sends the password. The client proves knowledge of the password by performing a challenge-response computation keyed on the NT hash. The hash is the secret the protocol cares about. So if you hold the hash, you can complete the handshake. The plaintext is irrelevant to NTLM. That is the whole attack in one sentence: for NTLM auth, the hash is password-equivalent.
Doing it
On a Linux attack box, Impacket is the usual path. The execution tools take a -hashes flag in LMHASH:NTHASH form:
psexec.py -hashes aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 administrator@10.0.0.20
wmiexec.py -hashes :31d6cfe0d16ae931b73c59d7e0c089c0 contoso.local/administrator@10.0.0.20
That aad3b435b51404eeaad3b435b51404ee is not the user's LM hash. It is the empty LM hash, the value LM produces for a blank password. Modern Windows stores no LM hash at all, so tools just drop that placeholder in front of the colon. You can usually pass :NTHASH with an empty LM half, as the second command shows.
For spraying a hash across a subnet, CrackMapExec or its maintained successor NetExec does it at scale:
nxc smb 10.0.0.0/24 -u administrator -H 31d6cfe0d16ae931b73c59d7e0c089c0
Every host that returns Pwn3d! is a box where that hash is local admin. On Windows, Mimikatz does it in-memory with sekurlsa::pth, which spawns a process whose NTLM credential is the hash you supply. Run it and the new shell authenticates as that identity to anything NTLM will accept.
Why one hash spreads so far
The reason pass-the-hash turns a single compromised machine into a domain-wide problem is the local administrator account. For years the standard build was one golden image with one baked-in local admin password, cloned onto every workstation. Same password, same NT hash, on a thousand machines.
So you pop one laptop, dump the local SAM, grab the local admin NT hash, and now you own every machine built from that image. You never touched a domain controller and you never cracked anything. NetExec just walks the subnet. This is the single most common way an engagement goes from one host to "we have everything" in an afternoon.
When you still have to crack it
Pass-the-hash is not a universal key. It only works where NTLM authentication is accepted, and there are real cases where you need the plaintext after all.
Kerberos-only services will not take the hash directly. You either pivot to overpass-the-hash (forge a Kerberos ticket from the NT hash) or you crack the password and authenticate normally.
EDR increasingly flags the classic pass-the-hash tooling. The sekurlsa::pth process-injection pattern and the named-pipe behavior of psexec.py are well-signatured. Sometimes a quiet login with a real password beats a loud hash-pass that gets you isolated.
And the big one: password reuse. A cracked plaintext is portable in ways a hash is not. If Sql_Svc!2021 cracks out of one hash, you can try it against VPNs, web apps, cloud tenants, and SSH, none of which speak NTLM. So you still run the NT hashes through hashcat mode 1000 when you want reach beyond NTLM, even though you do not need to crack them to move laterally inside the domain.
The defender's side
The uncomfortable part is that you cannot patch pass-the-hash away. It is NTLM working as designed. What you can do is shrink the blast radius.
Stop reusing the local admin password. Microsoft LAPS (or Windows LAPS) randomizes the local admin password per machine and rotates it, so a hash dumped from one box is worthless on the next. This single control breaks the most common spread path.
Put your high-value accounts in the Protected Users group. Members cannot use NTLM authentication at all, which means their hashes cannot be passed even if they leak.
Disable NTLM where you can. Auditing first with the Restrict NTLM policies tells you what would break, then you tighten. Full elimination is hard in a real estate full of legacy apps, but every service you move to Kerberos is one fewer place a hash works.
And restrict who is local admin where. If the domain admin never logs into a workstation, their credentials never sit in that workstation's memory to be dumped in the first place. Tiering the administrative model is the structural fix; LAPS and Protected Users are the controls that buy you time while you get there.