This took me a while to get my head around recently so I figured I’d document it here in case it helps someone else.
Thanks to @exploitph for answering several questions about this and @harmj0y for writing this blog post which explains the entire process. Whilst that post does go into a lot of detail, it also covers several other things as well and at the time it was written Windows Server 2019 was not a thing. So I decided to make a post that keeps things relatively brief and explains why this trick no longer works in Server 2019.
A quick Kerberos refresher
Here’s a simplified description of some things you need to know for this post to make any sense:
- A TGT is a special ticket that a user receives from a domain controller when they first log on
- You use the TGT to request service tickets, which let you access services like SQL server, SMB, etc
- Service tickets are encrypted using the password of the user/computer account that runs that service
- Kerberoasting is the name of an attack where we request service tickets and then try to crack them to get the service account’s password
Why do we want to get our own TGT?
There are some built in functions in Windows that let us request Kerberos authentication to a particular service, but the problem for us as an attacker is that these APIs are designed to be opaque. Windows handles everything for us, so we can’t directly control anything in these requests (other than which service we want a ticket for).
This means that the service tickets we get back will be encrypted with whatever level of encryption Windows decides to use. If the user account running the service is configured to support AES encryption then that is what will be used to encrypt the service tickets, which makes the process of cracking the passwords much slower and practically impossible in some cases.
If we could get hold of a usable TGT, then we could use it to craft our own service ticket requests and specify that they only support the weaker RC4 encryption.
Getting a user’s TGT and using it for Kerberoasting
If we know the user’s password (or even just a hash of their password) then we can request a TGT with Rubeus like so:
rubeus.exe asktgt /domain:mydomain.local /user:mydomain.local\username /password:Passw0rd
or in the GUI version:
Then all we need to do is copy the base64 that it gave us (or export to a .kirbi file) and then supply that to the Kerberoast function.
First of all though let’s look at what happens if we try Kerberoasting without supplying our own TGT, just using the normal Windows APIs to request service tickets:
When we let Windows handle things for us we get a service ticket encrypted using the highest level of encryption that the service account supports (in this case AES-128, as the account supports RC4 and AES-128).
Now let’s try again but supply our own TGT that we exported earlier:
As you can see with this method we have successfully downgraded the encryption to RC4, which will be much easier to crack.
So problem solved… right?
Well in this case yes, but it turns out there are two scenarios where this approach won’t work.
Problem 1: The DC we request a TGT from is running Windows Server 2019
If we try the same technique to downgrade the encryption but the domain controller we get a TGT from is running Server 2019 or newer, something different happens.
We supply our own TGT and send the same request as before that says we only support RC4 encryption, as you can see in Wireshark (ARCFOUR = RC4):
But this time the server responds with a service ticket encrypted with AES-128 instead of RC4:
Unfortunately it turns out that any DC running Server 2019 will ignore the encryption type that the client requested and will always use the highest level of encryption that the service account supports.
I’m not aware of any workaround for this at the moment. You’re stuck with AES encryption if the service account is marked as supporting it. Luckily AES is not enabled on new user accounts by default, but it is very easy to enable it.
Problem 2: We have code executing as the user but don’t know their password
Without knowing the user’s password (or a hash of their password) we can’t request a TGT for them like we did before.
Luckily there is another way we can get their TGT, which is known as the TGT delegation trick.
This trick takes advantage of the fact that domain controllers are configured to allow unconstrained delegation by default:
This just means that any service running on this machine is allowed to impersonate users that authenticate with it – a perfectly legitimate thing that services often need to do.
However, for the server to be able to impersonate a user it needs a TGT for that user. So when a client gets a ticket for a service that supports delegation it also sends an additional request to get a new TGT that can be passed to that service for it to use for impersonation.
As an attacker executing code as a domain user we can mimic this behaviour just using the standard Windows APIs, but instead of sending the new TGT to a service we just extract it and use it for Kerberoasting like we did in the original demo.
This is what happens if you use the /tgtdeleg option when Kerberoasting with Rubeus, or select this option in the GUI
This is a clever solution to the problem (assuming you can get Rubeus to run as the user without getting blocked by AV) but as mentioned above it won’t work with 2019 DCs.
I simplified some things in this post as I didn’t want it to be overly long and complex (not sure I succeeded there…) but if you want to understand specific details then these resources should help:
- Kerberos explained in 3 levels of detail: https://www.youtube.com/watch?v=snGeZlDQL2Q
- Kerberos delegation concepts: https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc772815(v=ws.10)?redirectedfrom=MSDN#the-need-for-delegation
- TGT delegation trick: http://www.harmj0y.net/blog/redteaming/rubeus-now-with-more-kekeo/
- Kerberos authentication via Windows APIs: https://docs.microsoft.com/en-us/windows/win32/secauthn/acquirecredentialshandle–kerberos
- Rubeus: https://github.com/GhostPack/Rubeus
- Rubeus GUI: https://github.com/VbScrub/Rubeus-GUI