This article introduces a new attack targeting Group Managed Service Accounts (gMSA), dubbed the “Golden GMSA” attack, allowing attackers to dump Key Distribution Service (KDS) root key attributes and then generate the password for all the associated gMSAs offline.
An attacker with high privileges can obtain all the ingredients for generating the password of any gMSA in the domain at any time with two steps:
- Retrieve several attributes from the KDS root key in the domain
- Use the GoldenGMSA tool to generate the password of any gMSA associated with the key, without a privileged account.
Service accounts’ passwords are commonly not regularly rotated, putting them at risk, especially because they can be targeted through Kerberoasting attacks. A gMSA (group Managed Service Account; lower-case g is a mystery) is a special type of account in Active Directory (AD) introduced in Windows Server 2012 to solve this exact problem. This object’s sole purpose is to be used as a service account, with the important feature of password rotation. A gMSA account can be used by one or more servers with compatible services.
The gMSA’s password is managed by AD. It is automatically rotated every 30 days to a randomly generated password of 256 bytes, making it infeasible to crack. As Microsoft documentation describes:
The password is calculated based on a secret that periodically changes and unnamed attributes of the gMSA. The secret used is stored in the KDS root key object.
Automagically rotating password—how can that be?
The idea behind this type of account is interesting. The writable DCs manage the gMSA’s password and rotate it every 30 days (by default). When a server that uses this account needs to use the gMSA, it first requests the most recent password from the DC by retrieving an attribute called msDS-ManagedPassword. This attribute is a Binary Large Object (BLOB) that contains the password.
The msDS-GroupMSAMembership attribute of a gMSA specifies who is permitted to obtain the password. The writable DC, in turn, uses this attribute to validate that the caller is authorized to obtain it. So far, we can see a straightforward way to abuse it: If an attacker gains access to an account that has access to msDS-ManagedPassword, the attacker can read the password for the specific gMSA.
DCs do not usually store passwords in their databases, but only secrets derived from passwords. That raises a question: Suppose the writable DC does not store the cleartext password; how can it be retrieved by reading the msDS-ManagedPassword attribute? It turns out msDS-ManagedPassword is a constructed attribute, meaning this attribute is not stored in the AD but computed when an authorized user attempts to read it.
How is the msDS-ManagedPassword constructed?
We now know that the password is constructed on demand by the writable DCs. We also know that every writable DC can construct the attribute, and the result will always be the same. This page shows how the password is generated, but it isn’t detailed enough to allow us to construct it ourselves.
Looking for the function that constructs this attribute would lead us to ntdsai.dll!dbGetConstructedAtt, which is the function responsible for constructing attributes.
We can see a call to dbGetManagedServiceAccountPassword, but we are interested only in the password rather than the entire ManagedPasswordBlob. Digging further into the function eventually leads us to a call to the function KdsCli.dll!KdsGetGmsaPasswordBasedOnKeyId.
Let’s review the parameters of this function:
- _SecurityDescriptor is a predefined security descriptor (SD), hardcoded in ntdsai.dll, which grants read and write permissions to S-1-5-9 (enterprise domain controllers), as shown in the following screenshot:
- _SID is the ObjectSID of the desired gMSA.
- _MsdsManagedPasswordID is an attribute of the gMSA object that, among other things, references by GUID the KDS Root Key instance used by the gMSA for password generation.
We did not find formal documentation for this attribute, but more reversing helped us figure out how to construct it ourselves with the parameters shown in the screenshot below.
- _OutPassword is a pointer to a buffer that will store the password.
- Password size is the number of bytes that will be written into OutPassword. It has a hardcoded value of 256.
Retrieving the key and constructing the password
The execution flow ultimately gets to the function GetKeyFromKdsService, which invokes the RPC method GetKey.
We can see that the parameters match the documentation from [MS-GKDI]
Interesting note: GetKey performs access checks against the TargetSD provided by the caller, meaning that we can invoke this function with a security descriptor that grants us access, and the DC will return a key. Unfortunately, the SD is also used in the calculation of the key. So, even though we can always change the SD to bypass the access checks, unless the provided SD is valid, we will get an invalid key. The only valid security descriptor that results in a valid key is the hard-coded one we mentioned before (gmsaSecurityDescriptor), granting access only to enterprise domain controllers (S-1-5-9).
If the caller has the required permissions, this method generates a key and returns it in a Group Key Envelope (GKE) structure. After obtaining the key, we can finally call kdscli.dll!_GenerateGmsaPassword to generate the password.
The sixth and seventh parameters are optional and do not affect the password. So, for this purpose, we can just set them to null.
Recap of gMSA password generation
A gMSA password is generated by calling a function that resides in kdscli.dll (not exported, unfortunately), which we have on any Windows endpoint. Calling this function requires three things:
- msds-ManagedPasswordID of the gMSA (can be retrieved by using an LDAP query)
- GKE, which can be generated by calling the GetKey method on the DC with high privileges via RPC
Implementing GetKey and offline password generation
We attempted to simplify generating gMSA passwords as much as possible, to the extent that it could be done offline. The only RPC call in the process is for GetKey. So, we decided to reverse and port it to C# code that generates a GKE.
We discovered that the key periodically changes, as documented by Microsoft. But the key is derived from attributes of the KDS root key object and three integers (L0KeyID, L1KeyID, L2KeyID) derived from a timestamp, as shown in the following screenshot.
The current timestamp is stored in the msDS-ManagedPasswordID attribute of the gMSA, readable by all domain users by default, so we can retrieve the timestamp and generate those integers.
The only ingredients the DC uses to generate the passwords, but we don’t have, are the following attributes of the KDS root key:
Retrieving those attributes requires read and extended rights on the KDS root key object, which only domain admins have by default. However, those values ARE NOT periodically changed.
This means that if we compromise those values, we can use them to compute a GKE for any gMSA associated with the KDS root key and generate its password offline.
The Golden GMSA Attack
The Golden GMSA attack occurs when an attacker dumps a KDS root key’s relevant attributes and then uses them to generate the password for associated gMSA accounts offline. The Golden GMSA attack is somewhat similar to the Golden Ticket attack, which allows attackers who compromise the krbtgt account to forge Ticket Granting Tickets (TGTs) as long as the krbtgt password remains unchanged.
One notable difference between a Golden Ticket attack and the Golden GMSA attack is that we are not aware of a way of rotating the KDS root key secret. Therefore, if a KDS root key is compromised, there is no way to protect the gMSAs associated with it. The only mitigation in such a scenario is to create new gMSAs with a new KDS root key.
Weaponizing the Golden GMSA attack
We weaponized the Golden GMSA attack in a tool you can download here.
The GoldenGMSA Attack tool can retrieve the necessary attributes from a specified KDS root key object or use values provided by the user to generate a GKE. The tool can also retrieve the msDS-ManagedPasswordID based on a gMSA SID and, of course, generate the gMSA’s password offline.
An attacker can potentially use the password to compromise services that use the gMSA by forging a Silver Ticket or obtaining a Kerberos service ticket for privileged accounts through S4U2Self. If the gMSA has high privileges, the attacker might use it to compromise other resources and, in some cases, compromise the entire forest.
Golden GMSA attack example
An attacker can use the GoldenGMSA tool to enumerate all gMSAs and their associated KDS root keys. An attacker with high privileges can also use the GoldenGMSA tool to dump the KDS root keys.
The attacker can later use the dumped KDS root key BLOB to generate the password of the gMSA completely offline.
The password is Base64 encoded because it could include non-printable characters.
Detecting a Golden GMSA attack
The compromise of a KDS root key does not generate security events by default. Defenders should configure a SACL on the KDS root key objects for everyone reading the msKds-RootKeyData attribute. Once the system access control list (SACL) is configured, any attempt to dump the key data of a KDS root key will generate security event 4662 on the DC where the object type is msKds-ProvRootKey and the account name is not a DC, as demonstrated in the following screenshot:
Defending against Golden GMSA attacks
Group Managed Service Accounts are a great Active Directory feature that mitigates some risks associated with service accounts, such as Kerberoasting attacks. However, the passwords associated with gMSAs are generated using inputs that cannot be rotated if compromised, allowing attackers with high privileges to dump KDS root keys and generate the passwords of the associated gMSAs offline for as long as they exist.
Defenders should monitor abnormal access to KDS root keys, such as non DCs reading the msKds-ProvRootKey attribute and abnormal logon events linked to gMSA accounts.