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 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:
January 2026, audit mode
New KDCSVC event IDs 201 to 209 added to the System log. RC4DefaultDisablementPhase registry key introduced. No behaviour changes yet.
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.
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:
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:
- Bulk encryption-type changes on accounts (null then 28, then 28 then 24). Every bulk run wrote a CSV change log and auto-generated a PowerShell rollback script that would revert every changed account to its previous value, including back to null where appropriate. If anything went wrong after a batch, one command would put things back. The bulk-change-with-rollback pattern is in the companion post.
- GPO enforcement of AES-only Kerberos. Set via Default Domain Controllers Policy. Easy to revert by unchecking the policy entries and re-running
gpupdate. Useful as a quick brake if monitoring shows authentication failures spreading. - The
RC4DefaultDisablementPhaseregistry key, moving from1(audit) to2(enforcement) on each DC. The easiest rollback of all: change the value back to1, reboot the DC. This rollback path disappears after the July 2026 update removes the key entirely, which is the deadline pressure in concrete form.
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.
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
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 ID | Meaning | Action |
|---|---|---|
| 201 | Service account using RC4 in audit mode | Investigate, plan fix |
| 202 | RC4 ticket issued in audit mode | Investigate, plan fix |
| 203 / 204 | RC4 blocked, enforcement mode | Urgent fix needed |
| 205 | RC4 explicitly allowed in DC registry | Review registry |
| 206 / 207 | Service AES-only but account missing AES keys | Password reset |
| 208 / 209 | Authentication blocked in enforcement mode | Urgent 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
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)
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:
- You cannot RDP into the managed DCs or run auditpol on them directly.
- The built-in Administrator, Guest, and krbtgt accounts cannot be modified via PowerShell. This is by design, the rights are reserved to AWS.
- Service accounts that RDS or other AWS services create automatically (commonly named with an AWS-prefixed pattern) are AWS-managed and cannot be modified directly.
- The trust object on the AWS side may return an "illegal modify operation" error even with admin rights.
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:
| Code | Meaning | Action |
|---|---|---|
0x6 | Account not found in domain | Usually pre-existing noise: machines in a trusted domain pointing at the wrong DC. Not RC4 related. |
0xE | Encryption type not supported | Old password with only RC4 keys. Reset password to regenerate AES keys. |
0x12 | Account disabled, locked, or expired | Stale profile or service using a leaver's account. Investigate what is running as that account. |
0x17 | Password expired | Reset password or use the pwdLastSet trick. |
0x18 | Wrong password | Stored credential not updated after a password change. Check services and scheduled tasks. |
0x25 | Clock skew too large | Kerberos requires clocks within 5 minutes. Check NTP sync on the client machine. |
0x37 | Encryption 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.
- 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.
- Enable Kerberos audit logging on every DC. Run
auditpol /set, then verify withauditpol /geton every DC, not just one. If a GPO is overriding, configure via Default Domain Controllers Policy. - Set
RC4DefaultDisablementPhaseto1(audit mode) on every DC. No reboot needed for audit mode. - 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. - Find all null and otherwise-explicit-RC4 accounts. Group by object class so you can spot any inetOrgPerson objects you would otherwise miss.
- 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.
- Reset krbtgt twice, waiting at least 15 to 20 minutes between resets for replication. Verify with
repadmin /replsummarybefore the second reset. - 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
pwdLastSettrick if you cannot change the password. - Re-run audit checks. Confirm no live RC4 dependencies remain. If you find any, fix and re-audit before moving on.
- 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".
- Set
RC4DefaultDisablementPhaseto2(enforcement). Reboot DCs one at a time, waiting for each to come back before the next. - 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.
- Keep the live failure monitor running for a couple of weeks after enforcement. Watch for
0xEand0x37codes, 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