# 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

```powershell
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

```powershell
Find-CertificateAutoenrollment
```

**3.** Scan a specific template

```powershell
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

```powershell
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

```powershell
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

```powershell
Find-CertificateAutoenrollmentSimple
```

**3.** Scan a specific template object

```powershell
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.

![certsrv](/files/qQOH6TjmUXsPu52UAiik)

## 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.

![certsrv](/files/qQOH6TjmUXsPu52UAiik)

## 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.

| Event ID | Description                                                                   | Fields/Attributes              | References                                                                                                             |
| -------- | ----------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| 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

* [Manual Execution of the Auto-Enrollment Process - Gradenegger.eu](https://www.gradenegger.eu/en/manual-execution-of-the-auto-enrollment-process/)
* [First Issuance Manual with Automated Renewals - Microsoft Tech Community](https://techcommunity.microsoft.com/blog/askds/first-issuance-manual-with-automated-renewals/4085859)
* [Certificate Auto-Enrollment & Issuance - Keyfactor Blog](https://www.keyfactor.com/blog/certificate-auto-enrollment-issuance/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/ad/certificateautoenrollment.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
