[REQ-OPS-001] Enforce KRBTGT Password Rotation
Target Scope
- Applicable Systems: Domain Controllers (Primary Domain Controller / PDC Emulator, replica Domain Controllers)
- Operating Systems: Windows Server 2016, Windows Server 2019, Windows Server 2022, Windows Server 2025
- Directory Scope: Forest Root Domain, Child Domains, and Read-Only Domain Controllers (RODCs)
Implementation Details
- Priority: High
- GPO Path / Registry Location: Active Directory Object Management (krbtgt account object in the Users container:
CN=krbtgt,CN=Users,DC=[Domain],DC=[tld])- Primary KRBTGT Object:
CN=krbtgt,CN=Users,DC=[Domain],DC=[tld] - RODC KRBTGT Objects:
CN=krbtgt_[RODC-Identifier],CN=Users,DC=[Domain],DC=[tld] - Key Monitored Attributes:
pwdLastSet,msDS-KeyVersionNumber(kvno),msDS-SupportedEncryptionTypes,whenChanged,userAccountControl
- Primary KRBTGT Object:
[!WARNING] The
krbtgtaccount password must be rotated periodically using a two-step rotation process to limit the lifespan of potentially compromised Ticket Granting Tickets (TGTs).
- Standard Frequency (DoD STIG): Reset the password at least every 180 days (semi-annually) in accordance with DoD STIG requirements (V-205877, V-225006, V-254427).
- High-Security Frequency (ANSSI): High-security baselines (such as the ANSSI Active Directory hardening guide, recommendation R23) recommend rotating the password every 40 to 90 days.
- Incident Response / Ad-Hoc Rotation: Perform an immediate two-step rotation in the event of a suspected Active Directory compromise, DCSync activity, NTDS.dit exfiltration, Golden/Diamond ticket detection, or following the departure of administrative staff with Tier 0 access.
Rationale
The krbtgt account is a built-in local service account that serves as the Key Distribution Center (KDC) service account in Active Directory. The long-term secret cryptographic keys derived from the krbtgt account password are used by the KDC to sign and encrypt all Kerberos Ticket Granting Tickets (TGTs) issued within the domain, as well as to compute the Privilege Attribute Certificate (PAC) signatures that vouch for user identity, security identifiers (SIDs), and group memberships.
1. Kerberos Ticket Forgery Attack Vectors
If an adversary compromises Active Directory credentials with directory replication privileges (DCSync via DS-Replication-Get-Changes-All), dumps the NTDS.dit database (e.g., via Volume Shadow Copy or raw disk extraction), or compromises Domain Admin privileges, they can harvest the krbtgt password hashes and keys. Possessing these keys enables several devastating persistence and privilege escalation attacks:
- Golden Ticket Attacks (MITRE ATT&CK T1558.001):
An attacker uses the
krbtgtNTLM hash or AES keys to construct forged Kerberos TGTs entirely offline without interacting with a Domain Controller. The attacker can inject arbitrary user identities (including non-existent users), assign the well-known RID 500 (Administrator), embed membership in privileged groups (e.g.,Domain Admins,Enterprise Admins,Schema Admins), and specify arbitrarily long validity periods (often 10 years). When presented to any service in the domain, the KDC accepts the forged TGT because it successfully decrypts with thekrbtgtkey, issuing service tickets (TGS) without re-validating the user's account state or password in Active Directory. - Diamond Ticket Attacks:
In a Diamond Ticket attack, an attacker requests a legitimate TGT from the KDC via standard
AS-REQ/AS-REPexchange, decrypts the issued ticket using the stolenkrbtgtkey, modifies the PAC to add high-privilege group SIDs, recalculates the PAC signatures using thekrbtgtkey, and re-encrypts the TGT. This bypasses behavioral detection systems that look for anomalous TGT issuance lacking prior Kerberos authentication traffic. - Sapphire Ticket Attacks:
In a Sapphire Ticket attack, an attacker leverages Kerberos S4U2self (Service for User to Self) to obtain a legitimate, cryptographically authentic PAC for an administrative account, and transplants that authentic PAC into a forged ticket signed with the
krbtgtkey, making the forged authorization payload indistinguishable from legitimate directory traffic. - PAC Signature Validation & CVE-2022-37967:
Active Directory security updates enforce strong HMAC-SHA1 AES algorithms for PAC signatures (
KERB_CHECKSUM_HMAC_SHA1_96_AES128/KERB_CHECKSUM_HMAC_SHA1_96_AES256). Because PAC signatures are generated using the KDC'skrbtgtkeys, rotatingkrbtgtinvalidates all PAC checksums generated under previous compromised keys.
2. The Two-Password History Architecture and kvno
Active Directory maintains the current password and the immediately previous password for the krbtgt account in its database:
- Current Key (History Index 0): Used by the KDC to encrypt and sign newly issued TGTs.
- Previous Key (History Index 1): Retained by the KDC to allow existing, legitimately issued TGTs to be decrypted and validated during renewal or service ticket requests, avoiding immediate operational disruption.
Each password update increments the Key Version Number (msDS-KeyVersionNumber or kvno) of the krbtgt account:
====================================================================================================
TWO-STEP KRBTGT ROTATION LIFECYCLE
====================================================================================================
INITIAL STATE (Compromised or Expired Key):
+-------------------------+-------------------------+
| Current Key: kvno = 10 | Previous Key: kvno = 9 | <-- Attacker holds kvno = 10 hash
+-------------------------+-------------------------+ (Golden Tickets active)
STEP 1: First Password Reset
+-------------------------+-------------------------+
| Current Key: kvno = 11 | Previous Key: kvno = 10 | <-- New tickets issued with kvno = 11.
+-------------------------+-------------------------+ Old valid tickets & Golden Tickets
(kvno = 10) still decrypt via index 1.
COOLDOWN WINDOW: Wait >= 10 Hours (Recommended: 24 Hours)
- Active Directory replication converges across all Domain Controllers.
- Legitimate user/computer TGTs naturally expire or renew, adopting kvno = 11.
STEP 2: Second Password Reset
+-------------------------+-------------------------+
| Current Key: kvno = 12 | Previous Key: kvno = 11 | <-- kvno = 10 is PURGED from AD history.
+-------------------------+-------------------------+ ALL Golden/Diamond tickets signed with
kvno <= 10 fail decryption and are rejected!
====================================================================================================
- First Reset (Step 1): Increments the
kvnotoN+1. The compromised key shifts to history index 1. Newly issued TGTs usekvno = N+1. Existing legitimate user sessions continue uninterrupted because the KDC can still decrypt tickets signed withkvno = N. - Cooldown Period: A mandatory waiting period allowing Active Directory replication to converge across all Domain Controllers and existing legitimate Kerberos tickets (default lifetime of 10 hours) to expire or renew using
kvno = N+1. - Second Reset (Step 2): Increments the
kvnotoN+2. The key from Step 1 (kvno = N+1) shifts to history index 1, and the compromised key (kvno = N) is permanently purged from the Active Directory database. At this point, any Golden Ticket forged withkvno = Nis rejected withKRB_AP_ERR_MODIFIED(0x29) orKDC_ERR_TGT_REVOKED(0x17).
3. Kerberos Policy Timers and Cooldown Window
The cooldown window between the first and second reset is governed by the domain Kerberos Policy configured in the Default Domain Policy:
- Maximum lifetime for user ticket (
MaxTicketAge): Default is 10 hours (600 minutes). Legitimate user TGTs remain valid for this duration. - Maximum lifetime for user ticket renewal (
MaxRenewAge): Default is 7 days. When a client renews an existing TGT, the KDC issues a renewed ticket signed with the current key (kvno = N+1). - Maximum lifetime for service ticket (
MaxServiceAge): Default is 10 hours (600 minutes). - Maximum tolerance for computer clock synchronization (
MaxClockSkew): Default is 5 minutes.
Because legitimate clients automatically renew their TGTs before MaxTicketAge elapses, waiting at least 10 hours (24 hours recommended in production to account for replication latency and multi-site links) guarantees that all legitimate sessions have migrated to the new key before the second reset purges the previous key.
4. Read-Only Domain Controllers (RODCs) and krbtgt_XXXXX
Read-Only Domain Controllers (RODCs) introduce a distinct architectural separation:
- Each RODC possesses a dedicated
krbtgtaccount namedkrbtgt_[RODC-Identifier](e.g.,krbtgt_12345). - RODCs do not store the main domain
krbtgtaccount key. Instead, the RODC signs TGTs using its own specifickrbtgt_[RODC-Identifier]key. - The domain-wide
krbtgtpassword rotation does not rotate RODCkrbtgtkeys. - If an RODC is physically stolen or its database extracted, administrators must specifically reset that RODC's dedicated
krbtgt_[RODC-Identifier]account and invalidate all user accounts whose credentials were cached on that RODC according to its Password Replication Policy (PRP).
5. Multi-Domain Forests and Inter-Realm Trusts
In a multi-domain Active Directory forest, each domain maintains its own independent krbtgt account. Cross-domain authentication within a forest utilizes inter-realm referral TGTs signed with inter-realm trust keys stored in Trusted Domain Objects (TDOs), rather than the root domain's krbtgt key. However, if the forest root domain krbtgt is compromised, an attacker can forge inter-realm referral tickets that grant Enterprise Admin access across all child domains. Therefore, in a forest recovery scenario, krbtgt accounts across all domains in the forest must be systematically rotated.
6. Security Event Auditing
Monitor the following Windows Security Event Log IDs on all Domain Controllers during and following krbtgt rotation:
- Event ID 4723: An attempt was made to change an account's password (target:
krbtgt). - Event ID 4724: An attempt was made to reset an account's password (target:
krbtgt). - Event ID 4738: A user account was changed (shows modifications to
krbtgtattributes such aspwdLastSet). - Event ID 4768: A Kerberos authentication ticket (TGT) was requested.
- Event ID 4769: A Kerberos service ticket was requested (look for failure code
0x1For0x29indicating outdated or forged keys). - Event ID 4771: Kerberos pre-authentication failed (failure code
0x18indicating bad password, or0x17indicating TGT revoked).
Legacy Impact & Compatibility
- Authentication Outage Hazard from Premature Double Reset: Resetting the
krbtgtpassword twice in rapid succession (without waiting for the Kerberos ticket lifetime and replication to complete) purges active tickets from both history slots. This immediately invalidates all active TGTs across the enterprise, causing massive, domain-wide authentication failures for all users, computer logons, background services, scheduled tasks, and Kerberos-authenticated applications. - Operational Wait Time: A minimum of 10 hours (24 hours is strongly recommended for production environments with multi-site Active Directory replication) must be observed between the first and second reset.
- Trust Relationships: Cross-forest and external trusts use independent shared trust keys and are not invalidated by domain
krbtgtrotation. However, cross-domain referral tickets in flight during an emergency double-reset may require renewal. - Group Managed Service Accounts (gMSA): gMSAs automatically renew their Kerberos tickets based on standard ticket lifespans. As long as the cooldown window is observed, gMSA services will experience zero interruption.
- Direct Azure AD / Entra ID Connect Impact: Pass-through authentication and password hash synchronization are unaffected by
krbtgtrotation, but seamless SSO (Kerberos-based) relying on theAZUREADSSOACCcomputer account relies on Kerberos ticket renewal within standard lifetimes.
Implementation Steps
Option A: Group Policy Object (GPO) Baseline & Staged Administrative Procedure
Kerberos ticket lifetimes are governed via Group Policy Objects, while the password rotation itself is an operational database procedure executed against Active Directory objects.
1. Verify and Enforce Domain Kerberos Policy via GPO
- Open the Group Policy Management Console (
gpmc.msc). - Edit the Default Domain Policy (or a dedicated Tier 0 Domain Controller Policy linked at the domain root).
- Navigate to:
Computer Configuration\Policies\Windows Settings\Security Settings\Account Policies\Kerberos Policy - Verify and enforce standard Kerberos parameters:
- Enforce user logon restrictions:
Enabled - Maximum lifetime for service ticket:
600minutes (10 hours) - Maximum lifetime for user ticket:
10hours - Maximum lifetime for user ticket renewal:
7days - Maximum tolerance for computer clock synchronization:
5minutes
- Enforce user logon restrictions:
2. Staged Administrative Procedure for KRBTGT Rotation
To execute the staged rotation using Active Directory administrative tools:
Phase 1: Pre-Rotation Assessment
- Identify the Primary Domain Controller (PDC) Emulator for the domain:
netdom query fsmo - Verify that Active Directory replication is completely healthy across all Domain Controllers:
repadmin /replsummary repadmin /showrepl * /csv - Audit the existing
krbtgtpassword age andkvno(using the audit script below).
Phase 2: First Password Reset (Step 1)
- Log on to the PDC Emulator with Domain Admin or Enterprise Admin credentials.
- Open Active Directory Users and Computers (
dsa.msc), ensure View -> Advanced Features is enabled. - Navigate to the Users container, right-click krbtgt, and select Reset Password.
- Enter a strong, cryptographically complex random password (minimum 128 characters) and confirm it. Click OK.
- Trigger replication across all Domain Controllers:
repadmin /syncall /AdeP
Phase 3: Cooldown and Ticket Expiration Window
- Wait a minimum of 10 to 24 hours.
- Monitor Domain Controller Security Event Logs for Kerberos ticket renewal events (Event ID 4768) and verify replication convergence.
Phase 4: Second Password Reset (Step 2)
- On the PDC Emulator, repeat the password reset procedure on the krbtgt account with a new, distinct 128-character password.
- Trigger replication across all Domain Controllers:
repadmin /syncall /AdeP
Phase 5: Post-Rotation Verification
- Run the audit script below to verify that all Domain Controllers report the updated
PasswordLastSetand matchingkvno.
Option B: PowerShell Operational Automation (Remediation / Audit)
Use the following enterprise-grade PowerShell scripts to programmatically manage and audit the KRBTGT password rotation lifecycle.
Download Script: Reset-KrbtgtPassword.ps1
# Reset-KrbtgtPassword.ps1
# Description: Resets the KRBTGT account password on the PDC Emulator with a cryptographically secure 128-character password, enforces cooldown safety, and triggers AD replication.
# Target Engine: Windows PowerShell 5.1
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
param (
[Parameter(Mandatory = $false)]
[switch]$Force,
[Parameter(Mandatory = $false)]
[int]$MinCooldownHours = 10,
[Parameter(Mandatory = $false)]
[string]$Server
)
Write-Host "--- Applying Hardening Requirement: KRBTGT Password Rotation ---" -ForegroundColor Cyan
# 1. Verify Active Directory module
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
Write-Error "The ActiveDirectory PowerShell module is required to execute this script."
return
}
Import-Module ActiveDirectory -ErrorAction Stop
# 2. Discover target Domain Controller (PDC Emulator)
try {
$domain = Get-ADDomain -ErrorAction Stop
$targetServer = $Server
if (-not $targetServer) {
$targetServer = $domain.PDCEmulator
}
Write-Host "[*] Target Domain: $($domain.DNSRoot)" -ForegroundColor Gray
Write-Host "[*] Authoritative PDC Emulator: $targetServer" -ForegroundColor Gray
} catch {
Write-Error "Failed to locate domain or PDC Emulator: $($_.Exception.Message)"
return
}
# 3. Retrieve authoritative KRBTGT object
try {
$krbtgt = Get-ADUser -Identity "krbtgt" -Server $targetServer -Properties PasswordLastSet, Enabled, "msDS-KeyVersionNumber", userAccountControl -ErrorAction Stop
if (-not $krbtgt) {
Write-Error "KRBTGT account not found on $targetServer."
return
}
} catch {
Write-Error "Failed to retrieve KRBTGT account from $($targetServer): $($_.Exception.Message)"
return
}
$lastSet = $krbtgt.PasswordLastSet
$currentKvno = $krbtgt."msDS-KeyVersionNumber"
Write-Host "[*] Current KRBTGT Password Last Set: $lastSet" -ForegroundColor Gray
Write-Host "[*] Current Key Version Number (kvno): $currentKvno" -ForegroundColor Gray
# 4. Enforce Cooldown Safety Check
if ($null -ne $lastSet) {
$elapsedHours = (New-TimeSpan -Start $lastSet -End (Get-Date)).TotalHours
if ($elapsedHours -lt $MinCooldownHours -and -not $Force) {
$roundedHours = [math]::Round($elapsedHours, 1)
Write-Warning "SAFETY INTERLOCK ENGAGED: The KRBTGT password was last set only $roundedHours hours ago."
Write-Warning "Resetting KRBTGT again before the Kerberos ticket lifetime ($MinCooldownHours hours) has elapsed"
Write-Warning "will purge the previous key from history and invalidate ALL active domain TGTs,"
Write-Warning "causing enterprise-wide authentication failure for all users and services."
Write-Warning "To override this interlock (e.g., during active incident containment), specify -Force."
return
}
}
# 5. Generate Cryptographically Secure 128-Character Password
$length = 128
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{}|;:,.<>?"
$rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
$bytes = New-Object byte[] $length
$rng.GetBytes($bytes)
$securePassword = New-Object System.Security.SecureString
for ($i = 0; $i -lt $length; $i++) {
$char = $chars[$bytes[$i] % $chars.Length]
$securePassword.AppendChar($char)
}
$securePassword.MakeReadOnly()
$rng.Dispose()
# 6. Execute Password Reset via ShouldProcess
$confirmTarget = "KRBTGT account on $targetServer (Domain: $($domain.DNSRoot))"
if ($PSCmdlet.ShouldProcess($confirmTarget, "Reset KRBTGT account password and increment kvno")) {
try {
Set-ADAccountPassword -Identity $krbtgt -Server $targetServer -NewPassword $securePassword -Reset -ErrorAction Stop
Write-Host "[+] KRBTGT password successfully reset on PDC Emulator ($targetServer)." -ForegroundColor Green
# Re-query to verify kvno increment
Start-Sleep -Seconds 2
$updatedKrbtgt = Get-ADUser -Identity "krbtgt" -Server $targetServer -Properties PasswordLastSet, "msDS-KeyVersionNumber" -ErrorAction Stop
Write-Host "[+] New Password Last Set: $($updatedKrbtgt.PasswordLastSet)" -ForegroundColor Green
Write-Host "[+] New Key Version Number (kvno): $($updatedKrbtgt.'msDS-KeyVersionNumber')" -ForegroundColor Green
# 7. Dispatch Active Directory Replication
Write-Host "[*] Triggering Active Directory replication synchronization..." -ForegroundColor Cyan
$repadmin = Get-Command -Name "repadmin.exe" -ErrorAction SilentlyContinue
if ($repadmin) {
& repadmin.exe /syncall /AdeP | Out-Null
Write-Host "[+] Active Directory replication triggered across all domain partitions." -ForegroundColor Green
} else {
try {
Sync-ADObject -Identity $krbtgt.DistinguishedName -Server $targetServer -ErrorAction SilentlyContinue
Write-Host "[+] Sync-ADObject invoked for KRBTGT account." -ForegroundColor Green
} catch {
Write-Warning "Could not trigger replication automatically. Ensure replication runs across all domain controllers."
}
}
Write-Host ""
Write-Host "=========================================================================" -ForegroundColor Yellow
Write-Host "[IMPORTANT] Two-Step KRBTGT Password Rotation Protocol:" -ForegroundColor Yellow
Write-Host " 1. This reset constitutes Step 1 of the rotation cycle." -ForegroundColor Yellow
Write-Host " 2. Active Directory retains the previous key in history (index 1) so" -ForegroundColor Yellow
Write-Host " existing valid Kerberos tickets continue to function until expiration." -ForegroundColor Yellow
Write-Host " 3. You MUST WAIT at least $MinCooldownHours to 24 hours for all active tickets" -ForegroundColor Yellow
Write-Host " to renew and for replication to converge across all domain controllers." -ForegroundColor Yellow
Write-Host " 4. After the cooldown period, run this script again to perform Step 2," -ForegroundColor Yellow
Write-Host " which purges the pre-rotation key and completely invalidates any" -ForegroundColor Yellow
Write-Host " historical Golden, Diamond, or forged Kerberos tickets." -ForegroundColor Yellow
Write-Host "=========================================================================" -ForegroundColor Yellow
} catch {
Write-Error "Failed to reset KRBTGT password: $($_.Exception.Message)"
}
}
To audit the password rotation status and replication convergence of the KRBTGT account:
Download Script: Get-KrbtgtRotationStatus.ps1
# Get-KrbtgtRotationStatus.ps1
# Description: Audits KRBTGT password age, kvno, replication convergence across all Domain Controllers, and RODC accounts.
# Target Engine: Windows PowerShell 5.1
Import-Module ActiveDirectory -ErrorAction Stop
Write-Host "--- Auditing KRBTGT Password Rotation Status ---" -ForegroundColor Cyan
# 1. Discover Domain and Authoritative PDC Emulator
try {
$domain = Get-ADDomain -ErrorAction Stop
$pdc = $domain.PDCEmulator
Write-Host "[*] Domain: $($domain.DNSRoot)" -ForegroundColor Gray
Write-Host "[*] Authoritative PDC Emulator: $pdc" -ForegroundColor Gray
} catch {
Write-Error "Failed to query domain or PDC Emulator: $($_.Exception.Message)"
return
}
# 2. Retrieve Kerberos Policy MaxTicketAge
$maxTicketAgeHours = 10
try {
$kerbPolicy = Get-ADDefaultDomainPasswordPolicy -ErrorAction SilentlyContinue
if ($kerbPolicy -and $kerbPolicy.MaxTicketAge) {
$maxTicketAgeHours = [math]::Round($kerbPolicy.MaxTicketAge.TotalHours, 1)
}
} catch {
$maxTicketAgeHours = 10
}
Write-Host "[*] Configured Kerberos MaxTicketAge: $maxTicketAgeHours hours" -ForegroundColor Gray
# 3. Query Primary KRBTGT Object on PDC
$krbtgt = Get-ADUser -Identity "krbtgt" -Server $pdc -Properties PasswordLastSet, PasswordExpired, Enabled, "msDS-KeyVersionNumber", "msDS-SupportedEncryptionTypes" -ErrorAction SilentlyContinue
if (-not $krbtgt) {
Write-Error "KRBTGT account not found in Active Directory."
return
}
$passwordLastSet = $krbtgt.PasswordLastSet
$kvno = $krbtgt."msDS-KeyVersionNumber"
$encTypes = $krbtgt."msDS-SupportedEncryptionTypes"
Write-Host " - Account Name: $($krbtgt.Name)" -ForegroundColor White
Write-Host " - Enabled: $($krbtgt.Enabled)" -ForegroundColor White
Write-Host " - Key Version Number (kvno): $kvno" -ForegroundColor White
Write-Host " - Supported Encryption Types Bitmask: $encTypes" -ForegroundColor White
if ($null -ne $passwordLastSet) {
$ageDays = (New-TimeSpan -Start $passwordLastSet -End (Get-Date)).Days
$ageHours = (New-TimeSpan -Start $passwordLastSet -End (Get-Date)).TotalHours
$stigThresholdDays = 180
$anssiThresholdDays = 90
Write-Host " - Password Last Set: $passwordLastSet ($ageDays days ago / $([math]::Round($ageHours, 1)) hours ago)" -ForegroundColor White
# Check if currently inside the two-step cooldown window
if ($ageHours -lt $maxTicketAgeHours) {
Write-Host " - Cooldown Status: IN-PROGRESS (Step 1 executed $([math]::Round($ageHours, 1)) hours ago; wait until $maxTicketAgeHours hours have elapsed before executing Step 2)." -ForegroundColor Yellow
}
# Evaluate compliance thresholds
if ($ageDays -gt $stigThresholdDays) {
Write-Host " - Compliance Status: FAILED - KRBTGT password has not been rotated in $ageDays days (DoD STIG threshold: $stigThresholdDays days)." -ForegroundColor Red
} elseif ($ageDays -gt $anssiThresholdDays) {
Write-Host " - Compliance Status: WARNING - KRBTGT password age is $ageDays days (Exceeds ANSSI recommendation of $anssiThresholdDays days; compliant with STIG threshold of $stigThresholdDays days)." -ForegroundColor Yellow
} else {
Write-Host " - Compliance Status: PASSED - KRBTGT password age is $ageDays days (Compliant with STIG and ANSSI baselines)." -ForegroundColor Green
}
} else {
Write-Host " - Compliance Status: FAILED - PasswordLastSet attribute is null." -ForegroundColor Red
}
# 4. Audit Replication Consistency Across All Reachable DCs
Write-Host "`n[*] Auditing KRBTGT Replication Convergence Across Domain Controllers:" -ForegroundColor Cyan
$dcs = Get-ADDomainController -Filter * -ErrorAction SilentlyContinue
$dcResults = @()
$replicationDiscrepancy = $false
foreach ($dc in $dcs) {
try {
$dcKrbtgt = Get-ADUser -Identity "krbtgt" -Server $dc.HostName -Properties PasswordLastSet, "msDS-KeyVersionNumber" -ErrorAction Stop
$match = ($dcKrbtgt.PasswordLastSet -eq $passwordLastSet) -and ($dcKrbtgt."msDS-KeyVersionNumber" -eq $kvno)
if (-not $match) {
$replicationDiscrepancy = $true
}
$dcResults += [PSCustomObject]@{
DomainController = $dc.HostName
Reachable = $true
PasswordLastSet = $dcKrbtgt.PasswordLastSet
Kvno = $dcKrbtgt."msDS-KeyVersionNumber"
InSync = $match
}
} catch {
$dcResults += [PSCustomObject]@{
DomainController = $dc.HostName
Reachable = $false
PasswordLastSet = $null
Kvno = $null
InSync = $false
}
}
}
foreach ($res in $dcResults) {
if ($res.Reachable -and $res.InSync) {
Write-Host " [OK] $($res.DomainController): kvno=$($res.Kvno), LastSet=$($res.PasswordLastSet)" -ForegroundColor Green
} elseif ($res.Reachable -and -not $res.InSync) {
Write-Host " [MISMATCH] $($res.DomainController): kvno=$($res.Kvno), LastSet=$($res.PasswordLastSet) (Out of sync with PDC)" -ForegroundColor Red
} else {
Write-Host " [UNREACHABLE] $($res.DomainController): Unable to query" -ForegroundColor Yellow
}
}
if ($replicationDiscrepancy) {
Write-Host " [!] Warning: Replication discrepancy detected across Domain Controllers." -ForegroundColor Red
}
# 5. Audit Read-Only Domain Controller (RODC) KRBTGT Accounts
Write-Host "`n[*] Auditing Read-Only Domain Controller (RODC) KRBTGT Accounts:" -ForegroundColor Cyan
$rodcAccounts = Get-ADUser -Filter "Name -like 'krbtgt_*'" -Server $pdc -Properties PasswordLastSet, "msDS-KeyVersionNumber", Enabled -ErrorAction SilentlyContinue
if ($rodcAccounts -and $rodcAccounts.Count -gt 0) {
Write-Host " Found $($rodcAccounts.Count) RODC KRBTGT account(s):" -ForegroundColor Gray
foreach ($rodc in $rodcAccounts) {
$rodcAgeDays = "N/A"
if ($rodc.PasswordLastSet) {
$rodcAgeDays = (New-TimeSpan -Start $rodc.PasswordLastSet -End (Get-Date)).Days
}
Write-Host " - $($rodc.SamAccountName): kvno=$($rodc.'msDS-KeyVersionNumber'), LastSet=$($rodc.PasswordLastSet) ($rodcAgeDays days ago), Enabled=$($rodc.Enabled)" -ForegroundColor White
}
} else {
Write-Host " No Read-Only Domain Controller (RODC) accounts detected in this domain." -ForegroundColor Gray
}
Sources & Compliance References
- ANSSI AD Hardening Guide: Recommendation R23 (Secrets renewal - Renouvellement des secrets de l'Active Directory: Rotate KRBTGT password every 40 to 90 days and following security incidents).
- DoD STIG:
- Windows Server 2016 Domain Controller STIG: V-205877 (WN16-DC-000270)
- Windows Server 2019 Domain Controller STIG: V-225006 (WN19-DC-000270)
- Windows Server 2022 Domain Controller STIG: V-254427 (WN22-DC-000270)
- Windows Server 2025 Domain Controller STIG: The
krbtgtaccount password must be reset at least every 180 days.
- CIS Benchmark: CIS Microsoft Windows Server Benchmark (General Guidance on Kerberos TGT Key Lifecycle and Periodic Rotation).
- Microsoft Security Guidance:
- "Securing the KRBTGT Account"
- "Active Directory Forest Recovery - Reset the krbtgt Password"
- RFC 4120: The Kerberos Network Authentication Service (V5)
- RFC 3961 / RFC 3962: Encryption and Checksum Specifications for Kerberos 5
- MITRE ATT&CK: