Private/Set/Set-CAInterfaceFlags.ps1

function Set-CAInterfaceFlags {
    <#
        .SYNOPSIS
        Adds InterfaceFlags configuration properties to AD CS Certification Authority objects.
 
        .DESCRIPTION
        For each pKIEnrollmentService (CA) object, queries the CA's InterfaceFlags registry
        configuration using PSCertutil's Get-PCInterfaceFlag cmdlet and stores the results
        in the AdcsObjectStore.
         
        This function tracks various interface flags that control CA RPC/DCOM behavior,
        including the IF_ENFORCEENCRYPTICERTREQUEST flag which when disabled leads to
        the ESC11 vulnerability (allowing unauthenticated/unencrypted certificate requests).
         
        The function adds these properties to each CA object:
        - InterfaceFlags: Array of all InterfaceFlag objects returned by Get-PCInterfaceFlag
        - RPCEncryptionNotRequired: Boolean indicating if IF_ENFORCEENCRYPTICERTREQUEST is disabled (ESC11)
 
        .PARAMETER AdcsObject
        One or more DirectoryEntry objects representing AD CS Certification Authorities.
        Must have the CAFullName property set (typically by Set-CAComputerPrincipal).
 
        .INPUTS
        System.DirectoryServices.DirectoryEntry[]
        You can pipe CA DirectoryEntry objects to this function.
 
        .OUTPUTS
        System.DirectoryServices.DirectoryEntry[]
        Returns the input objects with added InterfaceFlags properties.
 
        .EXAMPLE
        $cas | Set-CAInterfaceFlags
        Queries InterfaceFlags for all CAs and stores the results.
 
        .EXAMPLE
        $cas | Set-CAComputerPrincipal | Set-CAInterfaceFlags
        Sets up CA identification and then queries InterfaceFlags.
 
        .NOTES
        Requires the PSCertutil module to be installed and loaded.
        This function must be called after Set-CAComputerPrincipal has set the CAFullName property.
         
        The function silently skips CAs that:
        - Don't have a CAFullName property
        - Are unreachable or don't respond to certutil queries
        - Return errors from Get-PCInterfaceFlag
 
        .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 "Querying InterfaceFlags for Certification Authorities..."
    }

    process {
        $AdcsObject | Where-Object SchemaClassName -eq pKIEnrollmentService | ForEach-Object {
            try {
                # Extract CA name for logging
                $caName = if ($_.Properties -and $_.Properties.Contains('cn')) {
                    $_.Properties['cn'][0]
                } elseif ($_.cn) {
                    $_.cn
                } else {
                    'Unknown CA'
                }
                
                Write-Verbose "Processing CA: $caName"
                
                # Get CAFullName from the AdcsObjectStore (where LS2AdcsObject has CAFullName ScriptProperty)
                $dn = $_.Properties.distinguishedName[0]
                $caFullName = if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn].CAFullName
                } else {
                    $null
                }
                
                if (-not $caFullName) {
                    Write-Verbose " CA '$caName' has no CAFullName property - skipping InterfaceFlags query"
                    $_
                    continue
                }
                
                Write-Verbose " Querying InterfaceFlags for: $caFullName"
                
                try {
                    # Query InterfaceFlags using PSCertutil
                    $interfaceFlags = Get-PCInterfaceFlag -CAFullName $caFullName -ErrorAction Stop
                    
                    if ($interfaceFlags) {
                        Write-Verbose " Retrieved $(@($interfaceFlags).Count) InterfaceFlags"
                        
                        # Check specifically for IF_ENFORCEENCRYPTICERTREQUEST
                        $encryptionFlag = $interfaceFlags | Where-Object { $_.InterfaceFlag.ToString() -eq 'IF_ENFORCEENCRYPTICERTREQUEST' }
                        $rpcEncryptionNotRequired = if ($encryptionFlag) { -not $encryptionFlag.Enabled } else { $true }
                        
                        Write-Verbose " IF_ENFORCEENCRYPTICERTREQUEST is $(if ($rpcEncryptionNotRequired) { 'disabled or missing - RPC encryption not required' } else { 'enabled - RPC encryption required' })"
                        
                        # Update the AD CS Object Store
                        $dn = $_.Properties.distinguishedName[0]
                        if ($script:AdcsObjectStore.ContainsKey($dn)) {
                            $script:AdcsObjectStore[$dn].InterfaceFlags = $interfaceFlags
                            $script:AdcsObjectStore[$dn].RPCEncryptionNotRequired = $rpcEncryptionNotRequired
                            Write-Verbose " Updated AD CS Object Store for $dn with InterfaceFlags data"
                        }
                        
                        # Also add to the pipeline object for backward compatibility
                        $_ | Add-Member -NotePropertyName InterfaceFlags -NotePropertyValue $interfaceFlags -Force
                        $_ | Add-Member -NotePropertyName RPCEncryptionNotRequired -NotePropertyValue $rpcEncryptionNotRequired -Force
                        
                    } else {
                        Write-Verbose " No InterfaceFlags returned from Get-PCInterfaceFlag"
                    }
                    
                } catch {
                    Write-Verbose " Failed to query InterfaceFlags for '$caFullName': $($_.Exception.Message)"
                    # Continue processing other CAs
                }
                
                # Return the modified object
                $_
                
            } catch {
                $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                    $_.Exception,
                    'InterfaceFlagQueryFailed',
                    [System.Management.Automation.ErrorCategory]::InvalidOperation,
                    $_
                )
                $PSCmdlet.WriteError($errorRecord)
                
                # Still return the object even if processing failed
                $_
            }
        }
    }

    end {
        Write-Verbose "Done querying InterfaceFlags for Certification Authorities."
    }
}