Credential Guard and Kerberos delegation
The vast majority of red team exercises that I (and my team, of course) have been doing lately are assumed breach scenarios. In an assumed breach scenario (and we cover this in the amazing SEC565: Red Team Operations and Adversary Emulation SANS course that I also teach!) red team is usually given access as a non-privileged domain user, simulating an attacker that has someone already established the first foothold in the organization.
This works quite well as we know that eventually the attacker will succeed and perhaps get a victim (most of the time through some kind of social engineering) to execute their binary. So the first part in such an engagement is to create a malicious binary (an implant) that will evade security controls in the target organization. Most of red teams will have specialists for this.
The next step includes delivery of implant and execution in context of a regular, non-privileged domain user, on the workstation designated for the red team exercise. And if everything works well, we’ll get that beacon communicating to our front end servers.
What now? While there are many things we do next, such as getting some awareness about the organization, setting up persistence, trying to move laterally, there are cases when we would like to fetch the user’s password, or their TGT (Ticket Granting Ticket) for Kerberos. Some actions will not need this, as we can use the builtin Windows authentication of the process our beacon is running under, but if you want, for example, to start a SOCKS proxy and tunnel some tools from your office, we will need to authenticate to target services, and for that we will either need the user’s password, their password hash or TGT. How do we get one through our implant, considering that we do not have local administrator privileges yet?
Unconstrained delegation
Back in 2018, Benjamin Deply, the famous Mimikatz/Kekeo author published a very interesting method (https://x.com/gentilkiwi/status/998219775485661184) of obtaining a user’s TGT without requiring administrator privileges.
The trick is the following: as our implant is running under a regular user, that is already authenticated, we will abuse Kerberos GSS-API to ask for a ticket for a service, but not any service – a service that has been configured for unconstrained delegation!
The idea is the following – as we will be requesting a service ticket for a service that is configured for unconstrained delegation, the resulting response that we will receive from a domain controller will also include our own TGT. In a normal workflow, this response is converted to an application request (AP-REQ) that is sent to the target service.
AP-REQ is made up of two components: a ticket and an authenticator. We are interested in the authenticator – it is encrypted with the ticket session key which is known to us, and to the target service that we want to access. And this is were Benjamin’s great research comes into place – if we request a service ticket for a service that has been configured for unconstrained delegation, the authenticator component will contain our TGT (since the target service will need it)!
In other words, we can carve out the TGT of the currently logged in user, without needing administrator privileges! This functionality exists in Rubeus, but if you are running your Cobalt Strike implant (in SEC565 we use Cobalt Strike and Empire), it is better to use a BOF for this purpose. There are several BOF’s you can use, one I like is the tgtdelegation BOF available at https://github.com/connormcgarr/tgtdelegation
Before we start using it, one thing we did not mention is how to find a service that has been configured for unconstrained delegation. This is actually trivial as Domain Controllers are configured for unconstrained delegation by default, so we can use, for example, CIFS/domain.controller or HOST/domain.controller as target SPN’s.
The figure above shows how easy it is to fetch the TGT. You can see how the BOF displayed the AP-REQ output, extracted the session key and identified the encryption algorithm (AES256) and finally (not visible) extracted the TGT.
Credential Guard
By fetching a TGT we can now perform a number of other things, including relaying traffic through a SOCKS proxy. So in a recent engagement I tried to do this but all requests failed – every single time the response received did not contain a TGT, even though the target service indeed was configured for unconstrained delegation, and the account used was not marked as “Account is sensitive and cannot be delegated.”
In other words, we can see that the AP-REQ was indeed received, but it did not contain our TGT in the authenticator part of the response. What could cause this?
After some time and research, it turned out that the reason for this was Credential Guard, which was enabled on the client machine.
Among other (great) security features that Credential Guard brings, one thing that is important for this particular attack (or abuse) is that Credential Guard completely blocks Kerberos Unconstrained delegation, which effectively blocks us from extracting the TGT (and will break any application that relies on this feature as well!).
Besides this, Credential Guard also blocks NTLMv1 completely and there are a number of other nice security controls, as listed https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/
Test and enable!
In engagements I do I still do not see Credential Guard enabled in many enterprises. No wonder since it can break some things, however as Microsoft is now enabling Credential Guard by default in Windows 11 22H2 and Windows Server 2025, it is definitely worth checking whether your organization is ready for a wide adoption of it. It will not stop every attack, but every single step will help!
Thanks to my team members Luka, Neven, Fran and Mislav for debugging! In a RT you need a team!
--
Bojan
@bojanz
@bojanz.bsky.social
INFIGO IS
Comments