CertificateAutoenrollment
Summary
FSProtect ACL Alias
CertificateAutoEnrollment
Affected Object Types
Certificate Templates
Exploitation Certainty
Certain
AD Permission Guid
a05b8cc2-17bc-4802-a710-e7c15ab866a2
Description
The CertificateAutoEnrollment
permission allows accounts in an Active Directory Enterprise PKI to automatically request digital certificates from an Enterprise CA. Certificates are important for secure authentication and encrypted communications. They confirm identities, secure email, enable VPN access, and allow code signing. With automatic certificate enrollment, devices and users can obtain certificates that meet security requirements without manual intervention.
However, if certificate template permissions are misconfigured or are granted to untrusted accounts, serious security risks may result. Unauthorized users could obtain certificates without proper checks and use them to impersonate trusted services, intercept communications, sign malicious code, or decrypt sensitive data. This can enable attackers to bypass security controls, escalate privileges, and maintain persistence in the network.
How to trigger
The Certificate Auto-Enrollment mechanism is triggered by several events. Common triggers include the following:
You can configure this via: Computer/User Configuration > Policies > Windows Settings > Security Settings > Public Key Policies > Certificate Services Client – Auto-Enrollment
1. User logon
When a user logs on, the system checks for required certificates and automatically enrolls or renews them if necessary.
2. Computer startup
At startup, a domain-joined computer verifies its machine certificate status and initiates autoenrollment for any missing or expiring certificates.
3. Group Policy refresh
During a routine Group Policy refresh (approximately every 90 minutes), the system reviews certificate statuses and triggers autoenrollment as needed.
4. Certificate renewal period
When a certificate reaches its renewal threshold (for example, 80% of its validity period), autoenrollment renews it before expiration.
5. Certificate template configuration changes
Updates to certificate template settings cause clients to re-evaluate and, if necessary, enroll for certificates based on the new configuration.
6. Manual Group Policy update (gpupdate /force
)
Running a forced Group Policy update immediately triggers the autoenrollment process to process any pending certificate enrollments or renewals.
Identification
Active Directory PowerShell
You can enumerate CertificateAutoEnrollment
entries using the ActiveDirectory PowerShell module.
1. Find-CertificateAutoenrollment function
function Find-CertificateAutoenrollment {
[CmdletBinding()]
param ([string]$Target = $null,[string]$SearchBase = $null,[string]$OutputPath = "CertificateAutoenrollment.csv", [switch]$ExcludeAdmins = $false)
Import-Module ActiveDirectory -ErrorAction Stop
Write-Host "Gathering Active Directory Certificate Templates and inspecting ACLs for explicit 'Certificate Autoenrollment' permissions..."
$AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow;
$ActiveDirectoryRights = [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight;
$CertificateAutoenrollmentGuid = "a05b8cc2-17bc-4802-a710-e7c15ab866a2";
$ExcludedSIDs = @()
if ($ExcludeAdmins) {
Write-Host "Excluding default administrative groups and built-in accounts."
$ExcludedSIDs += (New-Object System.Security.Principal.NTAccount "NT AUTHORITY\SYSTEM").Translate([System.Security.Principal.SecurityIdentifier]),(New-Object System.Security.Principal.NTAccount "NT AUTHORITY\SELF").Translate([System.Security.Principal.SecurityIdentifier]),(New-Object System.Security.Principal.NTAccount "BUILTIN\Account Operators").Translate([System.Security.Principal.SecurityIdentifier]),(New-Object System.Security.Principal.NTAccount "BUILTIN\Administrators").Translate([System.Security.Principal.SecurityIdentifier]),(New-Object System.Security.Principal.NTAccount "NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS").Translate([System.Security.Principal.SecurityIdentifier])
try {
$ExcludedSIDs += (Get-ADGroup -Identity "Domain Admins").SID ;$ExcludedSIDs += (Get-ADGroup -Identity "Enterprise Admins").SID
$ExcludedSIDs += (Get-ADGroup -Identity "Schema Admins").SID ;$ExcludedSIDs += (Get-ADGroup -Identity "Cert Publishers").SID
$ExcludedSIDs += (Get-ADGroup -Identity "Group Policy Creator Owners").SID ;$ExcludedSIDs += (Get-ADGroup -Identity "Domain Controllers").SID
$ExcludedSIDs += (Get-ADGroup -Identity "Key Admins").SID ;$ExcludedSIDs += (Get-ADGroup -Identity "Enterprise Key Admins").SID
$ExcludedSIDs += (Get-ADGroup -Identity "Enterprise Read-only Domain Controllers").SID ;$ExcludedSIDs += (Get-ADGroup -Identity "RAS and IAS Servers").SID
}
catch {
Write-Warning "Could not resolve one or more default domain admin groups (e.g., Domain Admins, Enterprise Admins, Schema Admins). They might not be filtered from results."
}
}
$foundAcls = @()
$objectsToScan = @()
try {
if ($Target) {
Write-Host "Searching for permissions on specific object: '$Target'."
$specificObject = Get-ADObject -Identity $Target -Properties nTSecurityDescriptor -ErrorAction Stop
if ($specificObject) {
$objectsToScan += $specificObject
} else {
Write-Output "Object '$Target' not found."
return
}
} else {
$configNC = (Get-ADRootDSE).ConfigurationNamingContext
$adObjectParams = @{
Filter = "ObjectClass -eq 'pKICertificateTemplate'"
Properties = "nTSecurityDescriptor"
ErrorAction = "Stop"
}
if ($SearchBase) {
$adObjectParams.Add("SearchBase", $SearchBase)
Write-Host "Searching for certificate templates within '$SearchBase'."
} else {
$adObjectParams.Add("SearchBase", $configNC)
Write-Host "Searching for all certificate templates in the Configuration Naming Context."
}
$objectsToScan = Get-ADObject @adObjectParams
}
if (-not $objectsToScan) {
Write-Output "No Active Directory Certificate Templates found matching the criteria."
return
}
foreach ($obj in $objectsToScan) {
$ObjectDistinguishedName = $obj.DistinguishedName;
try {
$acl = Get-Acl -Path "AD:$ObjectDistinguishedName"
foreach ($ace in $acl.Access) {
$isExcluded = $false
if ($ExcludeAdmins) {
try {
if ($ExcludedSIDs -contains $ace.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier])) {
$isExcluded = $true
}
}
catch {
Write-Warning "Could not translate SID for exclusion check: $($ace.IdentityReference.Value). Error: $($_.Exception.Message)"
}
}
if ($ace.AccessControlType -eq $AccessControlType -and
($ace.ActiveDirectoryRights -band $ActiveDirectoryRights) -and # Use -band for bitwise comparison
($ace.ObjectType -eq $CertificateAutoenrollmentGuid) -and
-not $ace.IsInherited -and
-not $isExcluded) { # Apply exclusion filter
$foundAcls += [PSCustomObject]@{
'Vulnerable Template' = $ObjectDistinguishedName
'Internal Threat' = $ace.IdentityReference.Value # Get the string representation
}
}
}
}
catch {
Write-Warning "Could not retrieve ACL for '$ObjectDistinguishedName': $($_.Exception.Message)"
}
}
}
catch {
Write-Error "Failed to retrieve Active Directory Certificate Templates: $($_.Exception.Message)"
return
}
if ($foundAcls.Count -gt 0) {
$exclusionMessage = if ($ExcludeAdmins) { " (excluding default admin groups and built-in accounts)" } else { "" }
Write-Host "Found $($foundAcls.Count) Active Directory certificate template(s) with explicit 'Certificate Autoenrollment' permissions$exclusionMessage."
try {
$foundAcls | Sort-Object -Unique 'Vulnerable Template', 'Internal Threat' | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Output "Results exported successfully to '$OutputPath'"
}
catch {
Write-Error "Failed to export results to CSV file '$OutputPath': $($_.Exception.Message)"
}
} else {
$exclusionMessage = if ($ExcludeAdmins) { " (excluding default admin groups and built-in accounts)" } else { "" }
Write-Output "No Active Directory certificate template(s) found with explicit 'Certificate Autoenrollment' permissions$exclusionMessage."
}
}
2. Scan all templates in the domain
Find-CertificateAutoenrollment
3. Scan a specific template
Find-CertificateAutoenrollment -Target "CN=DirectoryEmailReplication,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=forestall,DC=labs"
4. To exclude default admin ACLs for better visibility
Find-CertificateAutoenrollment -ExcludeAdmins
.NET Directory Services
By using PowerShell’s built-in .NET System.DirectoryServices
APIs, you can enumerate CertificateAutoEnrollment
entries without relying on external modules or additional dependencies.
1. Find-CertificateAutoenrollmentSimple function
function Find-CertificateAutoenrollmentSimple {
[CmdletBinding()]
param ([string]$Target = $null,[string]$OutputPath = "CertificateAutoenrollment.csv")
$CertificateAutoenrollmentGuid = [Guid]"a05b8cc2-17bc-4802-a710-e7c15ab866a2"
$results = @()
try {
if ($Target) {
Write-Verbose "Binding directly to specified DN: $Target"
$templates = @(New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target"))
} else {
Write-Verbose "Enumerating all certificate templates from ConfigurationNamingContext..."
$rootDSE = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
$configNC = $rootDSE.Properties["configurationNamingContext"][0]
$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"LDAP://$configNC")
$searcher.Filter = "(objectClass=pKICertificateTemplate)"
$searcher.PageSize = 1000
$searcher.PropertiesToLoad.Add("distinguishedName") | Out-Null
$hits = $searcher.FindAll()
$templates = foreach ($hit in $hits) {
try { $hit.GetDirectoryEntry() } catch { Write-Warning "Error binding: $_"; continue }
}
}
foreach ($entry in $templates) {
$dn = $entry.distinguishedName
Write-Verbose "Checking ACLs on: $dn"
try {
$acl = $entry.ObjectSecurity
$aces = $acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])
} catch {
Write-Warning "Failed to retrieve ACL for $dn : $_"
continue
}
foreach ($ace in $aces) {
if (
$ace.AccessControlType -eq "Allow" -and
($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight) -and
$ace.ObjectType -eq $CertificateAutoenrollmentGuid -and
-not $ace.IsInherited
) {
$principal = try {
$ace.IdentityReference.Translate([System.Security.Principal.NTAccount]).Value
} catch {
$ace.IdentityReference.Value
}
$results += [PSCustomObject]@{
Template = $dn
Principal = $principal
Right = "Certificate Autoenrollment"
}
}
}
}
if ($results.Count -gt 0) {
$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Exported $($results.Count) entr$(if($results.Count -eq 1){'y'}else{'ies'}) to $OutputPath"
} else {
Write-Host "No certificate autoenrollment ACLs found."
}
} catch {
Write-Error "Unhandled error : $_"
}
}
2. Scan all certificate templates in the domain
Find-CertificateAutoenrollmentSimple
3. Scan a specific template object
Find-CertificateAutoenrollmentSimple -Target "CN=KerberosAuthentication,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=forestall,DC=labs"
Certification Authority
1. Open Certification Authority (certsrv)
on your Windows server.
2. Expand the CA name and right-click Certificate Templates.
3. Select Manage from the context menu.
4. In the Certificate Templates Console window, double-click the vulnerable Certificate Template.
5. In the Properties window, navigate to the Security tab.
6. In the Security Settings window, locate and select the relevant Access Control Entry (ACE) for the user or group you wish to configure.
7. In the permissions list, ensure the Autoenroll
option is set appropriately (enable for trusted accounts, remove for untrusted accounts).
8. Click OK to save your changes and close the dialogs.

Exploitation
The CertificateAutoEnrollment
permission is not a standalone vulnerability and cannot be exploited on its own. However, when combined with other attack vectors (for example, ESC1), it can be an enabler in a larger exploit chain by facilitating lateral movement or privilege escalation.
Mitigation
1. Open Certification Authority (certsrv)
on your Windows server.
2. Expand the CA name and right-click Certificate Templates.
3. Select Manage from the context menu.
4. In the Certificate Templates Console window, double-click the vulnerable Certificate Template.
5. In the Properties window, navigate to the Security tab.
6. In the Security Settings window, locate and remove the Autoenroll
permission from any untrusted or unnecessary user or group.
7. Click OK to save your changes and close the dialogs.

Detection
Adding new Access Control Entries to Active Directory objects changes the ntSecurityDescriptor
attribute of those objects. These changes can be detected with Event IDs 5136 and 4662 to identify potentially risky or unauthorized modifications. Event ID 4886 indicates that a certificate request has been received by the Certification Authority, while Event ID 4887 confirms that the request has been approved and the certificate issued. These logs are important for monitoring certificate operations and maintaining security.
5136
A directory service object was modified.
ntSecurityDescriptor
https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136
4662
An operation was performed on an object.
AccessList, AccessMask
https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662
4886
Certificate Services received a certificate request.
CertificateTemplate, Requester
https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/dn319076(v=ws.11)
4887
Certificate Services approved a certificate request and issued a certificate.
CertificateTemplate, Requester
https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/dn319076(v=ws.11)
References
Last updated
Was this helpful?