Overview of the Kerberos Protocol
Kerberos is the default authentication protocol for Active Directory domains in Windows Server 2022. Developed at MIT and standardized in RFC 4120, it uses symmetric-key cryptography and a trusted third party — the Key Distribution Center (KDC) — to authenticate principals (users, computers, and services) without transmitting passwords over the network. The KDC runs on every Active Directory domain controller and consists of two logical services: the Authentication Service (AS) and the Ticket-Granting Service (TGS).
When a user logs on to a domain, the following exchange occurs. The client sends an Authentication Service Request (AS-REQ) to the KDC, identifying the user principal. The KDC validates the request and issues an AS-REP containing a Ticket-Granting Ticket (TGT) encrypted with the krbtgt account’s secret key, along with a session key for the client encrypted with the user’s key (derived from their password). The client caches the TGT in its Ticket Cache. When the user wants to access a service, the client sends a Ticket-Granting Service Request (TGS-REQ) to the KDC, presenting the TGT. The KDC issues a Service Ticket (TGS-REP) encrypted with the target service’s key. The client presents this Service Ticket to the service, which decrypts it using its own key to verify authenticity. No passwords are transmitted at any point in this exchange.
Service Principal Names
A Service Principal Name (SPN) is a unique identifier for a service instance. It associates a running service with the domain account under which it runs, enabling Kerberos to look up the correct account to generate and encrypt a service ticket. An SPN follows the format ServiceClass/Host:Port/ServiceName. For example, an HTTP service on a web server might have the SPN HTTP/webserver.corp.local.
SPNs are registered as attributes on the Active Directory service account. Use the setspn command-line tool to manage SPNs:
# List all SPNs registered to a specific account
setspn -L CORPsvc-sqlprod
# List all SPNs registered to a computer account
setspn -L CORPSQLSERVER01$
# Add an SPN to a service account
setspn -S MSSQLSvc/sqlserver01.corp.local:1433 CORPsvc-sqlprod
setspn -S MSSQLSvc/sqlserver01:1433 CORPsvc-sqlprod
# Add HTTP SPNs for a web application service account
setspn -S HTTP/webapp.corp.local CORPsvc-webapp
setspn -S HTTP/webapp CORPsvc-webapp
# Remove an SPN
setspn -D MSSQLSvc/oldserver.corp.local:1433 CORPsvc-sqlprod
# Search for duplicate SPNs across the entire domain (critical for troubleshooting)
setspn -X -F
Duplicate SPNs are a common source of Kerberos failures. If the same SPN is registered to two different accounts, the KDC cannot determine which account to use for ticket encryption and will fall back to NTLM or fail authentication entirely. The setspn -X -F command scans the entire forest for duplicate SPNs and should return no output if the environment is healthy.
You can also query SPNs using PowerShell and the Active Directory module:
# Find all accounts with a specific SPN
Get-ADObject -Filter { ServicePrincipalName -like "MSSQLSvc/*" } `
-Properties ServicePrincipalName, SamAccountName |
Select-Object SamAccountName, ServicePrincipalName
# Find all service accounts (users with SPNs registered)
Get-ADUser -Filter { ServicePrincipalName -ne "$null" } `
-Properties ServicePrincipalName, PasswordLastSet, PasswordNeverExpires |
Select-Object SamAccountName, ServicePrincipalName, PasswordLastSet
Viewing Kerberos Tickets with klist
The klist command-line tool displays the contents of the current user’s Kerberos ticket cache. It is the primary diagnostic tool for confirming that Kerberos tickets are being issued and inspecting their properties:
# Display all cached Kerberos tickets for the current session
klist
# Display the TGT specifically
klist tgt
# Purge all tickets from the cache (forces re-authentication)
klist purge
# Display tickets for all logon sessions (requires elevation)
klist sessions
# Display tickets for a specific logon session
klist -li 0x3e7
The output of klist shows each cached service ticket with its client, server, KerbTicket Encryption Type (e.g., AES256-CTS-HMAC-SHA1-96), ticket flags (FORWARDED, RENEWABLE, PRE-AUTHENT), start time, end time, and renew-until time. Confirming the encryption type is AES256 or AES128 (rather than RC4-HMAC) is important from a security hardening perspective, as RC4 (NTLM hash-based) Kerberos encryption is considered weak and susceptible to pass-the-hash and kerberoasting attacks.
The kerbtray.exe tool from the Windows Support Tools provides a graphical interface for viewing and managing Kerberos tickets in the system tray, though in modern Windows Server 2022 environments, klist in an elevated PowerShell or command prompt is the standard approach.
Kerberos Delegation Types
Kerberos delegation allows a service to impersonate a client when connecting to a backend service on the client’s behalf. There are three delegation models in Windows Server 2022 Active Directory environments, each with different security implications.
Unconstrained delegation is the oldest and most permissive model. When a computer or service account is configured for unconstrained delegation, it can impersonate any user to any service anywhere in the domain. The delegating service caches the user’s TGT, which can be stolen and used for lateral movement. This setting should be avoided on modern deployments. To identify accounts with unconstrained delegation:
# Find computer accounts with unconstrained delegation
Get-ADComputer -Filter { TrustedForDelegation -eq $true } `
-Properties TrustedForDelegation, Description |
Select-Object Name, TrustedForDelegation, Description
# Find user/service accounts with unconstrained delegation
Get-ADUser -Filter { TrustedForDelegation -eq $true } `
-Properties TrustedForDelegation |
Select-Object SamAccountName, TrustedForDelegation
Constrained delegation limits delegation to a specific list of services defined in the AD account’s msDS-AllowedToDelegateTo attribute. It prevents the delegating service from impersonating users to arbitrary services. Configure constrained delegation:
# Configure constrained delegation for a service account
Set-ADUser -Identity "svc-webapp" `
-TrustedForDelegation $false
# Allow delegation only to specific backend services
Set-ADAccountControl -Identity "svc-webapp" -TrustedToAuthForDelegation $true
Set-ADUser -Identity "svc-webapp" `
-Add @{ "msDS-AllowedToDelegateTo" = @(
"MSSQLSvc/sqlserver01.corp.local:1433",
"MSSQLSvc/sqlserver01:1433"
)}
Resource-Based Constrained Delegation (RBCD) is the modern and most flexible model, introduced with Windows Server 2012. Unlike traditional constrained delegation, which is configured on the front-end service account, RBCD is configured on the back-end resource (the service being accessed). The resource controls who is allowed to delegate to it. RBCD does not require domain administrator privileges to configure — only write access to the msDS-AllowedToActOnBehalfOfOtherIdentity attribute on the target computer:
# Configure RBCD: allow svc-webapp to delegate to SQLSERVER01
$frontEnd = Get-ADServiceAccount -Identity "svc-webapp"
Set-ADComputer -Identity "SQLSERVER01" `
-PrincipalsAllowedToDelegateToAccount $frontEnd
# Verify the RBCD configuration
Get-ADComputer -Identity "SQLSERVER01" `
-Properties PrincipalsAllowedToDelegateToAccount |
Select-Object -ExpandProperty PrincipalsAllowedToDelegateToAccount
# To allow multiple front-end services
$principals = @(
Get-ADServiceAccount -Identity "svc-webapp",
Get-ADServiceAccount -Identity "svc-api"
)
Set-ADComputer -Identity "SQLSERVER01" -PrincipalsAllowedToDelegateToAccount $principals
To clear RBCD configuration from a resource:
Set-ADComputer -Identity "SQLSERVER01" -PrincipalsAllowedToDelegateToAccount $null
Protecting Privileged Accounts from Delegation
High-privilege accounts such as Domain Admins should be protected from delegation so that even if a compromised delegating service holds their TGT or service ticket, it cannot be used to impersonate them to other services. Mark sensitive accounts with the AccountNotDelegated flag:
# Mark a specific admin account as sensitive and cannot be delegated
Set-ADAccountControl -Identity "da-adminuser" -AccountNotDelegated $true
# Apply to all members of Domain Admins
Get-ADGroupMember "Domain Admins" | ForEach-Object {
Set-ADAccountControl -Identity $_.SamAccountName -AccountNotDelegated $true
Write-Host "Protected: $($_.SamAccountName)"
}
Alternatively, add the account to the Protected Users security group, which enforces a set of credential protection policies including: no delegation is allowed, no NTLM authentication is permitted, only AES encryption is used for Kerberos, and TGTs expire after 4 hours rather than the default 10 hours.
Add-ADGroupMember -Identity "Protected Users" -Members "da-adminuser"
Kerberos Armoring (FAST)
Kerberos Flexible Authentication Secure Tunneling (FAST), also known as Kerberos armoring, is a security enhancement that wraps Kerberos authentication exchanges in a protective tunnel using a computer’s TGT. This prevents offline dictionary attacks against AS-REQ messages for user accounts that have Kerberos pre-authentication enabled, and enables compound authentication for Dynamic Access Control. FAST requires all DCs to run Windows Server 2012 or later and is enabled via Group Policy.
Configure Kerberos armoring via GPO at Computer Configuration > Administrative Templates > System > KDC:
Set KDC support for claims, compound authentication and Kerberos armoring to Supported or Always provide claims on all DCs. Set Kerberos client support for claims, compound authentication and Kerberos armoring to Supported on client machines.
# Verify armoring configuration via registry on a DC
Get-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindowsCurrentVersionPoliciesSystemKDCParameters" |
Select-Object EnableCbacAndArmor, CbacAndArmoringLevel
Troubleshooting Kerberos Errors
Kerberos authentication failures are recorded in the Security event log. The most important event for failed Kerberos pre-authentication is Event ID 4771 (Kerberos pre-authentication failed), and Event ID 4768 records TGT requests (both successful and failed). Query these events:
Get-WinEvent -ComputerName DC01 -FilterHashtable @{
LogName = 'Security'
Id = 4771, 4768, 4769
StartTime = (Get-Date).AddHours(-2)
} | Select-Object TimeCreated, Id, Message | Format-Table -AutoSize -Wrap
Event ID 4771 includes a Failure Code field that maps to standard Kerberos error codes. Common codes and their meanings:
0x6 (KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) — the client user account was not found. Check for typos in the username or a deleted/disabled account.
0x12 (KRB5KDC_ERR_CLIENT_REVOKED) — the account is disabled, expired, or locked out. Check Get-ADUser -Identity username -Properties LockedOut, Enabled, AccountExpires.
0x17 (KRB5KDC_ERR_KEY_EXPIRED) — the user’s password has expired. Reset the password or check PasswordExpired attribute.
0x18 (KRB5KDC_ERR_PREAUTH_FAILED) — incorrect password provided during pre-authentication.
0x25 (KRB5KDC_ERR_PREAUTH_REQUIRED) — the account requires pre-authentication but the client did not send it. This can indicate a client-side misconfiguration or a Kerberoasting attempt against an account with pre-auth disabled.
0x7 (KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) — the SPN for the target service was not found. This is the most common Kerberos service ticket error. Use setspn -X -F to check for missing or duplicate SPNs.
For clock skew errors (KRB5KRB_AP_ERR_SKEW), the client and server clocks differ by more than the Kerberos tolerance (default 5 minutes). Verify time synchronization:
# Check time sync status on domain members
w32tm /query /status
w32tm /query /source
# Force time sync on a domain member
w32tm /resync /force
# Check the PDC Emulator time source (all DCs sync to the PDC)
w32tm /query /computer:PDCEMULATOR01 /status
For deeper Kerberos tracing, enable Kerberos debug logging on the client to capture the full exchange in a log file:
# Enable Kerberos debug logging via registry
reg add "HKLMSYSTEMCurrentControlSetControlLsaKerberosParameters" `
/v LogLevel /t REG_DWORD /d 1 /f
# View Kerberos log entries in the System event log
Get-WinEvent -LogName System |
Where-Object { $_.ProviderName -eq "Microsoft-Windows-Security-Kerberos" } |
Select-Object TimeCreated, Id, Message |
Format-Table -AutoSize -Wrap
# Disable Kerberos debug logging when done
reg add "HKLMSYSTEMCurrentControlSetControlLsaKerberosParameters" `
/v LogLevel /t REG_DWORD /d 0 /f
Securing Kerberos in Windows Server 2022
Several hardening steps improve Kerberos security in Windows Server 2022 environments. Disable RC4 encryption for Kerberos to prevent kerberoasting and downgrade attacks. Configure this via Group Policy at Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options > Network security: Configure encryption types allowed for Kerberos and deselect DES_CBC_CRC, DES_CBC_MD5, and RC4_HMAC_MD5, leaving only AES128_HMAC_SHA1 and AES256_HMAC_SHA1 enabled.
Ensure the krbtgt account password is rotated periodically (at least annually, or after any potential credential compromise). Because the krbtgt account key is used to encrypt all TGTs in the domain, compromising it allows forging arbitrary TGTs (Golden Ticket attack). Rotate the password twice in sequence to invalidate all existing TGTs:
# Reset the krbtgt account password (do this twice, 12+ hours apart)
# WARNING: This invalidates all current TGTs and will require users to re-authenticate
$newPassword = ConvertTo-SecureString (
[System.Web.Security.Membership]::GeneratePassword(64, 10)
) -AsPlainText -Force
Set-ADAccountPassword -Identity "krbtgt" `
-NewPassword $newPassword `
-Reset
# Wait for replication to all DCs before the second reset
repadmin /syncall /AdeP
# Check krbtgt password last set date
Get-ADUser -Identity "krbtgt" -Properties PasswordLastSet |
Select-Object SamAccountName, PasswordLastSet
Additionally, audit service accounts for those configured with Do not require Kerberos preauthentication (UF_DONT_REQUIRE_PREAUTH) as these are vulnerable to AS-REP Roasting, where an attacker can request an AS-REP without credentials and attempt to crack the encrypted portion offline:
# Find accounts with Kerberos pre-authentication disabled
Get-ADUser -Filter { DoesNotRequirePreAuth -eq $true } `
-Properties DoesNotRequirePreAuth, PasswordLastSet, Enabled |
Select-Object SamAccountName, DoesNotRequirePreAuth, PasswordLastSet, Enabled
# Re-enable pre-authentication on vulnerable accounts
Set-ADAccountControl -Identity "svc-vulnerable" -DoesNotRequirePreAuth $false