HUNT 06 AD ADCS Vulnerable Template Audit 30d
Query
// =========================================================
// HUNT-06 | AD-ADCS-Vulnerable-Template-Audit-30d
// Description : All certificate issuances (4886/4887) over
// 30 days grouped by template, with SAN
// presence, requester vs. subject mismatch
// detection, enrollment agent use flags,
// and bulk enrollment rate indicators.
// Maps to ESC1 (SAN mismatch), ESC3
// (enrollment agent), ESC8 (DC cert relay).
// Period : 30 days
// Use Case : ADCS attack surface review, PKI audit
// Tables : SecurityEvent
// =========================================================
let Period = 30d;
// Event 4887 — Certificate Issued with detailed fields
SecurityEvent
| where TimeGenerated > ago(Period)
| where EventID in (4886, 4887)
| extend
EventDesc = case(EventID == 4886, "Certificate Requested",
EventID == 4887, "Certificate Issued", "Other"),
RequesterUPN = SubjectUserName,
RequesterDomain = SubjectDomainName,
// Parse template from EventData
TemplateName = extract(@"(?i)<Data Name=.CertificateTemplate.>(.*?)</Data>", 1, EventData),
RequestAttr = extract(@"(?i)<Data Name=.RequestAttributes.>(.*?)</Data>", 1, EventData),
SubjectField = extract(@"(?i)<Data Name=.Subject.>(.*?)</Data>", 1, EventData)
| extend
// SAN present in request attributes (locally computed from RequestAttr)
SANValue = extract(@"(?i)san=([^;,\n]+)", 1, RequestAttr),
EnrollmentAgent = extract(@"(?i)enrollmentagent=([^;,\n]+)", 1, RequestAttr),
RequesterNorm = tolower(RequesterUPN),
IsDCTemplate = TemplateName has_any ("DomainController", "KerberosAuthentication",
"DirectoryEmailReplication")
| extend
HasSAN = isnotempty(SANValue),
HasEnrollAgent = isnotempty(EnrollmentAgent),
SANNorm = tolower(SANValue),
SANMismatch = isnotempty(SANValue) and not(tolower(SANValue) contains tolower(RequesterUPN))
| extend
SensitiveSAN = SANNorm has_any ("administrator", "domain admins", "enterprise admins",
"krbtgt", "dc$", "dc01", "dc02")
| summarize
TotalIssuances = count(),
SANMismatches = countif(SANMismatch),
SensitiveSANIssuances = countif(SensitiveSAN),
EnrollAgentUses = countif(HasEnrollAgent),
DCTemplateCerts = countif(IsDCTemplate),
UniqueRequesters = dcount(RequesterUPN),
Requesters = make_set(RequesterUPN, 20),
SANValues = make_set(SANValue, 20),
IssuanceDays = dcount(startofday(TimeGenerated)),
LastIssuance = max(TimeGenerated),
FirstIssuance = min(TimeGenerated)
by TemplateName
| extend
DailyRateAvg = round(todouble(TotalIssuances) / todouble(IssuanceDays), 1),
RiskScore = (SANMismatches * 30)
+ (SensitiveSANIssuances * 50)
+ (EnrollAgentUses * 20)
+ (DCTemplateCerts * 15),
ESCFlags = strcat(
iff(SANMismatches > 0, "ESC1_SAN_Mismatch; ", ""),
iff(EnrollAgentUses > 0, "ESC3_EnrollAgent; ", ""),
iff(DCTemplateCerts > 0, "ESC8_DCTemplate; ", ""),
iff(SensitiveSANIssuances > 0, "Critical_SensitiveSAN; ", "")
),
RiskLevel = case(
SensitiveSANIssuances > 0, "Critical",
SANMismatches > 0, "High",
EnrollAgentUses > 0, "High",
DCTemplateCerts > 5, "Medium",
"Low"
)
| project
TemplateName,
RiskLevel,
RiskScore,
ESCFlags,
TotalIssuances,
DailyRateAvg,
SANMismatches,
SensitiveSANIssuances,
EnrollAgentUses,
DCTemplateCerts,
UniqueRequesters,
FirstIssuance,
LastIssuance,
SANValues,
Requesters
| order by RiskScore descExplanation
This KQL query is designed to audit and analyze certificate issuances in an Active Directory Certificate Services (ADCS) environment over the past 30 days. Here's a simple breakdown of what the query does:
-
Data Source: It looks at security events related to certificate requests and issuances (Event IDs 4886 and 4887) from the
SecurityEventtable. -
Time Frame: The analysis covers a period of the last 30 days.
-
Data Extraction: For each event, it extracts and processes several pieces of information:
- The type of event (certificate requested or issued).
- The template used for the certificate.
- The requester's user principal name (UPN) and domain.
- Subject Alternative Name (SAN) details.
- Whether an enrollment agent was used.
- Whether the certificate template is for a domain controller.
-
Analysis:
- It checks for SAN presence and mismatches between the SAN and the requester.
- It identifies sensitive SAN values that could indicate potential security risks.
- It counts the use of enrollment agents and domain controller templates.
-
Summarization: The query summarizes the data by certificate template, calculating:
- Total number of issuances.
- Number of SAN mismatches and sensitive SAN issuances.
- Use of enrollment agents and domain controller templates.
- Unique requesters and their details.
- Issuance frequency and time range.
-
Risk Assessment:
- It calculates a risk score based on the presence of SAN mismatches, sensitive SANs, enrollment agent usage, and domain controller templates.
- It assigns a risk level (Critical, High, Medium, Low) based on specific criteria.
-
Output: The results are projected to show key details like template name, risk level, risk score, and other relevant metrics, sorted by risk score in descending order to prioritize higher-risk templates.
This query is useful for identifying potential vulnerabilities and risks in the certificate issuance process, helping to enhance the security posture of the ADCS environment.
Details

David Alonso
Released: March 24, 2026
Tables
Keywords
Operators