Analyzers/Test-Esc10.ps1

function Test-Esc10 {
    <#
    .SYNOPSIS
        ESC10 - Weak Certificate Mappings (per-DC).
    .DESCRIPTION
        Read-only analyzer over $Context.DcMappings. Two cases:
          Case 1 (Kerberos): StrongCertificateBindingEnforcement == 0
            (Kdc\StrongCertificateBindingEnforcement) - disables strong binding,
            allowing certificate-to-account mapping abuse.
          Case 2 (Schannel): WeakSchannelMapping == $true, i.e.
            (CertificateMappingMethods -band 0x4) - UPN mapping enabled
            (SCHANNEL\CertificateMappingMethods).
        One finding per affected DC (a DC can be flagged for both cases).
        Unreachable DCs -> ManualReview.
    .PARAMETER Context
        AssessmentContext pscustomobject.
    .PARAMETER ExtraLowPrivSid
        Unused for ESC10 (kept for signature uniformity).
    .OUTPUTS
        Finding[]
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [pscustomobject] $Context,

        [Parameter(Mandatory = $false)]
        [string[]] $ExtraLowPrivSid = @()
    )

    $reference = 'https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-and-some-fixes-42f9f5f4c142'
    $findings = @()

    if ($null -eq $Context.DcMappings -or @($Context.DcMappings).Count -eq 0) {
        return @([pscustomobject]@{
            Id = 'ESC10'; Title = 'ESC10 DC mapping data unavailable'; Severity = 'High';
            Status = 'ManualReview'; AffectedObject = 'Domain Controllers';
            Evidence = [pscustomobject]@{ Reason = 'Context.DcMappings was null/empty; DC registry values not collected.' };
            Principals = @(); Exploitability = 'Theoretical'; RiskScore = 0;
            Remediation = 'Collect Kdc\StrongCertificateBindingEnforcement and SCHANNEL\CertificateMappingMethods from each DC.';
            Reference = $reference
        })
    }

    $anyReachable = $false

    foreach ($dc in $Context.DcMappings) {
        $dcName = $dc.DomainController

        if ($dc.Reachable -eq $false) {
            $findings += [pscustomobject]@{
                Id = 'ESC10'; Title = "DC '$dcName' unreachable - mapping enforcement unknown"; Severity = 'High';
                Status = 'ManualReview'; AffectedObject = $dcName;
                Evidence = [pscustomobject]@{ DomainController = $dcName; Reachable = $false; Reason = 'Remote registry read failed.' };
                Principals = @(); Exploitability = 'Theoretical'; RiskScore = 0;
                Remediation = 'Manually verify StrongCertificateBindingEnforcement and CertificateMappingMethods on this DC.';
                Reference = $reference
            }
            continue
        }
        $anyReachable = $true

        if ($null -ne $dc.StrongCertificateBindingEnforcement -and [int]$dc.StrongCertificateBindingEnforcement -eq 0) {
            $findings += [pscustomobject]@{
                Id = 'ESC10'
                Title = "DC '$dcName' has StrongCertificateBindingEnforcement=0 (ESC10 Case 1)"
                Severity = 'High'
                Status = 'Vulnerable'
                AffectedObject = $dcName
                Evidence = [pscustomobject]@{
                    DomainController = $dcName
                    Case = 'Kerberos (StrongCertificateBindingEnforcement)'
                    StrongCertificateBindingEnforcement = 0
                    Meaning = '0=Disabled. Certificate SAN/UPN can be mapped to arbitrary accounts (combine with altSecurityIdentities / ESC9).'
                }
                Principals = @()
                Exploitability = 'High'
                RiskScore = 0
                Remediation = 'Set Kdc\StrongCertificateBindingEnforcement=2 (Full Enforcement) after remediating weak certificate mappings.'
                Reference = $reference
            }
        }

        $weakSchannel = $false
        if ($null -ne $dc.WeakSchannelMapping) { $weakSchannel = [bool]$dc.WeakSchannelMapping }
        elseif ($null -ne $dc.CertificateMappingMethods) { $weakSchannel = (([int]$dc.CertificateMappingMethods) -band 0x4) -ne 0 }

        if ($weakSchannel) {
            $findings += [pscustomobject]@{
                Id = 'ESC10'
                Title = "DC '$dcName' enables weak Schannel UPN mapping (ESC10 Case 2)"
                Severity = 'High'
                Status = 'Vulnerable'
                AffectedObject = $dcName
                Evidence = [pscustomobject]@{
                    DomainController = $dcName
                    Case = 'Schannel (CertificateMappingMethods)'
                    CertificateMappingMethods = $dc.CertificateMappingMethods
                    UpnBitSet = $true
                    Meaning = '0x4 (UPN) mapping enabled - a certificate with a spoofable UPN can authenticate over Schannel as the victim.'
                }
                Principals = @()
                Exploitability = 'High'
                RiskScore = 0
                Remediation = 'Remove the 0x4 (UPN) bit from SCHANNEL\CertificateMappingMethods; prefer strong mapping methods only.'
                Reference = $reference
            }
        }
    }

    if ($findings.Count -eq 0) {
        $status = 'NotVulnerable'; $sev = 'Info'
        if (-not $anyReachable) { $status = 'ManualReview'; $sev = 'High' }
        return @([pscustomobject]@{
            Id = 'ESC10'; Title = 'No ESC10 weak certificate mappings detected'; Severity = $sev;
            Status = $status; AffectedObject = 'Domain Controllers';
            Evidence = [pscustomobject]@{ DcsChecked = @($Context.DcMappings).Count; AnyReachable = $anyReachable };
            Principals = @(); Exploitability = 'Theoretical'; RiskScore = 0;
            Remediation = 'None required.'; Reference = $reference
        })
    }

    return $findings
}