Skip to main content
cd ../config-traps
risk/register/domain-controller-delegation-exemptions-hide-real-risk.html
Active Directory Kerberos Delegationhigh severityActive Directory

Domain Controller Delegation Exemptions Hide Real Risk

Overview

A domain controller's userAccountControl carries TRUSTED_FOR_DELEGATION by default, and audit tooling built to silence that expected noise often keys exclusions on OU path or naming pattern rather than actual DC identity. Once a member server lands in the same OU or matches the naming filter, unconstrained delegation on that box stops surfacing in any future BloodHound or PingCastle sweep.

At a glance

Unsafe setting
Domain controller Kerberos delegation audits are excluded by OU path or naming pattern rather than by primaryGroupID or isCriticalSystemObject.
Failure trigger
A member server is moved into the Domain Controllers OU or matches the DC naming convention, inheriting the audit exclusion meant only for real DCs.
Blast radius
Genuine unconstrained delegation on a compromised member server stops appearing in BloodHound or PingCastle sweeps with no alert or audit trail.
Recommended control
Filter delegation audits on primaryGroupID 516/521 and diff results against a stored objectGUID baseline on every run, alerting on any drift.

The Trap

Every domain controller gets the TRUSTED_FOR_DELEGATION bit (0x80000) set in userAccountControl the moment dcpromo or Install-ADDSDomainController finishes, because DC-to-DC replication, referral chasing, and several Kerberos service tickets rely on unconstrained delegation between controllers. This is expected and cannot be removed without breaking authentication. The trap is what happens next, when someone builds an audit query to stop that expected finding from drowning out real ones.

The Default State

Security teams running BloodHound, PingCastle, or a custom LDAP query against (userAccountControl:1.2.840.113556.1.4.803:=524288) get one critical finding per domain controller on day one. Rather than filtering on primaryGroupID=516 or isCriticalSystemObject=TRUE, which correctly identify DCs regardless of location, the common shortcut is an OU-path exclusion such as NOT (distinguishedName -like "*OU=Domain Controllers*") or a name-pattern exclusion like NOT (name -like "DC*"). Both get pasted into the recurring audit script and forgotten.

The Blast Radius

Admins consolidating GPO scope routinely relocate print servers, backup proxies, and jump hosts into the Domain Controllers OU to inherit hardened baseline policies, or they name new build servers with a DC-prefixed convention for asset tracking. Either move now satisfies the exclusion filter written months earlier. A member server carrying real unconstrained delegation, reachable by any user with local admin who can coerce authentication from a privileged account, silently drops out of every subsequent sweep. There is no scan failure, no alert, and no audit trail showing the exemption was ever applied to that object, because the exemption logic lives inside the query itself rather than as a documented risk acceptance against a specific object GUID.

The Lead Mechanic Fix

Rewrite delegation audits to key exclusions on role, not location or naming: filter on (primaryGroupID=516 OR primaryGroupID=521) AND (userAccountControl:1.2.840.113556.1.4.803:=524288), then explicitly diff the remaining unconstrained-delegation object list against a stored objectGUID baseline of known DCs on every run: Get-ADComputer -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)" -Properties primaryGroupID | Where-Object { $_.primaryGroupID -notin 516,521 }. Any drift against the prior baseline should page, not silently absorb into the exclusion.