Active Directory

Hardening Domain Controllers: The 10-Point Checklist Most Companies Skip

Your organization may have hardened Active Directory at the domain level and still left domain controllers wide open at the OS layer. These are the DC-specific controls that security teams consistently miss — and that attackers consistently exploit.

There is an important distinction between hardening Active Directory and hardening domain controllers. Most organizations have made progress on the former — they've addressed common AD misconfigurations, tightened group memberships, and reviewed delegation. But the DCs themselves — the Windows Server instances running the AD DS role — often remain in a default or near-default configuration that no attacker would respect.

Domain controllers are Tier 0 assets. They hold the keys to every identity in your environment. A single compromised DC means every credential, every service account, every privileged identity is now owned by the attacker. The hardening steps below are DC-specific — not general AD hygiene, but controls applied directly to the servers running AD DS. Each one includes the PowerShell or GPO path to implement it.

1. Enforce the Tiered Administration Model (Tier 0 Isolation)

The tiered administration model separates administrative credentials by the sensitivity of the assets they manage. Tier 0 is your most sensitive layer: domain controllers, the AD DS database, ADFS, PKI, and identity synchronization servers. The core rule is that Tier 0 credentials must never touch Tier 1 or Tier 2 systems — and non-Tier 0 credentials must never authenticate to DCs.

In practice, implement this via GPO-enforced logon restrictions on domain controllers:

  • Apply Allow log on locally and Allow log on through Remote Desktop Services exclusively to DC administrators (a dedicated, purpose-built group — not Domain Admins)
  • Apply Deny log on locally and Deny access to this computer from the network to all Tier 1 and Tier 2 admin groups
  • Ensure that Tier 0 admin accounts have no mailboxes, cannot browse the internet, and are not used for day-to-day work
# GPO path for logon restrictions:
# Computer Configuration → Policies → Windows Settings →
# Security Settings → Local Policies → User Rights Assignment

# Verify current local logon rights via PowerShell
$Policy = Get-LocalGroupPolicy -ComputerName "DC01"
# Use Group Policy Results (gpresult /r) to audit effective policy on each DC

2. Enable LSA Protection (RunAsPPL)

LSA Protection forces the Local Security Authority process (lsass.exe) to run as a Protected Process Light (PPL). When RunAsPPL is active, tools like Mimikatz that attempt to read LSASS memory via OpenProcess must have a kernel-level driver signed by Microsoft — raising the bar substantially for credential extraction attacks.

On Windows Server 2019 and later, enable via Group Policy:

# Enable via GPO (preferred for DCs):
# Computer Configuration → Administrative Templates →
# System → Local Security Authority
# → "Configures LSASS to run as a protected process" → Enabled

# Enable via registry (immediate effect, requires reboot):
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
  -Name "RunAsPPL" -Value 1 -Type DWord

# Verify the setting is applied:
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
  -Name "RunAsPPL"

After enabling LSA Protection, verify that no legitimate security software or endpoint agents break — some older AV products are not PPL-aware. On Windows Server 2022, you can also add UEFI lock (value of 2) to prevent disabling RunAsPPL without a BIOS-level change.

3. Enable Windows Defender Credential Guard

Credential Guard uses virtualization-based security (VBS) to isolate NTLM password hashes and Kerberos tickets in a separate, hypervisor-protected container. Even if an attacker dumps LSASS, Credential Guard prevents them from extracting credential material because the sensitive secrets never exist in accessible memory.

# Enable via registry (requires reboot):
# Enable Virtualization-Based Security:
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard" `
  -Name "EnableVirtualizationBasedSecurity" -Value 1 -Type DWord
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard" `
  -Name "RequirePlatformSecurityFeatures" -Value 1 -Type DWord

# Enable Credential Guard:
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
  -Name "LsaCfgFlags" -Value 1 -Type DWord

# Verify Credential Guard status:
Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard

Credential Guard requires 64-bit hardware with UEFI, Secure Boot, and either TPM 1.2 or 2.0. Verify hardware compatibility before deploying via GPO. Note that some Kerberos delegation configurations (unconstrained delegation) are incompatible with Credential Guard — resolve these first.

4. Enforce LDAP Signing and Channel Binding

Without LDAP signing, an attacker can execute man-in-the-middle attacks against LDAP traffic — intercepting bind requests and injecting malicious queries. Without channel binding, attackers can relay NTLM authentication to the DC's LDAP interface even over TLS. Both are default-off in pre-2025 Server versions.

The correct implementation sequence matters: configure clients first, then enforce on DCs. Skipping this order can lock clients out of the domain.

# Step 1: Enable auditing first to identify non-compliant clients
# Monitor Event ID 2889 (LDAP signing failures) on DCs
# Monitor Event IDs 3074/3075 (channel binding failures) on DCs

# Step 2: Configure DCs to require LDAP signing (via GPO):
# Computer Configuration → Policies → Windows Settings →
# Security Settings → Local Policies → Security Options
# → "Domain controller: LDAP server signing requirements" → Require signing

# Step 3: Enable channel binding via registry on each DC:
Set-ItemProperty `
  -Path "HKLM:\System\CurrentControlSet\Services\NTDS\Parameters" `
  -Name "LdapEnforceChannelBinding" -Value 2 -Type DWord
# Value 2 = Always enforce; Value 1 = When supported; Value 0 = Never

# Verify channel binding setting:
Get-ItemProperty `
  -Path "HKLM:\System\CurrentControlSet\Services\NTDS\Parameters" `
  -Name "LdapEnforceChannelBinding"

Windows Server 2025 now enforces LDAP signing by default via a new GPO setting ("LDAP server signing requirements Enforcement"). If you're running 2025 DCs, set the legacy policy to "None" and enable the new enforcement policy instead.

5. Disable the Print Spooler Service on All DCs

The Print Spooler service has no legitimate function on domain controllers, yet it runs by default on every Windows Server version. CVE-2021-1675 and CVE-2021-34527 (PrintNightmare) demonstrated how an authenticated attacker can use the Print Spooler to execute arbitrary code as SYSTEM on a DC, bypassing all privilege boundaries. The Spooler is also abused by tools like SpoolSample to force DC authentication coercion for relay attacks.

# Disable via GPO (recommended — persistent and auditable):
# Computer Configuration → Policies → Windows Settings →
# Security Settings → System Services → Print Spooler → Disabled

# Or disable immediately via PowerShell:
Stop-Service -Name Spooler -Force
Set-Service -Name Spooler -StartupType Disabled

# Verify across all DCs in the domain:
Get-ADDomainController -Filter * | ForEach-Object {
  $svc = Get-Service -Name Spooler -ComputerName $_.HostName
  [PSCustomObject]@{
    DC      = $_.HostName
    Status  = $svc.Status
    StartType = $svc.StartType
  }
}

6. Rotate the KRBTGT Password — Twice

The KRBTGT account's password hash is used to sign every Kerberos ticket in the domain. If an attacker obtains this hash — through a DCSync, a compromised DC, or a memory dump — they can forge Golden Tickets: Kerberos TGTs that grant access to any resource in the domain, impersonating any user, with an arbitrary validity period. These forged tickets remain valid even after password resets of individual accounts, because the forgery relies on the KRBTGT hash, not the user's hash.

Best practice is to rotate the KRBTGT password at least every 180 days. Use Microsoft's official script, which enforces a mandatory wait between the two rotations:

# Use Microsoft's official New-KrbtgtKeys.ps1 script:
# https://github.com/microsoft/New-KrbtgtKeys.ps1

# Before rotating, verify AD replication health:
repadmin /replsummary
repadmin /showrepl

# Rotation 1 — invalidates currently active keys:
# Run the script in simulation mode first, then execute
.\New-KrbtgtKeys.ps1 -Mode ModeSingle

# Wait at least 10 hours (Kerberos ticket lifetime)
# for replication to propagate to all DCs

# Rotation 2 — invalidates previous keys (critical for Golden Ticket remediation):
.\New-KrbtgtKeys.ps1 -Mode ModeSingle

# Verify KRBTGT password was updated on all DCs:
Get-ADUser krbtgt -Properties PasswordLastSet | Select-Object PasswordLastSet

If you suspect your environment has been compromised, treat both rotations as emergency actions within an incident response window. The 10-hour wait between rotations is not optional — forcing the second rotation too quickly causes authentication failures domain-wide.

7. Review and Restrict AdminSDHolder Permissions

AdminSDHolder is an AD object whose permissions serve as a template for protected groups — Domain Admins, Enterprise Admins, Schema Admins, and others. Every 60 minutes, the SDProp process copies AdminSDHolder's DACL onto every member of these protected groups, overwriting any changes. This mechanism exists to prevent privilege stripping, but it also means that any attacker who gains write access to AdminSDHolder has persistent control over every privileged account in the domain.

# Audit AdminSDHolder permissions:
$adminSDHolder = "CN=AdminSDHolder,CN=System," + (Get-ADDomain).DistinguishedName
$acl = Get-Acl "AD:\$adminSDHolder"
$acl.Access | Where-Object {
  $_.ActiveDirectoryRights -match "GenericAll|WriteDacl|WriteOwner|GenericWrite"
} | Select-Object IdentityReference, ActiveDirectoryRights, AccessControlType

# Identify accounts protected by AdminSDHolder (adminCount = 1):
Get-ADUser -Filter {AdminCount -eq 1} -Properties AdminCount |
  Select-Object Name, SamAccountName, Enabled

# Accounts that should NOT be protected (remove adminCount = 1 from stale accounts):
# Review service accounts, disabled accounts, and accounts no longer in privileged groups

Any principal with write rights to AdminSDHolder effectively owns Domain Admins. Strip non-authorized entries ruthlessly. Also audit accounts with adminCount=1 that are no longer members of protected groups — they retain the hardened DACL but are no longer actively monitored, making them attractive persistence targets.

8. Restrict DCSync Permissions

DCSync (MITRE ATT&CK T1003.006) abuses the MS-DRSR replication protocol to pull credential hashes from a DC without physically accessing it. The attack requires two extended rights at the domain root: Replicating Directory Changes and Replicating Directory Changes All. In a clean domain, only Domain Controllers and explicitly authorized replication accounts should hold these rights.

# Audit DCSync-capable accounts:
$domainDN = (Get-ADDomain).DistinguishedName
$acl = Get-Acl "AD:\$domainDN"
$acl.Access | Where-Object {
  $_.ObjectType -eq "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2" -or  # Replicating Directory Changes All
  $_.ObjectType -eq "89e95b76-444d-4c62-991a-0facbeda640c"      # Replicating Directory Changes in Filtered Set
} | Select-Object IdentityReference, ActiveDirectoryRights

Monitor Event ID 4662 with the GUID 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2 on all DCs. Any replication request from a non-DC source IP or account is a DCSync indicator. Alert on this immediately — it is rarely a false positive.

9. DC-Specific GPO: Audit Policy, Logon Restrictions, and Service Hardening

Apply a dedicated GPO scoped to the Domain Controllers OU with these specific settings:

  • Advanced Audit Policy → Account Logon → Audit Kerberos Authentication Service: Success and Failure (generates Event IDs 4768, 4769)
  • Advanced Audit Policy → DS Access → Audit Directory Service Access: Success (generates Event ID 4662 for DCSync detection)
  • Advanced Audit Policy → DS Access → Audit Directory Service Changes: Success (generates Event ID 5136 for DACL modification detection)
  • User Rights Assignment → Deny access to this computer from the network: Add Tier 1 and Tier 2 admin groups
  • User Rights Assignment → Allow log on through Remote Desktop Services: Restrict to DC-specific admin group only
  • Security Options → Interactive logon: Number of previous logons to cache: Set to 0 (prevents cached credential attacks on DCs)
# Verify Advanced Audit Policy is applied correctly on a DC:
auditpol /get /category:*

# Check cached credentials setting:
Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" `
  -Name "CachedLogonsCount"
# Should be 0 on all DCs

10. Monitor Key Event IDs on Domain Controllers

Hardening without detection is half a job. Configure your SIEM to alert on these DC-specific events in near-real-time:

  • Event ID 4662 — Directory service object access with replication GUIDs: DCSync attack indicator. Alert on any non-DC account performing this operation.
  • Event ID 4769 — Kerberos service ticket requested with RC4 encryption type (0x17): Kerberoasting indicator. Volume-based alerting for multiple RC4 requests in a short window.
  • Event ID 4768 — Kerberos AS-REQ with RC4 encryption type (0x17) for multiple accounts: AS-REP Roasting indicator.
  • Event ID 4625 — Failed logon with Logon Type 3 (network) to a DC: Password spray or lateral movement indicator.
  • Event ID 4720/4728/4732/4756 — Account creation or addition to privileged groups: Persistence indicator.
  • Event ID 5136 — Directory service object modification: AdminSDHolder or domain root ACL tampering.
# PowerShell: Pull recent DCSync-related events from all DCs
Get-ADDomainController -Filter * | ForEach-Object {
  Get-WinEvent -ComputerName $_.HostName -FilterHashtable @{
    LogName = 'Security'
    Id      = 4662
    StartTime = (Get-Date).AddHours(-24)
  } -ErrorAction SilentlyContinue
} | Where-Object {
  $_.Message -match "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2"
}

Execution Order and Priority

If you implement nothing else from this checklist today, prioritize in this order:

  1. Immediately: Disable Print Spooler on all DCs — zero downside, eliminates an entire class of coercion attacks
  2. This week: Enable LSA Protection (RunAsPPL) and audit DCSync permissions
  3. This month: Enable Credential Guard, enforce LDAP signing and channel binding, implement tiered admin model
  4. This quarter: Rotate KRBTGT password (twice with proper cadence), review AdminSDHolder, ensure SIEM alerting on critical Event IDs

These controls don't require significant investment — they are configuration changes to assets you already own. The attacker who compromises your environment will check for every one of them. Make sure they find a hardened target.

Ready to validate your DC hardening?

Our team conducts DC-specific hardening assessments for enterprise environments — verifying every control on this checklist, identifying gaps, and delivering a prioritized remediation plan. We've hardened DCs in some of the most complex environments in the Fortune 500.