Private/Set/Set-ManagerApprovalNotRequired.ps1

function Set-ManagerApprovalNotRequired {
    <#
        .SYNOPSIS
        Adds a ManagerApprovalNotRequired 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 NOT required for enrollment.
         
        The function checks bit 2 (0x00000002) of the msPKI-Enrollment-Flag attribute.
        When this bit is NOT set, certificate requests based on this template do NOT 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 ManagerApprovalNotRequired property to each input object
        where TRUE indicates vulnerability (manager approval is NOT required).
 
        .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 ManagerApprovalNotRequired boolean property.
        TRUE = manager approval is NOT required (vulnerable).
 
        .EXAMPLE
        $templates = Get-AdcsObject -RootDSE $rootDSE | Where-Object { $_.objectClass -contains 'pKICertificateTemplate' }
        $templates | Set-ManagerApprovalNotRequired
        Processes all certificate templates and adds the ManagerApprovalNotRequired property to each.
 
        .EXAMPLE
        Get-AdcsObject -RootDSE $rootDSE | Set-ManagerApprovalNotRequired | Where-Object { $_.ManagerApprovalNotRequired }
        Retrieves all AD CS objects, adds ManagerApprovalNotRequired property, and filters to only vulnerable templates.
 
        .EXAMPLE
        $template = Get-AdcsObject -RootDSE $rootDSE | Where-Object Name -eq 'WebServer'
        $template | Set-ManagerApprovalNotRequired
        if ($template.ManagerApprovalNotRequired) {
            Write-Host "Template does not require manager approval - potential security risk"
        }
        Checks a specific template for manager approval vulnerability.
 
        .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"
                
                $managerApprovalNotRequired = $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) {
                        $managerApprovalNotRequired = $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 ManagerApprovalNotRequired property
                $dn = $_.Properties.distinguishedName[0]
                if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn] | Add-Member -NotePropertyName ManagerApprovalNotRequired -NotePropertyValue $managerApprovalNotRequired -Force
                    Write-Verbose "Updated AD CS Object Store for $dn with ManagerApprovalNotRequired = $managerApprovalNotRequired"
                }
                
                # Also add to the pipeline object for backward compatibility
                $_ | Add-Member -NotePropertyName ManagerApprovalNotRequired -NotePropertyValue $managerApprovalNotRequired -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."
    }
}