Private/Set/Set-ManagerApprovalRequired.ps1

function Set-ManagerApprovalRequired {
    <#
        .SYNOPSIS
        Adds a ManagerApprovalRequired property to AD CS certificate template objects.
 
        .DESCRIPTION
        Examines the msPKI-Enrollment-Flag attribute of Active Directory Certificate Services
        certificate template objects to determine if manager approval is required for enrollment.
         
        The function checks bit 2 (0x00000002) of the msPKI-Enrollment-Flag attribute.
        When this bit is set, certificate requests based on this template require approval
        from a certificate manager before the certificate is issued.
         
        Manager approval is a security control that can help prevent abuse of certificate
        templates by requiring human authorization before certificates are issued. Templates
        without manager approval combined with other risky settings (like allowing SANs or
        dangerous enrollees) can lead to privilege escalation vulnerabilities.
         
        The function adds a boolean ManagerApprovalRequired property to each input object
        indicating whether the template requires manager approval for enrollment.
 
        .PARAMETER AdcsObject
        One or more DirectoryEntry objects representing AD CS certificate templates.
        These objects must contain the msPKI-Enrollment-Flag attribute.
 
        .INPUTS
        System.DirectoryServices.DirectoryEntry[]
        You can pipe certificate template DirectoryEntry objects to this function.
 
        .OUTPUTS
        System.DirectoryServices.DirectoryEntry[]
        Returns the input objects with an added ManagerApprovalRequired boolean property.
 
        .EXAMPLE
        $templates = Get-AdcsObject -RootDSE $rootDSE | Where-Object { $_.objectClass -contains 'pKICertificateTemplate' }
        $templates | Set-ManagerApprovalRequired
        Processes all certificate templates and adds the ManagerApprovalRequired property to each.
 
        .EXAMPLE
        Get-AdcsObject -RootDSE $rootDSE | Set-ManagerApprovalRequired | Where-Object { -not $_.ManagerApprovalRequired }
        Retrieves all AD CS objects, adds ManagerApprovalRequired property, and filters to only those NOT requiring approval.
 
        .EXAMPLE
        $template = Get-AdcsObject -RootDSE $rootDSE | Where-Object Name -eq 'WebServer'
        $template | Set-ManagerApprovalRequired
        if (-not $template.ManagerApprovalRequired) {
            Write-Host "Template does not require manager approval - potential security risk"
        }
        Checks a specific template for manager approval requirement.
 
        .NOTES
        The msPKI-Enrollment-Flag attribute uses bitwise flags:
        - Bit 2 (0x00000002): CT_FLAG_PEND_ALL_REQUESTS (requires manager approval)
         
        When manager approval is NOT required, users can obtain certificates automatically
        upon enrollment. Combined with other misconfigurations (like allowing SANs or
        overly permissive enrollees), this can enable privilege escalation attacks.
         
        Templates requiring manager approval provide a defense-in-depth control by requiring
        human review before certificates are issued.
 
        .LINK
        https://posts.specterops.io/certified-pre-owned-d95910965cd2
    #>

    [CmdletBinding()]
    [OutputType([System.DirectoryServices.DirectoryEntry[]])]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.DirectoryServices.DirectoryEntry[]]$AdcsObject
    )

    #requires -Version 5.1

    begin {
        Write-Verbose "Identifying templates that require manager approval for enrollment..."
    }

    process {
        $AdcsObject | Where-Object SchemaClassName -eq pKICertificateTemplate | ForEach-Object {
            try {
                $objectName = if ($_.Properties.displayName.Count -gt 0) {
                    $_.Properties.displayName[0] 
                } elseif ($_.Properties.name.Count -gt 0) {
                    $_.Properties.name[0]
                } else {
                    $_.Properties.distinguishedName[0]
                }
                Write-Verbose "Processing template: $objectName"
                
                $managerApprovalRequired = $true
                
                # Check if the msPKI-Enrollment-Flag attribute exists
                if ($_.Properties.'msPKI-Enrollment-Flag'.Count -gt 0) {
                    [int]$enrollmentFlag = $_.'msPKI-Enrollment-Flag'[0]
                    Write-Verbose "msPKI-Enrollment-Flag value: $enrollmentFlag (0x$($enrollmentFlag.ToString('X8')))"
                    
                    # Bit 2 (0x00000002) = CT_FLAG_PEND_ALL_REQUESTS (manager approval required)
                    # REVERSED LOGIC: Property is true when approval is NOT required (vulnerable)
                    if ($enrollmentFlag -band 2) {
                        $managerApprovalRequired = $false
                        Write-Verbose "Manager approval is required (bit 2 is set) - NOT vulnerable"
                    } else {
                        Write-Verbose "Manager approval is NOT required (bit 2 is not set) - VULNERABLE"
                    }
                } else {
                    Write-Verbose "msPKI-Enrollment-Flag attribute not found on object - assuming NOT required (vulnerable)"
                }
                
                # Update the AdcsObjectStore with the ManagerApprovalRequired property
                $dn = $_.Properties.distinguishedName[0]
                if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn] | Add-Member -NotePropertyName ManagerApprovalRequired -NotePropertyValue $managerApprovalRequired -Force
                    Write-Verbose "Updated AD CS Object Store for $dn with ManagerApprovalRequired = $managerApprovalRequired"
                }
                
                # Also add to the pipeline object for backward compatibility
                $_ | Add-Member -NotePropertyName ManagerApprovalRequired -NotePropertyValue $managerApprovalRequired -Force
                
                # Return the modified object
                $_
                
            } catch {
                $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                    $_.Exception,
                    'ManagerApprovalFlagProcessingFailed',
                    [System.Management.Automation.ErrorCategory]::InvalidOperation,
                    $_
                )
                $PSCmdlet.WriteError($errorRecord)
                
                # Still return the object even if processing failed
                $_
            }
        }
    }

    end {
        Write-Verbose "Done identifying templates that require manager approval for enrollment."
    }
}