Certifried: Active Directory Domain Privilege Escalation (CVE-2022–26923)
In this blog post, we’ll dive into a recently patched Active Directory Domain Privilege Escalation vulnerability that I reported through ZDI to Microsoft.
In essence, the vulnerability allowed a low-privileged user to escalate privileges to domain administrator in a default Active Directory environment with the Active Directory Certificate Services (AD CS) server role installed. At Institute For Cyber Risk, we see AD CS environments on almost every engagement. It’s rare that we see large and medium-sized Active Directory environments without AD CS installed. The vulnerability was patched as part of the May 2022 Security Updates from Microsoft.
In Summer 2021, Will Schroeder and Lee Christensen published their excellent whitepaper Certified Pre-Owned: Abusing Active Directory Certificate Services which took a deep dive into the security of Active Directory Certificate Services (AD CS). The whitepaper thoroughly explained various tricks for persistence, theft, and privilege escalation — but also defensive guidance and general documentation on AD CS.
When I initially read the whitepaper from Will Schroeder and Lee Christensen, I only began researching into abusing misconfigurations. It was not until December 2021 when I got inspired by Charlie Clark’s (@exploitph) blog post on CVE-2021–42287 and CVE-2021–42278 that I started to look into actual vulnerabilities related to AD CS.
Introduction to Active Directory Certificate Services
If you already feel comfortable with the basics of Active Directory Certificate Services, you can skip this section. On the other hand, if you’re still feeling a bit perplexed about public key infrastructure (PKI) and certificates after reading this section, don’t worry. For this vulnerability, you can think of a certificate as merely a prove of identification, similar to a Kerberos ticket.
If you haven’t already, I highly recommend reading the shortened version of “Certified Pre-Owned” before continuing. I’ll try to cover some details throughout this post as well, but Will Schroeder and Lee Christensen has already done a great job at explaining the essentials, so here’s a snippet from their blog post that perfectly summarizes AD CS.
AD CS is a server role that functions as Microsoft’s public key infrastructure PKI implementation. As expected, it integrates tightly with Active Directory and enables the issuing of certificates, which are X.509-formatted digitally signed electronic documents that can be used for encryption, message signing, and/or authentication.
The information included in a certificate binds an identity (the subject) to a public/private key pair. An application can then use the key pair in operations as proof of the identity of the user. Certificate Authorities (CAs) are responsible for issuing certificates.
At a high level, clients generate a public-private key pair, and the public key is placed in a certificate signing request (CSR) message along with other details such as the subject of the certificate and the certificate template name. Clients then send the CSR to the Enterprise CA server. The CA server then checks if the client is allowed to request certificates. If so, it determines if it will issue a certificate by looking up the certificate template AD object […] specified in the CSR. The CA will check if the certificate template AD object’s permissions allow the authenticating account to obtain a certificate. If so, the CA generates a certificate using the “blueprint” settings defined by the certificate template (e.g., EKUs, cryptography settings, issuance requirements, etc.) and using the other information supplied in the CSR if allowed by the certificate’s template settings. The CA signs the certificate using its private key and then returns it to the client.
That’s a lot of text. So here’s a graphic:
In essence, users can request a certificate based on a predefined certificate template. These templates specifies the settings for the final certificate, e.g. whether it can be used for client authentication, what properties must be defined, who is allowed to enroll, and so on. While AD CS can be used for many different purposes, we will only focus on the client authentication aspect of AD CS.
So, let’s just make a quick example on how certificates can be used for authentication in Active Directory. We’ll be using Certipy to request and authenticate with the certificate. I have created the domain
CORP.LOCAL with AD CS installed. I have also created a default, low-privileged user named
JOHN. In the example below, we request a certificate from the CA
CORP-DC-CA based on the template
User. We then use the issued certificate
john.pfx for authentication against the KDC. When authenticating with a certificate, Certipy will try to request a Kerberos TGT and retrieve the NT hash of the account.
By default, domain users can enroll in the
User certificate template, and domain computers can enroll in the
Machine certificate template. Both certificate templates allow for client authentication. This means that the issued certificate can be used for authentication against the KDC via the PKINIT Kerberos extension.
So why does AD CS have different templates for users and computers, one might ask? In short, user accounts have a User Principal Name (UPN), whereas computer accounts do not. When we request a certificate based on the
User template, the UPN of the user account will be embedded in to the certificate for identification. When we use the certificate for authentication, the KDC tries to map the UPN from the certificate to a user. If we look at the msPKI-Certificate-Name-Flag property of the
User template, we can also see that
CT_FLAG_SUBJECT_ALT_REQUIRE_UPN) is specified.
As per MS-ADTS (22.214.171.124.1.3 Uniqueness Constraints), the UPN must be unique, which means we cannot have two users with the same UPN. For instance, if we try to change the UPN of
John@corp.local, we will get a constraint violation, since the UPN
John@corp.local is already used by
As mentioned previously, computer accounts do not have a UPN. So what do computer accounts then use for authentication with a certificate? If we look at the
Machine certificate template, we see that
CT_FLAG_SUBJECT_ALT_REQUIRE_DNS) is specified instead.
So let’s try to create a new machine account, request a certificate, and then authenticate with the certificate.
As we can see above, the certificate is issued with the DNS host name
JOHNPC.corp.local, and if we look at the computer account
JOHNPC$, we can notice that this value is defined in the
If we look at the permissions of the
JOHNPC object, we can see that
John (the creator of the machine account) has the “Validated write to DNS host name” permission.
The “Validated write to DNS host name” permission is explained here, and described as “Validated write permission to enable setting of a DNS host name attribute that is compliant with the computer name and domain name.” So what does “compliant with the computer name and domain name” mean?
If we (as
John) try to update the DNS host name property of
TEST.corp.local, we encounter no issues or constraint violations, and the SAM Account Name of
JOHNPC is still
So let’s try to request a certificate now.
We notice that the certificate is now issued with the DNS host name
TEST.corp.local. So now we are fairly certain that the DNS host name in the issued certificate is derived from the
dNSHostName property, and
John (as the creator of the machine account) has the “Validated write to DNS host name” permission.
If we read the MS-ADTS (126.96.36.199.1.3 Uniqueness Constraints) documentation, nowhere does it mention that the
dNSHostName property of a computer account must be unique.
If we look at the domain controller’s (
dNSHostName property, we find that the value is
So without further ado, let’s try to change the
dNSHostName property of
This time, we get an error message saying “An operations error occurred”. This is different than when we tried to change the UPN to another user’s UPN, where we got a constraint violation. So what really happened?
Well, if we looked carefully when we changed the
dNSHostName property value of
TEST.corp.local, we might have noticed that the
servicePrincipalName property value of
JOHNPC was updated to reflect the new
And according to MS-ADTS (188.8.131.52.1.3 Uniqueness Constraints), the
servicePrincipalName property is checked for uniqueness. So when we tried to update the
dNSHostName property of
DC.corp.local, the domain controller tried to update the
servicePrincipalName property, which would be updated to include
HOST/DC.corp.local, which would then conflict with the domain controller’s
So by updating the
dNSHostName property of
JOHNPC, we indirectly caused a constraint violation when the domain controller also tried to update the
If we take a look at the permissions of
JOHNPC, we can also see that
John (as the creator of the machine account) has the “Validated write to service principal name” permission.
The “Validated write to service principal name” permission is explained here, and described as “Validated write permission to enable setting of the SPN attribute which is compliant to the DNS host name of the computer.” So if we want to update the
JOHNPC, the updated values must also be compliant with the
Again, what does “compliant” mean here? We notice that only two values are updated and checked when we update the
HOST/TEST.corp.local, which contains the
dNSHostName property value. The other two values
HOST/JOHNPC contains the
sAMAccountName property value (without the trailing
So only the
servicePrincipalName property values that contain the
dNSHostName value must be compliant with
dNSHostName property. But can we then just delete the
servicePrincipalName values that contain the
Yes we can. So if we now try to update the
dNSHostName property value of
DC.corp.local, the domain controller will not have to update the
servicePrincipalName, since none of the values contain the
dNSHostName property value.
Let’s try to update the
dNSHostName property value of
Success! We can see that the
dNSHostName property was updated to
DC.corp.local, and the
servicePrincipalName was not affected by the change, which means we didn’t cause any constraint violations.
JOHNPC has the same
dNSHostName as the domain controller
Now, let’s try to request a certificate for
JOHNPC using the
Machine template, which should embed the
dNSHostName property as identification.
Another success! We got a certificate with the DNS host name
DC.corp.local. Let’s try to authenticate using the certificate.
Authentication was also successful, and Certipy retrieved the NT hash for
dc$. As a Proof-of-Concept, we can use the NT hash to perform a DCSync attack to dump the hashes of all the users.
You might have wondered, why we didn’t have to change the DNS host name of
JOHNPC to something else before authenticating with the certificate. How did the KDC know what account to map the certificate to?
PKINIT & Certificate Mapping
If you don’t care about the technical details on how certificates are mapped to accounts during authentication, you can skip this section.
Public Key Cryptography for Initial Authentication (PKINIT) is an extension for the Kerberos protocol. The PKINIT extension enables the use of public key cryptography in the initial authentication exchange of the Kerberos protocol. In other words, PKINIT is the Kerberos extension that allows the use of certificates for authentication. In order to use a certificate for Kerberos authentication, the certificate must be configured with the “Client Authentication” Extended Key Usage (EKU), and some sort of identification of the account. The Windows implementation of the PKINIT protocol extension for Kerberos is described in MS-PKCA. The documentation specifies, among other things, how the KDC maps a certificate to an account during authentication. The certificate mapping is explained in MS-PKCA 184.108.40.206.1.
First, the account is looked up based on the principal name specified in the AS-REQ, e.g.
email@example.com. Then, depending on the
userAccountControl property of the account, the KDC validates the certificate mapping based on either the Subject Alternative Name (SAN) DNSName or UPNName in the certificate. If the
WORKSTATION_TRUST_ACCOUNT (domain computer) or
SERVER_TRUST_ACCOUNT (domain controller) bit is set, the KDC validates the mapping from the DNSName. Otherwise, the KDC validates the mapping from the UPNName. For this blog post, we’re only interested in the DNSName mapping. The mapping of the DNSName field is described in MS-PKCA 220.127.116.11.1.1.
The documentation states that the KDC must confirm that the
sAMAccountName of the account looked up matches the computer name in the DNSName field of the certificate terminated with
$ and that the DNS domain name in the DNSName field of the certificate matches the DNS domain name of the realm. As an example, suppose we have the computer account
JOHNPC$ in the domain
corp.local. For a valid mapping, the DNSName of the certificate must therefore be
<computername> is the
sAMAccountName without the trailing
So during PKINIT Kerberos authentication, we supply a principal name (e.g.
firstname.lastname@example.org) and a certificate with a DNSName set to
johnpc.corp.local. The KDC then looks up the account from the principal name. Since
johnpc$ is a computer account, the KDC then splits the DNSName field into a computer name and realm part. The KDC then validates that the computer name part matches the
sAMAccountName terminated with
$ and that the realm part matches the domain. If both parts match, the validation is a success, and the mapping is thus valid. It is worth noting that the
dNSHostName property of the account is not used for the certificate mapping. The
dNSHostName property is only used when the certificate is requested.
UPDATED MAY 11
The vulnerability was patched as part of the May 2022 Security Updates from Microsoft by introducing a new Object ID (OID) in new certificates to further fingerprint the user. This is done by embedding the user’s
objectSid (SID) within the new
18.104.22.168.4.1.311.25.2) OID. Certificate Templates with the new
0x80000) flag set in the
msPKI-Enrollment-Flag attribute will not embed the new
szOID_NTDS_CA_SECURITY_EXT OID, and therefore, these templates are still vulnerable to this attack. It is unlikely that this flag is set, but you should be aware of the implications of turning this flag on. Furthermore, the “Validated write to DNS host name” permission now only allows setting a
dNSHostName attribute that matches the SAM Account Name of the account. However, with a generic write permission over the computer account, it’s still possible to create a duplicate
An attempt to exploit the vulnerability against a patched domain controller will return
KDC_ERR_CERTIFICATE_MISMATCH during Kerberos authentication, if the certificate has the
szOID_NTDS_CA_SECURITY_EXT OID. I also tried to perform the authentication using Schannel against LDAPS to check whether the vulnerability was only patched in the Kerberos implementation. Fortunately, it seems that this method can’t bypass the security update. There might be some other interesting cases, since the
dNSHostName property can still be duplicated and embedded in the certificate. To check if a CA is vulnerable, we can simply request a certificate and check whether the user’s SID is embedded within the certificate. It is worth noting that both the KDC and CA server must be patched in order to fully mitigate the vulnerability.
This patch also brings an end to the ESC6 attack described in Will Schroeder and Lee Christensen’s whitepaper; but the ESC1 attack will still work, since the new OID isn’t embedded in certificates based on certificate templates with the
ENROLLEE_SUPPLIES_SUBJECT flag specified.
Along with release of this blog post, Certipy has received some new updates that includes functionality to easily create a new machine account with the DNS host name
dc.corp.local and then request a certificate.
A patch has officially been released by Microsoft. If you’re unable to install the patch, there are a few other measures you can take to mitigate the vulnerability. First of all, you can harden your AD CS environment by restricting certificate enrollment. While not directly a mitigation, you can also change the
MS-DS-Machine-Account-Quota attribute to
0, which is the value that determines the number of computer accounts that a user is allowed to create in a domain. By default, this value is set to
10. This does not mitigate the vulnerability, since an attacker might compromise a machine account by compromising a workstation, for instance with KrbRelay.
- Dec 14, 2021: Vulnerability reported to Zero Day Initiative
- Dec 17, 2021: Case assigned
- Dec 31, 2021: Case investigated
- Jan 11, 2022: Case contracted
- Jan 20, 2022: Case reviewed
- Jan 21, 2022: Vendor disclosure, tracked as ZDI-CAN-16168
- May 10, 2022: Patch released by Microsoft