Private/Set/Set-DangerousCAAdministrator.ps1

function Set-DangerousCAAdministrator {
    <#
        .SYNOPSIS
        Adds a DangerousCAAdministrator property to AD CS Certification Authority objects.
 
        .DESCRIPTION
        Examines the CA Administrator role assignments on Certification Authorities to identify
        overly permissive administrative permissions.
         
        The function checks for CA Administrator roles granted to well-known dangerous principals
        that represent overly broad groups. These should typically not have CA Administrator
        permissions, as they can lead to privilege escalation vulnerabilities (ESC7).
         
        The function adds two properties to each CA object:
        1. DangerousCAAdministrator: Array of SIDs for dangerous principals
        2. DangerousCAAdministratorNames: Array of human-readable names formatted as "DOMAIN\User (SID)"
           or "SID (could not resolve)" if the principal cannot be resolved.
 
        .PARAMETER AdcsObject
        One or more DirectoryEntry objects representing AD CS Certification Authorities.
        Must have the CAAdministrators property set (typically by Set-CAAdministrator).
 
        .INPUTS
        System.DirectoryServices.DirectoryEntry[]
        You can pipe CA DirectoryEntry objects to this function.
 
        .OUTPUTS
        System.DirectoryServices.DirectoryEntry[]
        Returns the input objects with added properties:
        - DangerousCAAdministrator: Array of SIDs
        - DangerousCAAdministratorNames: Array of human-readable names
 
        .EXAMPLE
        $cas | Set-DangerousCAAdministrator
        Processes all CAs and identifies dangerous CA Administrators.
 
        .EXAMPLE
        $cas | Set-CAAdministrator | Set-DangerousCAAdministrator
        Queries CA Administrators and then identifies dangerous ones.
 
        .NOTES
        Well-known dangerous principals checked by default:
        - S-1-0-0: NULL SID
        - S-1-1-0: Everyone (all users possibly including anonymous)
        - S-1-5-7: Anonymous Logon
        - S-1-5-32-545: BUILTIN\Users
        - S-1-5-11: Authenticated Users
        - SIDs ending in -513: Domain Users groups
        - SIDs ending in -515: Domain Computers groups
         
        CAs with these principals having administrative permissions are considered
        high-risk as they can approve malicious certificate requests.
 
        .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 CAs with dangerous CA Administrators..."
    }

    process {
        $AdcsObject | Where-Object SchemaClassName -eq pKIEnrollmentService | ForEach-Object {
            try {
                $caName = if ($_.Properties -and $_.Properties.Contains('cn')) {
                    $_.Properties['cn'][0]
                } elseif ($_.cn) {
                    $_.cn
                } else {
                    'Unknown CA'
                }
                
                Write-Verbose "Processing CA: $caName"
                
                # Get CAAdministrators from the AdcsObjectStore
                $dn = $_.Properties.distinguishedName[0]
                $caAdministrators = if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn].CAAdministrators
                } else {
                    $null
                }
                
                if (-not $caAdministrators) {
                    Write-Verbose " CA '$caName' has no CAAdministrators property - skipping dangerous check"
                    $_
                    return
                }
                
                [array]$dangerousSids = foreach ($admin in $caAdministrators) {
                    try {
                        # Convert the admin name to NTAccount and then to SID
                        $ntAccount = New-Object System.Security.Principal.NTAccount($admin.CAAdministrator)
                        $sid = $ntAccount | Convert-IdentityReferenceToSid
                        
                        $isDangerous = $sid | Test-IsDangerousPrincipal
                        if ($isDangerous) {
                            Write-Verbose " Dangerous CA Administrator found: $($admin.CAAdministrator)"
                            # Ensure principal is in PrincipalStore
                            $null = $ntAccount | Resolve-Principal
                            $sid.Value
                        }
                    } catch {
                        Write-Verbose " Failed to check if '$($admin.CAAdministrator)' is dangerous: $($_.Exception.Message)"
                    }
                }
                
                $dangerousSids = $dangerousSids | Sort-Object -Unique
                
                if ($dangerousSids) {
                    Write-Verbose " CA has $($dangerousSids.Count) dangerous CA Administrator(s): $($dangerousSids -join ', ')"
                } else {
                    Write-Verbose " No dangerous CA Administrators found"
                }
                
                # Build human-readable names array from PrincipalStore
                [array]$dangerousNames = $dangerousSids | ForEach-Object {
                    if ($script:PrincipalStore -and $script:PrincipalStore.ContainsKey($_)) {
                        $name = $script:PrincipalStore[$_].ntAccountName
                        if ($name) {
                            "$name ($_)"
                        } else {
                            "$_ (could not resolve)"
                        }
                    } else {
                        "$_ (could not resolve)"
                    }
                } | Sort-Object -Unique
                
                # Update the AD CS Object Store with the DangerousCAAdministrator property
                $dn = $_.Properties.distinguishedName[0]
                if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn].DangerousCAAdministrator = $dangerousSids
                    $script:AdcsObjectStore[$dn].DangerousCAAdministratorNames = $dangerousNames
                    Write-Verbose " Updated AD CS Object Store for $dn with DangerousCAAdministrator"
                }
                
                # Also add to the pipeline object for backward compatibility
                $_ | Add-Member -NotePropertyName DangerousCAAdministrator -NotePropertyValue $dangerousSids -Force
                $_ | Add-Member -NotePropertyName DangerousCAAdministratorNames -NotePropertyValue $dangerousNames -Force
                
                # Return the modified object
                $_
                
            } catch {
                Write-Warning "Error processing CA: $($_.Exception.Message)"
                $_
            }
        }
    }

    end {
        Write-Verbose "Dangerous CA Administrator check complete"
    }
}