Private/Set/Set-RequiresEnrollmentAgentSignature.ps1

function Set-RequiresEnrollmentAgentSignature {
    <#
        .SYNOPSIS
        Adds a RequiresEnrollmentAgentSignature property to AD CS certificate template objects.
 
        .DESCRIPTION
        Examines the msPKI-RA-Signature and msPKI-RA-Application-Policies attributes of Active Directory
        Certificate Services certificate template objects to determine if they require enrollment agent
        signatures for certificate requests.
         
        The function checks for:
        - msPKI-RA-Signature = 1 (requires authorized signature)
        - msPKI-RA-Application-Policies contains '1.3.6.1.4.1.311.20.2.1' (Certificate Request Agent)
         
        Templates with these settings are vulnerable to ESC3 Condition 2 attacks, where holders of
        enrollment agent certificates can request certificates on behalf of other principals.
         
        The function adds a boolean RequiresEnrollmentAgentSignature property to each input object
        indicating whether the template requires enrollment agent signatures.
 
        .PARAMETER AdcsObject
        One or more DirectoryEntry objects representing AD CS certificate templates.
        These objects must contain the msPKI-RA-Signature and msPKI-RA-Application-Policies attributes.
 
        .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 RequiresEnrollmentAgentSignature boolean property.
 
        .EXAMPLE
        $templates = Get-AdcsObject -RootDSE $rootDSE | Where-Object { $_.objectClass -contains 'pKICertificateTemplate' }
        $templates | Set-RequiresEnrollmentAgentSignature
        Processes all certificate templates and adds the RequiresEnrollmentAgentSignature property to each.
 
        .EXAMPLE
        Get-AdcsObject -RootDSE $rootDSE | Set-RequiresEnrollmentAgentSignature | Where-Object RequiresEnrollmentAgentSignature
        Retrieves all AD CS objects, adds RequiresEnrollmentAgentSignature property, and filters to vulnerable templates.
 
        .NOTES
        Templates requiring enrollment agent signatures (ESC3 Condition 2) are vulnerable when:
        - msPKI-RA-Signature = 1 (requires authorized signature)
        - msPKI-RA-Application-Policies contains Certificate Request Agent EKU
        - Template includes Client Authentication EKU
        - Enrollment permissions granted to low-privilege or dangerous principals
         
        An attacker with an enrollment agent certificate can use these templates to request
        certificates on behalf of other principals and authenticate as them.
 
        .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 enrollment agent signatures..."
        $enrollmentAgentEKU = '1.3.6.1.4.1.311.20.2.1'
    }

    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"
                
                $requiresEnrollmentAgentSignature = $false
                
                # Check if msPKI-RA-Signature = 1 (requires authorized signature)
                if ($_.Properties.'msPKI-RA-Signature'.Count -gt 0) {
                    [int]$raSignature = $_.Properties.'msPKI-RA-Signature'[0]
                    Write-Verbose "msPKI-RA-Signature value: $raSignature"
                    
                    # Check if msPKI-RA-Application-Policies contains enrollment agent EKU
                    if ($raSignature -eq 1 -and $_.Properties.'msPKI-RA-Application-Policies'.Count -gt 0) {
                        $raPolicies = $_.Properties.'msPKI-RA-Application-Policies'
                        Write-Verbose "msPKI-RA-Application-Policies contains $($raPolicies.Count) policy/ies: $($raPolicies -join ', ')"
                        
                        if ($enrollmentAgentEKU -in $raPolicies) {
                            $requiresEnrollmentAgentSignature = $true
                            Write-Verbose "Template requires enrollment agent signature"
                        } else {
                            Write-Verbose "Template requires signature but not from enrollment agent"
                        }
                    } else {
                        Write-Verbose "Template does not require enrollment agent signature (RA-Signature: $raSignature)"
                    }
                } else {
                    Write-Verbose "msPKI-RA-Signature not present or is 0 - no signature required"
                }
                
                # Update the AdcsObjectStore with the RequiresEnrollmentAgentSignature property
                $dn = $_.Properties.distinguishedName[0]
                if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn] | Add-Member -NotePropertyName RequiresEnrollmentAgentSignature -NotePropertyValue $requiresEnrollmentAgentSignature -Force
                    Write-Verbose "Updated AD CS Object Store for $dn with RequiresEnrollmentAgentSignature = $requiresEnrollmentAgentSignature"
                }
                
                # Also add to the pipeline object for backward compatibility
                $_ | Add-Member -NotePropertyName RequiresEnrollmentAgentSignature -NotePropertyValue $requiresEnrollmentAgentSignature -Force
                
                # Return the modified object
                $_
            }
            catch {
                Write-Error "Error processing template $($_.Properties.distinguishedName[0]): $_"
            }
        }
    }

    end {
        Write-Verbose "Finished identifying templates requiring enrollment agent signatures"
    }
}