---
title: "Inside Kerberos Delegation: Why Multi-Tier Windows Apps Need It (and Why It Breaks)"
description: "Walking through the Kerberos double-hop problem and the three generations of delegation in Windows: how Unconstrained, Constrained (S4U2Self / S4U2Proxy), and Resource-Based Constrained Delegation (RBCD) look in LDAP across the GOAD lab."
date: 2026-11-03
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/inside-kerberos-delegation-architecture/
---

## The Kerberos Double-Hop Problem in Enterprise Architecture

In standard single-hop Kerberos authentication (`RFC 4120`), a domain client authenticates to the Key Distribution Center (KDC) via an `AS-REQ` exchange, acquires a Ticket Granting Ticket (`TGT`), and submits a `TGS-REQ` for a target service. The application server (such as an IIS web portal on `CASTELBLACK` at `192.168.56.22`) decrypts the resulting Service Ticket (`TGS`) using its own long-term secret key, establishing the client's identity through the embedded Privilege Attribute Certificate (`PAC`).

The architectural failure occurs when that middle-tier application server must interact with an autonomous back-end resource (such as a database server or an SMB file share on `WINTERFELL` at `192.168.56.11`) *within the security context of the authenticated user*. 

Because Kerberos service tickets are strictly encrypted with the intermediate service's secret key (`HTTP/castelblack`), `CASTELBLACK` cannot simply forward the incoming ticket to `WINTERFELL`. The receiving service would fail to decrypt the ticket and reject the connection. Furthermore, the application server does not possess the caller's cleartext password or master key, precluding it from initiating an independent `AS-REQ` or `TGS-REQ` on the user's behalf.

To address this **double-hop boundary**, Microsoft engineered three successive generations of Kerberos delegation. Each iteration attempts to resolve the tension between operational multi-tier functionality and least-privilege boundary enforcement.

<figure class="diagram">
<svg viewBox="0 0 760 365" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison of the three Kerberos delegation models across the double-hop boundary: Unconstrained delegation forwards the full TGT; Classic Constrained Delegation (KCD) scopes target SPNs on the front-end; Resource-Based Constrained Delegation (RBCD) places delegation control on the back-end target resource.">
<defs>
  <marker id="del-m-right" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow" /></marker>
  <marker id="del-m-crit" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow-crit" /></marker>
</defs>
<text x="12" y="18" class="dg-muted">THE DOUBLE-HOP PROBLEM · THREE GENERATIONS OF KERBEROS DELEGATION</text>
<rect x="12" y="28" width="736" height="142" rx="8" class="dg-box-accent" />
<text x="22" y="44" class="dg-accent dg-compact">THREE GENERATIONS OF ACTIVE DIRECTORY KERBEROS DELEGATION</text>
<rect x="22" y="52" width="228" height="106" rx="6" class="dg-box" />
<text x="32" y="70" class="dg-label">1 · Unconstrained (Win2000)</text>
<text x="32" y="86" class="dg-mono dg-compact">UAC: 0x80000 (524288)</text>
<text x="32" y="100" class="dg-muted dg-compact">Client embeds full TGT in AP-REQ</text>
<text x="32" y="114" class="dg-muted dg-compact">TGT cached in LSASS on front-end</text>
<text x="32" y="128" class="dg-crit dg-compact">Risk: Front-end can impersonate anywhere</text>
<text x="32" y="142" class="dg-accent dg-compact">Defense: Protected Users / NO_DELEGATION</text>
<rect x="262" y="52" width="236" height="106" rx="6" class="dg-box" />
<text x="272" y="70" class="dg-label">2 · Constrained / KCD (2003)</text>
<text x="272" y="86" class="dg-mono dg-compact">msDS-AllowedToDelegateTo</text>
<text x="272" y="100" class="dg-muted dg-compact">Lives on front-end account</text>
<text x="272" y="114" class="dg-muted dg-compact">S4U2Self (Protocol Transition) + Proxy</text>
<text x="272" y="128" class="dg-accent dg-compact">Scoped strictly to explicit target SPNs</text>
<text x="272" y="142" class="dg-mono dg-compact">Requires Domain Admin to configure</text>
<rect x="510" y="52" width="228" height="106" rx="6" class="dg-box-danger" />
<text x="520" y="70" class="dg-label">3 · Resource-Based (2012)</text>
<text x="520" y="86" class="dg-mono dg-compact">msDS-AllowedToActOnBehalf...</text>
<text x="520" y="100" class="dg-muted dg-compact">Security Descriptor on back-end target</text>
<text x="520" y="114" class="dg-muted dg-compact">Back-end resource owner decides trust</text>
<text x="520" y="128" class="dg-crit dg-compact" font-weight="700">GenericWrite on host = Local Admin</text>
<text x="520" y="142" class="dg-accent dg-compact">Works across domain boundaries</text>
<rect x="12" y="180" width="736" height="150" rx="8" class="dg-box" />
<text x="22" y="196" class="dg-muted dg-compact">THE DOUBLE-HOP BOUNDARY · HOW S4U PROTOCOL TRANSITIONS REACH THE TARGET</text>
<rect x="22" y="206" width="194" height="112" rx="6" class="dg-box" />
<text x="32" y="224" class="dg-accent dg-compact">[CLIENT / CALLER]</text>
<text x="32" y="242" class="dg-label">samwell.tarly</text>
<text x="32" y="258" class="dg-mono dg-compact">Domain User · Client</text>
<text x="32" y="274" class="dg-muted dg-compact">Authenticates to web portal</text>
<text x="32" y="290" class="dg-mono dg-compact">Requests HTTP/castelblack</text>
<text x="32" y="304" class="dg-accent dg-compact">Hop 1: TGS Issued</text>
<line x1="216" y1="262" x2="260" y2="262" class="dg-line" marker-end="url(#del-m-right)" />
<rect x="220" y="238" width="38" height="16" rx="3" class="dg-box" />
<text x="239" y="250" text-anchor="middle" class="dg-mono dg-compact" font-size="8">Hop 1</text>
<rect x="266" y="206" width="228" height="112" rx="6" class="dg-box-accent" />
<text x="276" y="224" class="dg-accent dg-compact">[MIDDLE TIER · FRONT-END]</text>
<text x="276" y="242" class="dg-label">CASTELBLACK$ / jon.snow</text>
<text x="276" y="258" class="dg-mono dg-compact">IIS Web Server · 192.168.56.22</text>
<text x="276" y="274" class="dg-muted dg-compact">Cannot forward client TGS</text>
<text x="276" y="290" class="dg-accent dg-compact">Invokes S4U2Self + S4U2Proxy</text>
<text x="276" y="304" class="dg-crit dg-compact">Mints ticket for CIFS/winterfell</text>
<line x1="494" y1="262" x2="538" y2="262" class="dg-line-crit" marker-end="url(#del-m-crit)" />
<rect x="498" y="238" width="38" height="16" rx="3" class="dg-box-danger" />
<text x="517" y="250" text-anchor="middle" class="dg-crit dg-compact" font-size="8">Hop 2</text>
<rect x="544" y="206" width="194" height="112" rx="6" class="dg-box-danger" />
<text x="554" y="224" class="dg-crit dg-compact" font-weight="700">[BACK-END TARGET]</text>
<text x="554" y="242" class="dg-label">DC02 · winterfell</text>
<text x="554" y="258" class="dg-mono dg-compact">CIFS / SQL · 192.168.56.11</text>
<text x="554" y="274" class="dg-muted dg-compact">Validates Delegation ACL</text>
<text x="554" y="290" class="dg-mono dg-compact">Executes as samwell.tarly</text>
<text x="554" y="304" class="dg-crit dg-compact" font-weight="700">Access Granted / Enforced</text>
<rect x="12" y="338" width="736" height="20" rx="4" class="dg-box" style="fill: #0a0c0d; stroke: rgba(255,255,255,0.12);" />
<text x="380" y="352" text-anchor="middle" class="dg-muted dg-compact">EVOLUTION: Unconstrained exposes full TGT ➔ Constrained locks target SPN ➔ RBCD transfers trust to target DACL</text>
</svg>
<figcaption>The three Kerberos delegation models across the two-hop boundary: Unconstrained forwards a live TGT; Classic Constrained (<code>KCD</code>) restricts outgoing SPNs on the middle-tier object; Resource-Based (<code>RBCD</code>) moves the trust decision onto the back-end resource itself.</figcaption>
</figure>

---

## 1. Generation 1: Unconstrained Delegation (`TRUSTED_FOR_DELEGATION`)

Windows 2000 implemented delegation by allowing the client to delegate its primary authentication token directly to the intermediate server.

When a computer or user account has the `TRUSTED_FOR_DELEGATION` bit set (`0x80000` / `524288`) in its `userAccountControl` attribute, the KDC inspects this flag during ticket requests. 

Querying GOAD via LDAP bitwise matching (`1.2.840.113556.1.4.803` for `LDAP_MATCHING_RULE_BIT_AND`):

```bash
$ ldapsearch -x -H ldap://192.168.56.10 -D "samwell.tarly@north.sevenkingdoms.local" -w "Heartsbane" \
    -b "DC=sevenkingdoms,DC=local" "(userAccountControl:1.2.840.113556.1.4.803:=524288)" \
    sAMAccountName dNSHostName userAccountControl | grep -E "^(sAMAccountName|dNSHostName|userAccountControl)"
sAMAccountName: KINGSLANDING$
userAccountControl: 532480
dNSHostName: kingslanding.sevenkingdoms.local
```

The resulting `userAccountControl` value `532480` represents `0x80000 (TRUSTED_FOR_DELEGATION) | 0x2000 (SERVER_TRUST_ACCOUNT)`.

### Protocol Mechanics and Exploitation Dynamics

1. When a client requests a service ticket for an unconstrained host, the KDC sets the `OK-AS-DELEGATE` flag in the `TGS-REP`.
2. The client's Kerberos Security Support Provider (Kerberos SSP) requests a **forwardable TGT** (`KRB_TGS_REQ` with `forwardable` flag).
3. The client embeds this delegated TGT inside the `KRB_CRED` structure within the `GSS-API` authenticator checksum of the `AP-REQ` exchange.
4. Upon decrypting the `AP-REQ`, the intermediate server extracts the caller's TGT and caches it directly inside the Local Security Authority Subsystem Service (`LSASS`) process memory under the caller's LSA Logon Session.
5. The intermediate server can now use this cached TGT to request service tickets to **any service on any host across the entire Active Directory forest** as that caller.

If unconstrained delegation is granted to any non-Domain Controller member server, an attacker with local administrative privileges on that host can execute coercion primitives (such as `MS-RPRN` `RpcRemoteFindFirstPrinterChangeNotificationEx` or `MS-EFSR` `EfsRpcOpenFileRaw`). Coercing authentication from a Domain Controller machine account (`DC01$`) forces it to deposit its machine account TGT into the compromised host's LSASS memory, immediately enabling forest root takeover via `DCSync`.

---

## 2. Generation 2: Classic Constrained Delegation (KCD / `[MS-SFU]`)

Windows Server 2003 introduced the **Service for User (`[MS-SFU]`)** protocol extensions to eliminate middle-tier caching of unconstrained TGTs. Under Constrained Delegation, the middle-tier server is restricted to requesting service tickets for a predefined, explicit list of Service Principal Names stored in the `msDS-AllowedToDelegateTo` attribute.

```bash
$ ldapsearch -x -H ldap://192.168.56.11 -D "samwell.tarly@north.sevenkingdoms.local" -w "Heartsbane" \
    -b "DC=north,DC=sevenkingdoms,DC=local" "(msDS-AllowedToDelegateTo=*)" \
    sAMAccountName userAccountControl msDS-AllowedToDelegateTo | grep -E "^(sAMAccountName|userAccountControl|msDS-Allowed)"
sAMAccountName: CASTELBLACK$
userAccountControl: 4096
msDS-AllowedToDelegateTo: HTTP/winterfell.north.sevenkingdoms.local
msDS-AllowedToDelegateTo: HTTP/winterfell

sAMAccountName: jon.snow
userAccountControl: 16842752
msDS-AllowedToDelegateTo: CIFS/winterfell.north.sevenkingdoms.local
msDS-AllowedToDelegateTo: CIFS/winterfell
```

### Protocol Transition (`S4U2Self`) and Constrained Proxy (`S4U2Proxy`)

Classic Constrained Delegation operates under two distinct modalities governed by `userAccountControl`:

1. **Standard Constrained Delegation (Kerberos Only - `CASTELBLACK$`, `UAC = 4096`):**
   `CASTELBLACK$` possesses `WORKSTATION_TRUST_ACCOUNT (0x1000)` without protocol transition. It is restricted strictly to **`S4U2Proxy`**: it must supply a valid forwardable Kerberos service ticket (`evidence ticket`) received from an authentic client who logged in over Kerberos.
2. **Constrained Delegation with Protocol Transition (`jon.snow`, `UAC = 16842752`):**
   Account `jon.snow` carries bit `0x1000000` (`TRUSTED_TO_AUTH_FOR_DELEGATION`). This bit enables **Protocol Transition**, designed for environments where clients authenticate to web front-ends via NTLM, SAML, or web forms without obtaining a Kerberos ticket:

<figure class="diagram">
<svg viewBox="0 0 760 220" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="S4U2Self and S4U2Proxy protocol transition sequence from compromised service account jon.snow through the Domain Controller KDC to target host WINTERFELL as Administrator.">
<defs><marker id="s4u-seq-c" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow-crit" /></marker></defs>
<text x="12" y="18" class="dg-muted">S4U2SELF + S4U2PROXY PROTOCOL TRANSITION SEQUENCE</text>
<rect x="12" y="34" width="210" height="52" rx="8" class="dg-box-accent" />
<text x="26" y="56" class="dg-label">1 · Compromised Service</text>
<text x="26" y="74" class="dg-mono dg-compact">jon.snow (UAC: 0x1000000)</text>
<line x1="222" y1="60" x2="274" y2="60" class="dg-line-crit" marker-end="url(#s4u-seq-c)" />
<rect x="278" y="34" width="220" height="52" rx="8" class="dg-box" />
<text x="292" y="56" class="dg-label">2 · KDC (S4U2Self)</text>
<text x="292" y="74" class="dg-mono dg-compact">PA-FOR-USER: Administrator</text>
<line x1="388" y1="86" x2="388" y2="122" class="dg-line-crit" marker-end="url(#s4u-seq-c)" />
<rect x="278" y="126" width="220" height="52" rx="8" class="dg-box" />
<text x="292" y="148" class="dg-label">3 · KDC (S4U2Proxy)</text>
<text x="292" y="166" class="dg-mono dg-compact">TGS → CIFS/winterfell</text>
<line x1="498" y1="152" x2="546" y2="152" class="dg-line-crit" marker-end="url(#s4u-seq-c)" />
<rect x="550" y="126" width="198" height="52" rx="8" class="dg-box-danger" />
<text x="564" y="148" class="dg-label">4 · Target Host</text>
<text x="564" y="166" class="dg-crit dg-compact">WINTERFELL$ as Admin</text>
<text x="12" y="204" class="dg-muted">1-2: S4U2Self mints forwardable evidence TGS · 3-4: S4U2Proxy exchanges it for CIFS/winterfell TGS</text>
</svg>
<figcaption><span>Protocol Transition</span> <code>jon.snow</code> invokes <code>S4U2Self</code> (<code>PA-FOR-USER</code>) to obtain a forwardable ticket on behalf of <code>Administrator</code>, then presents it in <code>S4U2Proxy</code> (<code>additional-tickets</code>) to obtain a <code>CIFS/winterfell</code> service ticket.</figcaption>
</figure>

### Attack Vector: SPN Service-Class Substitution (`sname` Mutation)

Kerberos ticket verification contains an architectural decoupling:
- In a `TGS-REP`, the target service identity appears in two locations: the outer unencrypted `sname` field (`Ticket.sname`) and the inner PAC signature fields.
- When `Target Host` validates an incoming `AP-REQ`, it decrypts the ticket using its host secret key (`WINTERFELL$`). Because all services running on `winterfell` under the machine account (`CIFS`, `HOST`, `LDAP`, `HTTP`) share the identical machine account key, the ticket decrypts successfully regardless of the service class.
- Tools such as `Rubeus tgssub` or `impacket-tgssub` rewrite the unencrypted outer `sname` from `CIFS/winterfell` to `LDAP/winterfell`. Because the KDC signs the PAC but does not cryptographically bind the outer `sname` to the service class, the target DC accepts the ticket over LDAP, granting immediate `DCSync` rights.

---

## 3. Generation 3: Resource-Based Constrained Delegation (RBCD)

Classic Constrained Delegation posed a significant administrative challenge: because `msDS-AllowedToDelegateTo` is an outbound permission configured on the *front-end* service, establishing delegation required Domain Admin credentials (`SeEnableDelegationPrivilege`).

Windows Server 2012 reversed the control model with **Resource-Based Constrained Delegation (RBCD)**. Under RBCD, the authorization logic moves entirely to the *back-end target computer*. The target host defines which front-end services can delegate to it by writing a binary Security Descriptor to its own `msDS-AllowedToActOnBehalfOfOtherIdentity` attribute.

```bash
# Active Directory Machine Account Quota Configuration
$ ldapsearch -x -H ldap://192.168.56.10 -D "samwell.tarly@north.sevenkingdoms.local" -w "Heartsbane" \
    -b "DC=sevenkingdoms,DC=local" -s base ms-DS-MachineAccountQuota | grep ms-DS-MachineAccountQuota
ms-DS-MachineAccountQuota: 10
```

### Protocol Dynamics: The Non-Forwardable Ticket Exception

The critical architectural characteristic of RBCD is how the KDC processes `S4U2Self` tickets when `TRUSTED_TO_AUTH_FOR_DELEGATION` is *absent*:

1. Any standard computer account (such as one created through `ms-DS-MachineAccountQuota`) can execute an `S4U2Self` request impersonating `Administrator`.
2. Because the requesting machine account lacks `TRUSTED_TO_AUTH_FOR_DELEGATION` (`0x1000000`), the KDC marks the issued service ticket as **non-forwardable** (`forwardable: 0`).
3. Under Classic Constrained Delegation, submitting a non-forwardable ticket into `S4U2Proxy` fails with `KDC_ERR_BADOPTION`.
4. Under RBCD, the KDC inspects the target host's `msDS-AllowedToActOnBehalfOfOtherIdentity` DACL. **If the caller's SID is present in the target's DACL, the KDC explicitly overrides the non-forwardable check**, issuing a valid delegated service ticket for the target host.

Consequently, any Active Directory principal possessing `GenericWrite`, `GenericAll`, or `WriteProperty (msDS-AllowedToActOnBehalfOfOtherIdentity)` over a computer object can execute an RBCD attack to achieve local administrator access on that computer.

---

## Telemetry and Defensive Engineering

### Delegation Event Telemetry Matrix

| Event ID | Log Provider | Event Name | Technical Context |
| :--- | :--- | :--- | :--- |
| `4769` | Security | Kerberos Service Ticket Operations | Service Name matches `krbtgt` or target SPN; Ticket Options contain `0x40810010` (Constrained Delegation / S4U). |
| `4768` | Security | Kerberos TGT Request | Monitored for anomalous `PreAuthType = 16` or unexpected machine accounts requesting forwardable TGTs. |
| `5136` | Directory Service | Directory Object Modified | Attribute `msDS-AllowedToActOnBehalfOfOtherIdentity` or `msDS-AllowedToDelegateTo` written to directory objects. |
| `4741` | Security | Computer Account Created | Monitors standard user accounts exploiting `ms-DS-MachineAccountQuota` to provision rogue machine accounts. |

```kql
// KQL Query: Detect Suspicious RBCD Security Descriptor Modifications
SecurityEvent
| where EventID == 5136
| where AttributeLDAPDisplayName =~ "msDS-AllowedToActOnBehalfOfOtherIdentity"
| project TimeGenerated, Computer, SubjectUserName, ObjectDN, AttributeValue
```

### Remediation and Hardening Protocols

1. **Disable Machine Account Quota:** Set `ms-DS-MachineAccountQuota` to `0` across all domains to prevent standard users from provisioning machine accounts.
2. **Apply Protected Users Security Group:** Place administrative accounts in `CN=Protected Users,CN=Users,DC=...`. Members of this group cannot be delegated via unconstrained or constrained delegation (`bForwardable` is enforced to false).
3. **Configure NOT_DELEGATED Flag:** Set the `Account is sensitive and cannot be delegated` flag (`userAccountControl` bit `0x100000`) on all Tier-0 service and user accounts.
4. **Audit Cross-Service SPN Delegation:** Eliminate wildcard or DC delegation paths (`CIFS/winterfell`) in `msDS-AllowedToDelegateTo`.

---

## Delegation Models Comparison

| Delegation Architecture | Governing LDAP Attribute / Flag | Configuration Permission | Exploitation Vector | Hardening Protocol |
| :--- | :--- | :--- | :--- | :--- |
| **Unconstrained** | `userAccountControl: 0x80000` | Domain Admin (`SeEnableDelegationPrivilege`) | Coerce DC authentication via `MS-RPRN`/`MS-EFSR`, dump TGT from LSASS. | Strip `TRUSTED_FOR_DELEGATION` on all non-DCs; enroll admins in `Protected Users`. |
| **Constrained (Kerberos Only)** | `msDS-AllowedToDelegateTo` | Domain Admin (`SeEnableDelegationPrivilege`) | Service ticket `sname` substitution via `tgssub` (`HTTP` to `LDAP`). | Prohibit delegation to DC SPNs; enforce AES-only Kerberos encryption. |
| **Constrained with Protocol Transition** | `msDS-AllowedToDelegateTo` + `UAC: 0x1000000` | Domain Admin (`SeEnableDelegationPrivilege`) | Arbitrary identity impersonation via unauthenticated `S4U2Self`. | Strip `TRUSTED_TO_AUTH_FOR_DELEGATION`; mark privileged accounts as sensitive. |
| **Resource-Based (RBCD)** | `msDS-AllowedToActOnBehalfOfOtherIdentity` | Principal with `GenericWrite` on target computer | Combine `MAQ=10` with target DACL injection to mint delegated tickets. | Set `ms-DS-MachineAccountQuota = 0`; remove non-default DACL write rights on hosts. |

---

## Evidence and limits

The LDAP attributes, ticket transitions, and exploitation examples were checked in the GOAD domains used by the surrounding walkthroughs. The article uses Microsoft protocol specifications for the parts the lab alone cannot prove: ticket flag semantics, S4U request structure, and the KDC's delegation rules. Command output is evidence of this configuration, not a substitute for those protocol definitions.

The post does not claim every delegation entry is exploitable. Account sensitivity, service-class behavior, patch level, SPN ownership, encryption types, and reachable services all change the result. Detection fields also depend on audit policy and log collection; the listed event IDs are starting points that must be validated in the defender's own environment.

## References

- **Microsoft Open Specifications, `[MS-SFU]` Kerberos Protocol Extensions: Service for User:** [learn.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/3bff5864-8135-400e-bdd9-33b552051d94)
- **Microsoft Open Specifications, `[MS-PAC]` Privilege Attribute Certificate Data Structure:** [learn.microsoft.com](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac)
- **Microsoft, Kerberos Constrained Delegation Overview:** [learn.microsoft.com](https://learn.microsoft.com/en-us/windows-server/security/kerberos/kerberos-constrained-delegation-overview)
- **RFC 4120, The Kerberos Network Authentication Service (V5):** [datatracker.ietf.org/doc/html/rfc4120](https://datatracker.ietf.org/doc/html/rfc4120)
- **MITRE ATT&CK, Steal or Forge Kerberos Tickets (T1558):** [attack.mitre.org/techniques/T1558](https://attack.mitre.org/techniques/T1558)