ADCS-019: Certificate Template Enumeration
- Platform
- Active Directory
- Category
- AD Certificate Services
- Severity
- Info
- Zero Trust pillar
- Identity (weight 1)
- Golden fixtures
- 2
- Branch coverage
- Observed: fixtures prove the verdicts they exercise
- Provenance
- baseline
What it checks
The check binds to CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=domain,DC=tld and enumerates every object with objectClass=pKICertificateTemplate. For each template it records: - name and displayName (CN and friendly name) - msPKI-Template-Schema-Version (1 = Windows 2000, 2 = Server 2003, 3 = Server 2008, 4 = Server 2012+; schema v1 is the ESC15 prerequisite) - pKIExtendedKeyUsage and msPKI-Certificate-Application-Policy (the EKU OID list; Client Authentication 1.3.6.1.5.5.7.3.2, Smart Card Logon 1.3.6.1.4.1.311.20.2.2, PKINIT Client Auth 1.3.6.1.5.2.3.4, Any Purpose 2.5.29.37.0, and SubCA 1.3.6.1.4.1.311.20.1 are flagged) - msPKI-Certificate-Name-Flag (the bitmask that controls subject construction; the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT bit 0x00000001 is the ESC1 trigger, CT_FLAG_SUBJECT_ALT_REQUIRE_UPN and friends are recorded for ESC9 and ESC10 cross-reference) - msPKI-Enrollment-Flag (CT_FLAG_NO_SECURITY_EXTENSION bit 0x00080000 is the ESC9 trigger, CT_FLAG_PEND_ALL_REQUESTS bit 0x00000002 governs manager approval) - msPKI-RA-Signature (the authorized-signatures count; 0 means no countersignature required, which combined with permissive enrollment is the ESC1/ESC2/ESC3 precondition) - pKIExpirationPeriod and pKIOverlapPeriod (validity and renewal windows, parsed from the Windows FILETIME byte array) - nTSecurityDescriptor (the template DACL; Enroll, AutoEnroll, WriteDacl, WriteOwner, WriteProperty, and GenericAll ACEs are extracted) - the per-CA publication list, computed by intersecting each Enterprise CAs certificateTemplates attribute (enumerated by ADCS-001) with the template CN The Certificate Templates container is the authoritative source: the certtmpl.msc MMC snap-in, certutil -template, and the Microsoft MS-CRTD specification all read these attributes. A template that exists in the container but is not published by any CA is enumerable but not exploitable, the finding flags both states.
Why it matters
Every ADCS escalation path published in Certified Pre-Owned and the subsequent ESC9 through ESC16 follow-ups is fundamentally a misconfigured template plus an enrollment grant. ESC1 is ENROLLEE_SUPPLIES_SUBJECT plus a client-auth EKU plus Authenticated Users Enroll. ESC2 is the Any Purpose EKU on a template Authenticated Users can enroll. ESC3 is the Certificate Request Agent EKU plus a downstream enrollment-agent-restricted template. ESC4 is WriteDacl, WriteOwner, or WriteProperty on the template object itself. ESC9 is CT_FLAG_NO_SECURITY_EXTENSION. ESC15 is schema v1 with a client-auth-capable application policy that lets the requester inject arbitrary EKUs at request time. The inventory ADCS-019 produces is the lookup table every other ADCS check joins against, defenders who run the recon series without ADCS-019 have no shared ground truth and cannot reproduce findings across runs. Beyond the ESC chain, the inventory is also the audit trail for least-privilege review: a template that grants Enroll to Authenticated Users or Domain Users but issues a certificate with the Smart Card Logon EKU is a domain-wide authentication risk regardless of which ESC label fits, and a template with WriteDacl granted to a non-Tier-0 principal is a latent ESC4.
Attack path
1. Enumeration: an attacker with any authenticated user context runs certipy find -u user@domain.tld -p pass -dc-ip 10.0.0.1 -vulnerable, which queries CN=Certificate Templates and CN=Enrollment Services in one pass and returns every template with its EKU list, name flags, enrollment flags, RA signature count, and enrollment ACL. Certify.exe find /vulnerable and PSPKIAudit Get-CertificateTemplate produce the same data via different APIs. 2. Triage: the attacker filters the template list for the ESC preconditions: ENROLLEE_SUPPLIES_SUBJECT plus client auth plus permissive enroll (ESC1), Any Purpose EKU plus permissive enroll (ESC2), Certificate Request Agent EKU (ESC3), permissive ACL on the template object (ESC4), CT_FLAG_NO_SECURITY_EXTENSION (ESC9), schema v1 with client auth (ESC15). 3. Publication check: the attacker confirms which CA publishes the candidate template by intersecting against the Enrollment Services certificateTemplates attribute; an unpublished template is enumerable but not enrollable. 4. Exploitation: certipy req -u user@domain.tld -p pass -ca CORP-ISSUING-CA -template VulnerableTemplate -upn Administrator@domain.tld issues a certificate that, on a typical ESC1, authenticates as Domain Administrator via PKINIT. 5. Persistence: the resulting certificate is bound to the attackers private key and survives the principals password reset. Without revocation of the issuing CA or the specific certificate, the attacker can re-authenticate indefinitely. Steps 1 through 3 are passive LDAP reads against the Configuration NC and are invisible to host-based EDR. They are only visible to defenders who enable LDAP diagnostic event logging (Event ID 1644 with the field engineering thresholds).
How Guerrilla assesses it
Guerrilla calls Get-ADObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter "(objectClass=pKICertificateTemplate)" -Properties name, displayName, msPKI-Template-Schema-Version, pKIExtendedKeyUsage, msPKI-Certificate-Application-Policy, msPKI-Certificate-Name-Flag, msPKI-Enrollment-Flag, msPKI-RA-Signature, pKIExpirationPeriod, pKIOverlapPeriod, nTSecurityDescriptor. The result is normalized in memory: bit flags are decoded against the MS-CRTD enums, EKU OIDs are resolved to friendly names where known, the nTSecurityDescriptor is parsed into Enroll, AutoEnroll, WriteDacl, WriteOwner, WriteProperty, and GenericAll ACE lists, and pKIExpirationPeriod is converted from the eight-byte little-endian FILETIME representation to days. The per-CA publication list is computed by reading certificateTemplates from each pKIEnrollmentService object surfaced by ADCS-001. The check always reports PASS at this stage; ADCS-002 through ADCS-018 read the same inventory and flag the specific ESC1 through ESC16 conditions.
Recommended value
Complete template inventory with security attributes documented; all templates reviewed for least-privilege enrollment and appropriate EKU
Remediation
Enumerate all certificate templates from CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration using certutil -template or PowerShell. For each template, document: display name, schema version, enrollment permissions, EKU, name flags (ENROLLEE_SUPPLIES_SUBJECT), enrollment flags, authorized signatures requirement, validity period, and renewal period. Cross-reference published templates on each CA.
Fixture-proven verdicts
Every verdict below is proven by a golden fixture in the module's gating test suite. This table derives from the last green run; it cannot be edited by hand.
| Scenario | Expected verdict |
|---|---|
| clean | PASS |
| throttled | Not Assessed |
Framework mappings
- NIST SP 800-53
- CM-8, CM-8(1), IA-5(2)
- MITRE ATT&CK
- T1649