Private/Set/Set-DangerousCACertificateManager.ps1

function Set-DangerousCACertificateManager {
    <#
        .SYNOPSIS
        Adds a DangerousCACertificateManager property to AD CS Certification Authority objects.
 
        .DESCRIPTION
        Examines the Certificate Manager role assignments on Certification Authorities to identify
        overly permissive administrative permissions.
         
        The function checks for Certificate Manager roles granted to well-known dangerous principals
        that represent overly broad groups. These should typically not have Certificate Manager
        permissions, as they can lead to privilege escalation vulnerabilities (ESC7).
         
        The function adds two properties to each CA object:
        1. DangerousCACertificateManager: Array of SIDs for dangerous principals
        2. DangerousCACertificateManagerNames: 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 CertificateManagers property set (typically by Set-CACertificateManager).
 
        .INPUTS
        System.DirectoryServices.DirectoryEntry[]
        You can pipe CA DirectoryEntry objects to this function.
 
        .OUTPUTS
        System.DirectoryServices.DirectoryEntry[]
        Returns the input objects with added properties:
        - DangerousCACertificateManager: Array of SIDs
        - DangerousCACertificateManagerNames: Array of human-readable names
 
        .EXAMPLE
        $cas | Set-DangerousCACertificateManager
        Processes all CAs and identifies dangerous Certificate Managers.
 
        .EXAMPLE
        $cas | Set-CACertificateManager | Set-DangerousCACertificateManager
        Queries Certificate Managers 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 Certificate Manager 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 Certificate Managers..."
    }

    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'
                }
                
                # Get the distinguished name - handle both DirectoryEntry and LS2AdcsObject
                $dn = if ($_.Properties.distinguishedName) {
                    $_.Properties.distinguishedName[0]
                } else {
                    $_.DistinguishedName
                }

                # Retrieve CertificateManagers from AdcsObjectStore
                $certificateManagers = if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn].CertificateManagers
                } else {
                    $_.CertificateManagers
                }

                [array]$dangerousSids = if ($certificateManagers) {
                    foreach ($manager in $certificateManagers) {
                        try {
                            # Convert the Certificate Manager name to an NTAccount
                            $ntAccount = New-Object System.Security.Principal.NTAccount($manager.CertificateManager)
                            
                            # Translate to SID
                            $sid = $ntAccount | Convert-IdentityReferenceToSid
                            
                            # Check if this is a dangerous principal
                            $isDangerous = $sid.Value | Test-IsDangerousPrincipal
                            
                            if ($isDangerous) {
                                Write-Verbose " Dangerous Certificate Manager found: $($manager.CertificateManager)"
                                # Ensure principal is in PrincipalStore
                                $null = $ntAccount | Resolve-Principal
                                $sid.Value
                            }
                        } catch {
                            Write-Verbose " Failed to check if '$($manager.CertificateManager)' is dangerous: $($_.Exception.Message)"
                        }
                    }
                } else {
                    @()
                }

                $dangerousSids = $dangerousSids | Sort-Object -Unique

                if ($dangerousSids) {
                    Write-Verbose " CA has $($dangerousSids.Count) dangerous Certificate Manager(s): $($dangerousSids -join ', ')"
                } else {
                    Write-Verbose " No dangerous Certificate Managers 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 DangerousCACertificateManager property
                if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn].DangerousCACertificateManager = $dangerousSids
                    $script:AdcsObjectStore[$dn].DangerousCACertificateManagerNames = $dangerousNames
                    Write-Verbose " Updated AD CS Object Store for $dn with DangerousCACertificateManager"
                }

                # Also add to the pipeline object for backward compatibility
                $_ | Add-Member -NotePropertyName DangerousCACertificateManager -NotePropertyValue $dangerousSids -Force
                $_ | Add-Member -NotePropertyName DangerousCACertificateManagerNames -NotePropertyValue $dangerousNames -Force
                
                # Return the modified object
                $_
                
            } catch {
                $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                    $_.Exception,
                    'DangerousCACertificateManagerProcessingFailed',
                    [System.Management.Automation.ErrorCategory]::InvalidOperation,
                    $_
                )
                $PSCmdlet.WriteError($errorRecord)
                
                # Still return the object even if processing failed
                $_
            }
        }
    }

    end {
        Write-Verbose "Dangerous Certificate Manager check complete"
    }
}