guideonce.
← All guides

Active Directory · Security Hardening

Remediating Kerberos RC4 Deprecation Across a Multi-Domain Environment

An honest account of working through CVE-2026-20833 across a multi-domain estate with an AWS Managed AD trust. What to audit, how to fix the accounts safely, and when to enforce.

CVE-2026-20833 Active Directory Kerberos AES Enforcement

Microsoft's phased deprecation of RC4 in Kerberos, tracked as CVE-2026-20833, is one of the most impactful Active Directory security changes in years. On paper it sounds straightforward: move from RC4 to AES. In practice, across a multi-domain environment with an AWS Managed AD forest trust, legacy service accounts, and a mixed cloud and on-prem estate, it turned into a methodical weeks-long project that surfaced issues I had not anticipated.

This is a straight account of how I approached it, what I found, what briefly broke, how I fixed it, and the tools I built along the way which turned out to be useful far beyond this one project. A companion post, Kerberos RC4 Remediation: Live Monitoring and Diagnostic Scripts, collects the PowerShell tooling itself with a focus on reuse.

The deadline

The July 2026 Windows security updates remove the RC4DefaultDisablementPhase registry key entirely and lock in enforcement with no rollback. If you have not started, start now.

Why This Matters and What Changes When

RC4 has been the default Kerberos fallback in Active Directory for decades. In event logs and registry values you will see it identified as encryption type 0x17 (the hex form) or 23 (the same number in decimal); both refer to the same RC4-HMAC algorithm, and which form appears depends on which tool you are using. The issue is not that environments are deliberately using RC4. It is that accounts with no explicit msDS-SupportedEncryptionTypes attribute silently default to whatever the domain default allows, which historically includes RC4.

This matters because of Kerberoasting: any authenticated domain user can request a Kerberos service ticket for any SPN in the domain. If that ticket is RC4-encrypted, an attacker can take it offline and crack it with commodity hardware, often within minutes for weak passwords. CVE-2026-20833 addresses this by making AES the enforced default.

Microsoft's rollout has three phases:

1

January 2026, audit mode

New KDCSVC event IDs 201 to 209 added to the System log. RC4DefaultDisablementPhase registry key introduced. No behaviour changes yet.

2

April 2026, enforcement with rollback

DefaultDomainSupportedEncTypes changes to AES-SHA1 only (0x18) for accounts without explicit configuration. Rollback to audit mode still possible via the registry key.

3

July 2026, full enforcement

RC4DefaultDisablementPhase removed. No rollback available. RC4 is gone as a default.

The Shape of the Environment

Some context on the scale of what this looked like, because the shape of the project influences the approach:

AD Domains
Multiple
A mix of traditional AD domains and an AWS Managed AD with a forest trust
Domain Controllers
Multiple
A mix of cloud-hosted and on-prem
Accounts Remediated
Many thousands
Users, computers, inetOrgPerson objects across all domains
Active RC4 Use Found
None
After weeks of audit logging, no live dependencies

The interesting bit there is the last one. After weeks of audit logging across every DC, the project found no genuine live RC4 dependencies. Everything was a case of accounts permitted to use RC4 by virtue of having no explicit attribute set, rather than anything actively negotiating it. That is reassuring but it does not let you off the hook: when April 2026 enforcement lands, accounts without explicit AES keys will still fail, regardless of whether they have ever been seen using RC4. The audit only tells you what is in use right now, not what would break under enforcement.

If You Also Need to Move Quickly

This was a deadline-driven project, not a slow gradual rollout. The work spanned several weeks rather than several months, shaped by the constraint that everything had to land before Microsoft's July 2026 enforcement deadline. That pressure made one thing matter more than anything else: having explicit rollback paths at the stages that actually warranted them, and being clear-eyed about the one stage that does not have one.

The stages where I built in real rollback ability:

The one stage with no rollback: the krbtgt password reset. Once you reset, you cannot go back. I mitigated this not by being able to revert but by staging it carefully: set the encryption type first, verify it took, then reset, wait for replication to fully complete, then reset again. The staging is the safety net.

If you want a quick, efficient route through this

The phase order in this post is the order I used and I would recommend it for similar deadline-driven situations. That said, every AD environment has its own peculiarities: the shape of your trusts, the age of your service accounts, the presence of legacy clients, the discipline of your existing GPO policies, and whether you have AWS Managed AD or other partially-controlled directories all change what "fast" actually looks like. Treat this as a baseline to adapt rather than a recipe to follow without thinking. Test the bulk-change scripts against a small batch in your own environment before running them at scale, and watch the live monitor for the first few hours after every significant change.

Phase 1: Audit Before You Change Anything

Enable Kerberos audit logging

The first and most important step is turning on audit logging across every domain controller. Run this on each DC:

auditpol /set /subcategory:"Kerberos Authentication Service" /success:enable /failure:enable
auditpol /set /subcategory:"Kerberos Service Ticket Operations" /success:enable /failure:enable

Then verify it took effect, because a GPO may be overriding your local auditpol settings:

auditpol /get /subcategory:"Kerberos Authentication Service"
auditpol /get /subcategory:"Kerberos Service Ticket Operations"

If it reports "No Auditing" despite running the set command, a Default Domain Controllers Policy GPO is winning. Set the policy there instead:

Computer Configuration > Policies > Windows Settings > Security Settings >
Advanced Audit Policy Configuration > Account Logon >
  Audit Kerberos Service Ticket Operations: Success, Failure
  Audit Kerberos Authentication Service: Success, Failure
Verify on every DC, not just one

In my project, several DCs were silently ignoring the auditpol commands because of a GPO override. I only discovered this when querying event logs and finding them completely empty on those DCs. Always verify with a direct query rather than trusting the auditpol output alone, and do so on every DC, not a representative one.

Set RC4DefaultDisablementPhase to audit mode

$DCs = @("DC1","DC2","DC3")
foreach ($DC in $DCs) {
    Invoke-Command -ComputerName $DC -ScriptBlock {
        $path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters'
        if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null }
        New-ItemProperty -Path $path -Name 'RC4DefaultDisablementPhase' `
            -PropertyType DWord -Value 1 -Force | Out-Null
        Write-Host "Audit mode set on $env:COMPUTERNAME" -ForegroundColor Green
    }
}

Query for active RC4 usage

After at least a week of audit logging (longer if you can, because you want to catch weekly batches, monthly processes, and the long tail), query for Event ID 4769 with encryption type 0x17:

$DCs = @("DC1.domain.local","DC2.domain.local")

$RC4Events = foreach ($DC in $DCs) {
    Write-Host "Querying $DC..." -ForegroundColor Cyan
    try {
        Get-WinEvent -ComputerName $DC -FilterHashtable @{
            LogName   = 'Security'
            Id        = 4769
            StartTime = (Get-Date).AddDays(-14)
        } -MaxEvents 20000 -ErrorAction Stop |
        Where-Object { $_.Message -match "Ticket Encryption Type:\s+(0x17|23)\b" } |
        Select-Object `
            @{N='DC';      E={ $DC }},
            @{N='Time';    E={ $_.TimeCreated }},
            @{N='Account'; E={ if ($_.Message -match "Account Name:\s+(\S+)") { $Matches[1] } }},
            @{N='Service'; E={ if ($_.Message -match "Service Name:\s+(\S+)") { $Matches[1] } }},
            @{N='ClientIP';E={ if ($_.Message -match "Client Address:\s+(\S+)") { $Matches[1] } }}
    } catch { Write-Warning "Could not query ${DC}: ${_}" }
}

if ($RC4Events) {
    $RC4Events | Group-Object Account | Sort-Object Count -Descending |
        Select-Object Count, Name | Format-Table -AutoSize
    $RC4Events | Export-Csv "$env:USERPROFILE\Desktop\rc4_audit.csv" -NoTypeInformation
} else {
    Write-Host "No RC4 detected." -ForegroundColor Green
}

Check the KDCSVC events (201 to 209)

These are newer events added in January 2026 specifically to flag RC4 deprecation risks. They live in the System log, not the Security log, which makes them easy to miss:

$DCs = @("DC1","DC2","DC3")

foreach ($DC in $DCs) {
    Write-Host "`nQuerying $DC..." -ForegroundColor Cyan
    Get-WinEvent -ComputerName $DC -FilterHashtable @{
        LogName   = 'System'
        Id        = @(201,202,203,204,205,206,207,208,209)
        StartTime = (Get-Date).AddDays(-7)
    } -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, Id,
        @{N='Message'; E={ $_.Message -replace '\s+', ' ' }} |
    Format-Table -AutoSize -Wrap
}
Event IDMeaningAction
201Service account using RC4 in audit modeInvestigate, plan fix
202RC4 ticket issued in audit modeInvestigate, plan fix
203 / 204RC4 blocked, enforcement modeUrgent fix needed
205RC4 explicitly allowed in DC registryReview registry
206 / 207Service AES-only but account missing AES keysPassword reset
208 / 209Authentication blocked in enforcement modeUrgent fix needed

Phase 2: Fix the Accounts

The null account problem

The most common finding in any environment is a large number of accounts with null msDS-SupportedEncryptionTypes. These accounts have never had the attribute explicitly set, so they fall back to the domain default, which historically includes RC4. Find them with:

$NullAccounts = Get-ADObject -Filter "objectClass -eq 'user' -or objectClass -eq 'computer'" `
    -Properties msDS-SupportedEncryptionTypes |
Where-Object { $null -eq $_."msDS-SupportedEncryptionTypes" }

Write-Host "Null accounts: $($NullAccounts.Count)"
$NullAccounts | Group-Object ObjectClass | Format-Table Count, Name -AutoSize

In one of the larger domains I worked through, this came back with thousands of users and a few hundred computers. None of it because anything was misconfigured deliberately. Just because nobody had ever set the attribute explicitly.

The safe interim step: set to 28, not 24

The instinct might be to go straight to AES-only (value 24). Do not, not yet. Set to 28 first (AES128 + AES256 + RC4). This explicitly configures the accounts while keeping RC4 available as a fallback, giving you time to verify nothing actually needs it before pulling the plug.

# Fix null user accounts (with logging and rollback generation)
$Server = "DC1.domain.local"
$Log = New-Object System.Collections.Generic.List[PSObject]

Get-ADUser -Filter * -Properties msDS-SupportedEncryptionTypes -Server $Server |
Where-Object { $null -eq $_.'msDS-SupportedEncryptionTypes' } |
ForEach-Object {
    try {
        Set-ADUser $_ -KerberosEncryptionType AES128,AES256,RC4 -Server $Server
        Write-Host "Updated: $($_.Name)" -ForegroundColor Green
        $Log.Add([PSCustomObject]@{ Name=$_.Name; DN=$_.DistinguishedName; OldValue="null"; Status="Success" })
    } catch {
        Write-Host "Failed: $($_.Name)" -ForegroundColor Red
        $Log.Add([PSCustomObject]@{ Name=$_.Name; DN=$_.DistinguishedName; OldValue="null"; Status="Error" })
    }
}

$Log | Export-Csv "$env:USERPROFILE\Desktop\account_changes.csv" -NoTypeInformation
Watch out for inetOrgPerson objects

A bulk remediation script that targets only user and computer object classes will miss any inetOrgPerson objects. These are non-standard account types used by some legacy directory integrations. They need Set-ADObject rather than Set-ADUser. Always run a post-change verification check that counts by object class.

The krbtgt account

The krbtgt account is the backbone of Kerberos in your domain. It signs every TGT issued. Finding it at value 0 (null) is serious. Set it carefully:

# Step 1: Set to AES + RC4 (interim)
Set-ADUser krbtgt -KerberosEncryptionType AES128,AES256,RC4

# Verify, should show 28
Get-ADUser krbtgt -Properties msDS-SupportedEncryptionTypes |
    Select-Object Name, msDS-SupportedEncryptionTypes

# Step 2: Reset password twice to generate fresh AES keys
# First reset
$Password = -join ((65..90) + (97..122) + (48..57) + (33..47) |
    Get-Random -Count 64 | ForEach-Object { [char]$_ })
Set-ADAccountPassword -Identity krbtgt -Reset `
    -NewPassword (ConvertTo-SecureString $Password -AsPlainText -Force)

# Check replication, wait 15 to 20 minutes, then second reset
repadmin /replsummary

# Second reset (different password)
$Password = -join ((65..90) + (97..122) + (48..57) + (33..47) |
    Get-Random -Count 64 | ForEach-Object { [char]$_ })
Set-ADAccountPassword -Identity krbtgt -Reset `
    -NewPassword (ConvertTo-SecureString $Password -AsPlainText -Force)
Why reset twice

Changing the encryption setting alone is not enough. The actual AES cryptographic keys are derived from the password. The first reset generates new AES keys and starts replicating. The second reset, after replication, ensures any tickets issued in the gap are also invalidated. You need both for a complete clean slate.

Phase 3: Enforce AES Only

Once you have confirmed through audit logging that nothing is actively using RC4, move everything to value 24 and enforce at the policy level.

Move accounts from 28 to 24

Get-ADObject -Filter * -Properties msDS-SupportedEncryptionTypes, ObjectClass |
Where-Object { 
    $_.ObjectClass -in @("user","computer","inetOrgPerson") -and
    $_."msDS-SupportedEncryptionTypes" -eq 28
} |
ForEach-Object {
    if ($_.ObjectClass -eq 'user') {
        Set-ADUser $_ -KerberosEncryptionType AES128,AES256
    } elseif ($_.ObjectClass -eq 'computer') {
        Set-ADComputer $_ -KerberosEncryptionType AES128,AES256
    } else {
        Set-ADObject $_ -Replace @{"msDS-SupportedEncryptionTypes" = 24}
    }
    Write-Host "Updated: $($_.Name)" -ForegroundColor Green
}

Enforce via GPO

Computer Configuration > Policies > Windows Settings >
Security Settings > Local Policies > Security Options >
"Network security: Configure encryption types allowed for Kerberos"

  DES_CBC_CRC:       unchecked
  DES_CBC_MD5:       unchecked
  RC4_HMAC_MD5:      unchecked
  AES128_HMAC_SHA1:  checked
  AES256_HMAC_SHA1:  checked
  Future enc types:  checked

Set enforcement mode

$DCs = @("DC1","DC2","DC3")
foreach ($DC in $DCs) {
    Invoke-Command -ComputerName $DC -ScriptBlock {
        $path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters'
        if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null }
        New-ItemProperty -Path $path -Name 'RC4DefaultDisablementPhase' `
            -PropertyType DWord -Value 2 -Force | Out-Null
        Write-Host "Enforcement set on $env:COMPUTERNAME" -ForegroundColor Green
    }
}

# Reboot DCs one at a time, the registry change requires a restart.
# Wait for each DC to come back before rebooting the next.

The Old Password Problem

This was the lesson that surprised me most. Setting msDS-SupportedEncryptionTypes to 28 or 24 tells AD what encryption types the account supports. The actual cryptographic keys are generated from the password hash. An account whose password was last set in 2012 (before AES was properly configured in your domain) may only have RC4 keys on record, regardless of what the attribute says.

The symptom is result code 0xE (encryption type not supported) after you enforce AES. The fix is straightforward, reset the password to regenerate the keys. If it is a service account and you know the password, you can reset to the same value:

$Password = Read-Host -AsSecureString "Enter current password"
Set-ADAccountPassword -Identity "sa-serviceaccount" -Reset -NewPassword $Password

If a user account's password is expired rather than unknown, you can use the pwdLastSet trick to clear the expiry without changing the password:

# Must be done in two steps, 0 first, then -1
Set-ADUser -Identity "username" -Replace @{pwdLastSet=0}
Set-ADUser -Identity "username" -Replace @{pwdLastSet=-1}

Find accounts likely to have this problem, any enabled account where the password has not been reset in years:

$CutoffDate = (Get-Date).AddYears(-5)
Get-ADUser -Filter * -Properties PasswordLastSet, msDS-SupportedEncryptionTypes |
Where-Object { $_.PasswordLastSet -lt $CutoffDate -and $_.Enabled -eq $true } |
Select-Object Name, PasswordLastSet, msDS-SupportedEncryptionTypes |
Sort-Object PasswordLastSet |
Export-Csv "$env:USERPROFILE\Desktop\old_passwords.csv" -NoTypeInformation

AWS Managed AD Considerations

If you have a forest trust with an AWS Managed AD directory, there are some hard limits worth knowing before you start. These are AWS's own documented restrictions, not anything quirky about a specific tenant:

AWS has added CloudWatch log forwarding for the 201 to 209 KDCSVC events. Enable this first to get visibility before raising support tickets:

AWS Console > Directory Service > your directory >
Networking & Security > Enable CloudWatch log forwarding

For everything else AWS owns, raise an AWS Support ticket citing CVE-2026-20833 and the July 2026 deadline. Include the specific account DNs and the target value (24). In my experience this gets handled promptly when the deadline context is clear, but expect the round-trip to take several days, so factor it into your timeline.

What the Kerberos Result Codes Mean

When you are monitoring authentication failures, these are the codes that matter and what to do about each one:

CodeMeaningAction
0x6Account not found in domainUsually pre-existing noise: machines in a trusted domain pointing at the wrong DC. Not RC4 related.
0xEEncryption type not supportedOld password with only RC4 keys. Reset password to regenerate AES keys.
0x12Account disabled, locked, or expiredStale profile or service using a leaver's account. Investigate what is running as that account.
0x17Password expiredReset password or use the pwdLastSet trick.
0x18Wrong passwordStored credential not updated after a password change. Check services and scheduled tasks.
0x25Clock skew too largeKerberos requires clocks within 5 minutes. Check NTP sync on the client machine.
0x37Encryption type not supported (service ticket)Same as 0xE but for service tickets. Same fix.

The companion troubleshooting scripts post has a live monitor that watches these in real time, which is useful any time you are making changes that touch authentication.


Quick-Action Checklist

For when you just need to do it. Each step links back to the section above with the detail.

  1. Confirm where you are in Microsoft's timeline. Audit mode (January 2026), enforcement with rollback (April 2026), full enforcement (July 2026). The July 2026 deadline is real and there is no rollback past it.
  2. Enable Kerberos audit logging on every DC. Run auditpol /set, then verify with auditpol /get on every DC, not just one. If a GPO is overriding, configure via Default Domain Controllers Policy.
  3. Set RC4DefaultDisablementPhase to 1 (audit mode) on every DC. No reboot needed for audit mode.
  4. Let audit logging run for at least a week. Two is better, four is better still. Query Event ID 4769 for encryption type 0x17, and System log events 201 to 209.
  5. Find all null and otherwise-explicit-RC4 accounts. Group by object class so you can spot any inetOrgPerson objects you would otherwise miss.
  6. Set accounts to 28 (AES + RC4), not straight to 24. Generates an audit trail and a rollback path while keeping RC4 available as a safety net.
  7. Reset krbtgt twice, waiting at least 15 to 20 minutes between resets for replication. Verify with repadmin /replsummary before the second reset.
  8. Identify accounts with passwords older than ~5 years. These are likely candidates for the old password problem. Plan password resets for them, or use the pwdLastSet trick if you cannot change the password.
  9. Re-run audit checks. Confirm no live RC4 dependencies remain. If you find any, fix and re-audit before moving on.
  10. Move accounts from 28 to 24 (AES-only). Update via GPO too: uncheck RC4_HMAC_MD5 under "Network security: Configure encryption types allowed for Kerberos".
  11. Set RC4DefaultDisablementPhase to 2 (enforcement). Reboot DCs one at a time, waiting for each to come back before the next.
  12. For AWS Managed AD trusts: raise an AWS Support ticket early. The trust object and built-in accounts need AWS to action. Cite the deadline.
  13. Keep the live failure monitor running for a couple of weeks after enforcement. Watch for 0xE and 0x37 codes, which indicate accounts with only RC4 keys. Reset their passwords.

Closing Thoughts

Like most security hardening projects, the work is less interesting than the discipline around the work. Audit before you change. Set things to the safe interim value before the target value. Log every change so you can roll back. Reset twice when keys are involved. Watch for codes 0xE and 0x37 after enforcement and treat them as routine, not panic. The end state is a fleet where every Kerberos ticket is AES-encrypted, every service account has explicit keys, and Kerberoasting is no longer a viable path. That is genuinely worth the weeks it takes.

For the PowerShell tooling I built along the way (live failure monitor, baseline snapshot, bulk change with auto-generated rollback, and a few others that turned out to be useful for far more than RC4), see the companion troubleshooting scripts post.

← All guides