Skip to content

Unconstrained Delegation

Introduction

Alright, so you want to know about one of the most absurd configurations in AD? Welcome to TRUSTED_FOR_DELEGATION. Now... I need you to imagine this: One day, someone comes to you and hands over to you a blank check with his signature already on it. You can type anything you want on it, any value, any sum, literally anything! Crazy, ah? :D That's how I see Unconstrained Delegation to be, an already signed blank check. In the Kerberos world you can be anyone or anything with this!

Discovery

Truth is Unconstrained Delegation has been a part of Windows AD since the release in 2000. Is just that years later cca. 2014~ Security Researchers and Threat Actors realized that this is like the golden-mine, the magic lamp of Privilege Escalation. You can literally become anyone who connects to a machine that has TRUSTED_FOR_DELEGATION enabled, even Domain Admins!

Is Unconstrained Delegation still a thing?

As you've probably guessed, even if Microsoft added Constrained Delegation in 2003 and years later Role-Based Constrained Delegation, Unconstrained Delegation still remains a thing!

We all know how Sys Admins act like :D :

  • Legacy systems that "need" it, but no one bothered to really test if that's the case! (most of the times they don't need it).
  • Lazy Sys Admins who use it as "quick fix" for authentication issues.
  • Lack of understanding about the security implications.
  • "If ain't broke, don't fix it!" mentality (SPOILER ALERT: IT IS BROKE!).

Modern assessments regularly find unconstrained delegation on critical systems like Exchange Servers, SQL Servers, Application Servers. It's like finding unlocked treasure chests scattered around the domain!

Summing it all up:

So, to wrap-it-up Unconstrained Delegation is one of the wildest configurations out there for us attackers. You only need Local Admin on the endpoint and then you are happy, it's like you killed the latest expansion set boss and it dropped all the best loot for your gear or worse loot, but still valuable!

It's terrifying because you can become anyone that connects to that box.

Delegation Types: The Holy Trinity of Privilege Escalation

Since you already saw my post on RBCD (if not, please do), let's break down how all three delegation types compare:

Unconstrained Delegation (The Nuclear Option)

  • What it does: "This computer can be ANYONE to access ANYTHING"
  • Kerberos flow: User's TGT gets stored on the server
  • Scope: Unlimited - any service, any user
  • Attack surface: Massive - compromise the server = game over
  • Real-world analogy: Giving someone your master key and saying "use it however you want"

Constrained Delegation (The "Safer" Option)

  • What it does: "This computer can be specific users to access specific services"
  • Kerberos flow: Uses S4U2Self and S4U2Proxy extensions
  • Scope: Limited to specified services (SPN list)
  • Attack surface: Smaller but still dangerous if misconfigured
  • Real-world analogy: Giving someone keys to specific rooms in your house

Resource-Based Constrained Delegation (RBCD - The "Modern" Option)

  • What it does: "These specific computers can impersonate users to access THIS resource"
  • Kerberos flow: Target resource controls who can delegate to it
  • Scope: Resource-specific control
  • Attack surface: More granular, but can be abused through computer account manipulation
  • Real-world analogy: Each room decides who gets keys to it

Unconstrained Delegation Architecture: The Attack Flow

How Unconstrained Delegation Works:

  1. User Authentication: User authenticates to Domain Controller
  2. TGT Request: User requests access to Server with Unconstrained Delegation
  3. TGT Forwarding: DC includes user's TGT in the service ticket's additional-tickets field
  4. TGT Storage: Server extracts and stores the user's TGT in LSASS memory
  5. Impersonation: Server can now use that TGT to request service tickets for any service as the user

How We Abuse It:

  1. Server Compromise: Gain admin access to machine with unconstrained delegation
  2. Memory Extraction: Dump LSASS memory to extract cached TGTs
  3. Ticket Analysis: Identify high-value user TGTs (Domain Admins, etc.)
  4. Ticket Injection: Use tools like Rubeus to inject tickets into our session
  5. Domain Domination: Access any resource as the compromised user

The Timing:

TGTs have a default lifetime of 10 hours, so you have a decent window to extract and use them. But here's the kicker - every time a user accesses the server, their TGT gets refreshed! So popular servers become TGT collection points.

Alright, let's get into the nitty-gritty of WHY unconstrained delegation is so broken at the Kerberos protocol level. This is where things get really interesting!

The Kerberos Double-Hop Problem (Why Delegation Exists)

Picture this: User Alice wants to access a file on FileServer through WebApp. Here's what happens without delegation:

  1. Alice authenticates to WebApp with her TGT
  2. WebApp gets a service ticket (ST) to access Alice's data
  3. WebApp tries to access FileServer on Alice's behalf
  4. PROBLEM: WebApp only has an ST for itself, not Alice's TGT!
  5. FileServer says "Who is Alice? I only see WebApp!"

This is the double-hop problem - the authentication "hops" twice but the identity gets lost!

Unconstrained Delegation: The Nuclear Solution

Microsoft's solution was basically: "Screw it, let's just give the service the user's ACTUAL TGT!" Here's the low-level flow:

Step 1: The Magic Happens During Service Ticket Request

Here's the key part- it's not APP01 requesting anything! It's the USER requesting a service ticket TO APP01, and the DC does something special:

Normal Service Request:
User -> DC: "Give me a Service Ticket for APP01"
DC -> User: "Here's your ST for APP01" (just the service ticket)

Unconstrained Delegation Request:  
User -> DC: "Give me a Service Ticket for APP01"
DC: *checks APP01's userAccountControl for TRUSTED_FOR_DELEGATION*
DC: "Oh! APP01 has unconstrained delegation, so I'll include the user's TGT too!"
DC -> User: "Here's your ST for APP01 + a COPY of your own TGT!"

Crucial: The user gets BOTH tickets in the response:

  1. The Service Ticket (encrypted with APP01's key)
  2. A copy of their own TGT (encrypted with the user's key)

When the user presents this ticket bundle to APP01, APP01 can decrypt the service ticket (because it's encrypted with APP01's key) AND it also receives the user's TGT in the same package!

The Automatic TGT Collection Problem

This means that APP01 becomes an automatic TGT harvesting machine! Every single user who:

  • Accesses a web application on APP01
  • Connects to a database hosted on APP01
  • Maps a network drive from APP01
  • Runs any service that authenticates against APP01
  • Even just browses to APP01 in Explorer (if it triggers Kerberos auth)

...will have their TGT automatically cached in APP01's LSASS memory!

Typical Day on APP01 with Unconstrained Delegation:
09:00 - Alice (regular user) accesses web app → Alice's TGT cached
10:30 - Bob (finance) connects to database → Bob's TGT cached  
11:15 - Carol (HR) maps network drive → Carol's TGT cached
14:45 - David (Domain Admin) checks server status → David's TGT cached 💀
16:20 - ServiceAccount runs scheduled task → ServiceAccount's TGT cached

End of day: APP01's memory = treasure trove of TGTs!

This is why popular servers with unconstrained delegation are so dangerous - they're like TGT honeypots that automatically collect authentication material from every user who touches them!

Step 2: The User Presents Both Tickets to APP01

This is where it gets really interesting:

What the user sends to APP01:
┌─ Kerberos Authentication Package ─┐
│                                   │
│ 1. Service Ticket (for APP01)     │ <- APP01 can decrypt this
│    - Encrypted with APP01's key   │
│    - Contains session key         │
│                                   │  
│ 2. User's TGT (forwarded)         │ <- APP01 gets this too!
│    - Still encrypted with user's  │
│      password hash                │
│    - Contains user's full privs   │
│                                   │
│ 3. Additional context/session     │
│    information                    │
└───────────────────────────────────┘

Here's the key: APP01 doesn't need to decrypt the user's TGT - it just stores it in memory and can present it back to the DC later when it wants to impersonate the user!

Step 3: APP01 Stores the TGT for Later Use

Here's where it gets juicy! When APP01 receives this authentication package:

APP01's Processing:
1. Decrypt the Service Ticket using APP01's service account key
2. Extract session key and user identity from Service Ticket  
3. **Store the forwarded TGT in LSASS memory cache**
4. User is now authenticated to APP01

Later, when APP01 wants to access FileServer as the user:
1. APP01 retrieves the cached TGT from memory
2. APP01 -> DC: "Here's UserX's TGT, give me a Service Ticket for FileServer"
3. DC: "This is a valid TGT for UserX, here's your ST for FileServer"
4. APP01 -> FileServer: Uses the ST to access resources as UserX

The vulnerability: APP01's LSASS memory now contains the user's TGT in a format that can be extracted and reused by anyone with admin access to APP01!

Step 4: Why Memory Extraction Works

The TGTs sit in LSASS memory in plaintext (after decryption), just waiting to be extracted:

LSASS Memory Structure:
├── Authentication Packages
├── Security Support Providers  
├── Cached Credentials
└── **Kerberos Tickets Cache** <- Our treasure trove!
    ├── User1's TGT (expires in 8 hours)
    ├── User2's TGT (expires in 6 hours)  
    ├── DomainAdmin's TGT (expires in 10 hours) <- JACKPOT!
    └── ServiceAccount's TGT (expires in 9 hours)