LDAP relay : sign your LDAP, bind your channels
Intercepted NTLM authentication, forwarded to your domain controller's LDAP service, becomes a valid login without knowing a single password. Enforce LDAP signing and channel binding, and the trick dies on the doorstep.
01 The Big PictureFor leadership · no jargon
Okay, so LDAP relay the attack where your own helpfulness gets used against you. This guide walks through what it actually is, how to check whether you're exposed, and the two settings that shut it down properly.
Think of it like the office receptionist who takes phone calls. An attacker can't convince the receptionist to hand over the safe code directly, but they can get an employee to phone in, then quietly patch that call through to the safe instead of the intended recipient. The receptionist checks the caller is a real employee (they are) and does as asked. Nobody's password is cracked, nobody's login is guessed; the attacker just borrowed someone else's. That's NTLM relay to LDAP in a nutshell a domain controller is tricked into completing a login that was meant for somewhere else, and the attacker gets to act as that account in the directory.
On engagements this is one of the classics, and the reason is simple most domains have LDAP signing off, because that was the default for decades. A pentester who gets a foothold on the network, or just coerces a machine into calling back, relays that authentication to a DC and starts making changes: adding a computer account, granting delegation rights, reading out local admin passwords. From "no credentials at all" to "domain admin on a silver platter" can be a single polite request. The tooling for it is public, mature and free.
The good news this is a configuration problem, not a waiting-for-a-patch problem, and it has been fixable since 2020. Two settings, "require LDAP signing" and "require channel binding", close the door. The job is minutes of change per domain controller plus some audit time to find the devices (multifunction printers are the usual suspects, sorry) that still talk LDAP the old way. No data loss, no downtime if you audit first.
02 Technical BreakdownFor engineers
NTLM authentication is challenge-response: the server issues a challenge, the client encrypts it with the password hash, done. Nothing in that exchange proves the server you started talking to is the server you finished with. If an attacker sits in the middle, or coerces a host into authenticating to them (Responder poisoning, mitm6, or coercions like PetitPotam and PrinterBug via WebDAV), they can forward the whole handshake to a third party. When that third party is a DC's LDAP service, the attacker ends up with an authenticated LDAP session as whatever account they relayed, no password needed.
Why LDAP specifically? Because a relayed SMB authentication can't easily become an LDAP session: the SMB client always negotiates signing, and unlike SMB servers, LDAP servers honour that. The escape hatch is HTTP: the WebClient (WebDAV) service doesn't negotiate signing, which is why the modern chain is coerce over WebDAV, relay HTTP to LDAP, profit. The canonical PoC is impacket's ntlmrelayx.py [1], which targets LDAP and LDAPS directly and, once relayed, can add a machine account (--add-computer, capped by ms-DS-MachineAccountQuota), write resource-based constrained delegation onto a machine (--delegate-access), drop shadow credentials, grant DCSync rights via ACL abuse, and dump LAPS passwords (--dump-laps). One neat implementation detail: try to add a computer over plain LDAP and it auto-upgrades to StartTLS, because setting a password requires TLS either way. There's also the Kerberos-flavoured sibling, KrbRelay [2], and its local-privesc wrapper KrbRelayUp, which turned any domain user into local SYSTEM on unpatched hosts precisely because LDAP signing was off by default.
The fix is two independent controls, and you need both. LDAP signing (LDAPServerIntegrity = 2, "Require Signing") makes the DC reject unsigned SASL binds and plaintext simple binds; a relayed authentication can't sign, because the attacker never had the session key. Channel binding (LdapEnforceChannelBinding, part of Extended Protection for Authentication, introduced with CVE-2017-8563) ties the TLS session on LDAPS to the authentication, so a relayed credential can't ride a TLS tunnel the attacker set up. Each alone has a bypass: signing enforced but channel binding off, relay to LDAPS instead (TLS satisfies the signing check); channel binding enforced but signing off, relay to LDAP with StartTLS. SpecterOps documented both bypasses years ago [3]. Also worth knowing: these are per-DC settings, so in a mixed estate the one forgotten DC is the one your pentester finds.
Microsoft shipped the controls in March 2020 (ADV190023 / KB4520412) [4] but, famously, never flipped the default on Server 2019/2022 after enterprise pushback. Only Windows Server 2025 enforces LDAP signing out of the box. So on anything older, it's on you. Fun detail: CVE-2025-54918, September 2025, showed attackers stripping the signing flags while keeping LOCAL_CALL, bypassing signing and channel binding even on hardened DCs, so keep DCs patched too.
03 Affected
Any Active Directory domain with domain controllers not requiring LDAP signing or channel binding, which in practice is the overwhelming majority of pre-Server-2025 estates. Exploitation needs network position to intercept or coerce authentication (Responder on the local segment, or a WebDAV-coercible host), and something to relay from: any account or machine account works, the DC's own machine account being the jackpot.
Companion exposure: ms-DS-MachineAccountQuota still at its default of 10 lets any relayed or phished user create machine accounts for the RBCD chain. Server 2025 DCs enforce signing by default but still need channel binding enforced manually.
04 Detection
Before we change anything, let's see what's actually talking to your DCs and how. Crank up the LDAP diagnostics first:
Reg Add HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics /v "16 LDAP Interface Events" /t REG_DWORD /d 2Enables the detailed LDAP event logging on the DC; this is what makes the unsigned-bind events show up at all.
Get-WinEvent -LogName "Directory Service" -MaxEvents 500 | Where-Object Id -eq 2889 | Select-Object TimeCreated,MessageEvent 2889 is the hunting event: every unsigned or plaintext-simple bind, with the client IP and account. Leave it running a couple of weeks before enforcing anything.
Get-WinEvent -LogName "Directory Service" -MaxEvents 500 | Where-Object Id -in 3039,3074,3075 | Select-Object TimeCreated,MessageChannel-binding audit events (3074/3075 on patched 2019/2022, 3039 on 2016): clients that *would* fail if channel binding were enforced. Note 3039 only fires on rejection, so it's useless for pre-monitoring.
Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' | Select-Object LDAPServerIntegrity,LdapEnforceChannelBindingCurrent state. Expect 1 (or nothing) and 0 (or nothing) on an unhardened DC. Yes, "nothing" means vulnerable.
From the attacker side, NetExec's --generate-relay-list (SMB signing off) and a quick nmap for 389/636 tell you what a pentester sees in the first ten minutes.
05 Remediation
01 Find and fix the unsigned talkers
Work through the 2889 log first. The usual casualties are multifunction printers (scan-to-email doing simple binds), older VPN appliances (SonicWall gets named a lot), Linux/Mac clients with ancient LDAP settings, and the odd line-of-business app. Reconfigure them to use LDAPS or signed SASL binds; for genuinely hopeless legacy kit, the community trick is pointing it at an OpenLDAP proxy that upgrades the connection to TLS on the way to AD.
02 Enforce LDAP signing
Set it via GPO ("Domain controller: LDAP server signing requirements" → Require Signing) or directly:
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name LDAPServerIntegrity -PropertyType DWORD -Value 2 -ForceMakes the DC reject unsigned SASL binds and plaintext simple binds. On Server 2025 there's a new "LDAP server signing requirements Enforcement" policy; set the old policy to None and the new one to Enabled.
03 Enforce channel binding
Start in audit mode, then enforce. One setting at a time (I know, patience, but you'll thank yourself when something breaks):
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name LdapEnforceChannelBinding -PropertyType DWORD -Value 1 -Force"When supported": compatible clients must present a channel binding token, so you can watch 3039/3074/3075 and fix stragglers. Takes effect immediately, no reboot.
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name LdapEnforceChannelBinding -Value 2"Always": all clients must present a CBT. Only after the audit events go quiet.
One gotcha from Microsoft's own hardening series: channel binding only applies to SASL binds over TLS, so simple binds over TLS are unaffected, and if you have a load balancer bridging TLS in front of the DCs, channel binding breaks until you persist TLS to the DC and add the VIP FQDN to the DC certificate SANs.
04 Reduce what a relay buys
Set-ADDomain -Identity domain.local -Replace @{"ms-DS-MachineAccountQuota"=0}Stops any authenticated user creating machine accounts, which defangs the RBCD chain. Caveat from the trenches: SQL AlwaysOn listener creation needs the quota, so raise it temporarily for that.
And the broader tidy-up: restrict NTLMv1 (Network security: LAN Manager authentication level), enforce SMB signing (independent control, same relay family), EPA on AD CS web enrolment, and keep DCs patched (CVE-2025-54918 is the recent "hardened and still bypassed" reminder).
06 Verification
Enforced? Let's prove it rather than assume it.
Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' | Select-Object LDAPServerIntegrity,LdapEnforceChannelBindingExpect LDAPServerIntegrity = 2 and LdapEnforceChannelBinding = 2, on every DC, not just the one you remembered.
Get-WinEvent -LogName "Directory Service" -MaxEvents 200 | Where-Object Id -eq 2888Event 2888 confirms signing is enforced and rejecting unprotected binds; the clients in it are your stragglers.
ldapsearch -x -H ldap://dc01.domain.local -D 'DOMAIN\testuser' -w 'P@ssw0rd' -b 'DC=domain,DC=local' sAMAccountName=testuserAn unsigned simple bind from a Linux box should now fail; if it succeeds, signing isn't actually enforced on that DC.
The real verification, of course, is having a pentester try the relay again and fail. Tools re-check the fix; people find the hole.
07 Rollback
The realistic rollback is a device that turns out to be mission-critical and still can't sign (it happens, and it's usually a printer attached to something scary). Drop back to audit mode rather than fully off:
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name LdapEnforceChannelBinding -Value 1Channel binding back to "When supported" while you fix the client.
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name LDAPServerIntegrity -Value 1Signing back to None as a last resort. Treat this as temporary; an unsigned DC is an open relay target.
A common grumble (fair, afaik there's still no allowlist): enforcement is all-or-nothing per DC, so if one legacy app blocks enforcement domain-wide, consider isolating it behind an OpenLDAP proxy rather than leaving every DC soft.
08 References
- impacket ntlmrelayx.py : the canonical LDAP relay PoC
- KrbRelay (Kerberos relay variant)
- Microsoft ADV190023 / KB4520412 : LDAP channel binding and signing requirements
- Microsoft AD Hardening Series Part 5 : Enforcing LDAP Channel Binding
- SpecterOps : The Renaissance of NTLM Relay Attacks
- TrustedSec : LDAP Channel Binding and LDAP Signing
- Server 2025 LDAP signing enforcement (Microsoft PFE blog)
- NVD : CVE-2025-54918
If I've got anything wrong here, or you've hit an edge case I haven't covered, do say, corrections and questions are always welcome :)
Ship the fix. Prove it’s closed.
If this runbook doesn’t fit your stack, send the CVE — an operator writes a verified path, usually within a business day. Or contribute your own and get co-authored credit.