Analyzers/Test-Esc6.ps1
|
function Test-Esc6 { <# .SYNOPSIS ESC6 - CA honours request-supplied SAN (EDITF_ATTRIBUTESUBJECTALTNAME2). .DESCRIPTION Flags CAs whose policy-module EditFlags has EDITF_ATTRIBUTESUBJECTALTNAME2 (0x00040000) set (collector-derived EditFlagsAttributeSanSet == $true). When set, the CA embeds a request-supplied SAN (san: request attribute) into the issued certificate for ANY template - even templates that build the subject from AD - effectively making every client-auth-capable template ESC1-like. Vulnerable (per CA) = EditFlagsAttributeSanSet == $true. CA unreachable / config missing -> ManualReview. .PARAMETER Context AssessmentContext pscustomobject. Reads $Context.CaConfigs. .PARAMETER ExtraLowPrivSid Extra SID strings to treat as low-privileged (unused; kept for signature parity). .OUTPUTS Finding[] #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [pscustomobject] $Context, [Parameter(Mandatory = $false)] [string[]] $ExtraLowPrivSid = @() ) $reference = 'SpecterOps "Certified Pre-Owned" (ESC6) / Certipy' $findings = @() $newFinding = { param($Title, $Severity, $Status, $Affected, $Evidence, $Principals, $Exploit, $Remediation) [pscustomobject]@{ Id = 'ESC6' Title = $Title Severity = $Severity Status = $Status AffectedObject = $Affected Evidence = $Evidence Principals = @($Principals) Exploitability = $Exploit RiskScore = 0 Remediation = $Remediation Reference = $reference } } $caConfigs = @() if ($null -ne $Context -and $null -ne $Context.CaConfigs) { $caConfigs = @($Context.CaConfigs) } if ($caConfigs.Count -eq 0) { return @(& $newFinding 'ESC6 - no CA configuration available' 'Critical' 'ManualReview' 'N/A' ` ([pscustomobject]@{ Error = 'Context.CaConfigs is empty or unavailable; could not evaluate ESC6.' }) ` @() 'Theoretical' 'Collect CA configuration (certutil -getreg policy\EditFlags) and re-run.') } foreach ($ca in $caConfigs) { if (-not $ca.Reachable) { $findings += & $newFinding ` ("ESC6 - CA '{0}' configuration unreachable" -f $ca.Name) ` 'Critical' 'ManualReview' $ca.Name ` ([pscustomobject]@{ Error = 'CA unreachable; EditFlags could not be read. Verify EDITF_ATTRIBUTESUBJECTALTNAME2 manually.'; DnsHostName = $ca.DnsHostName }) ` @() 'Theoretical' 'Verify certutil -config "<host>\<CA>" -getreg policy\EditFlags manually.' continue } if ($ca.EditFlagsAttributeSanSet) { $evidence = [pscustomobject]@{ EditFlagsAttributeSanSet = $true EditFlags = ('0x{0:X}' -f [int]$ca.EditFlags) FlagBit = 'EDITF_ATTRIBUTESUBJECTALTNAME2 (0x00040000)' DnsHostName = $ca.DnsHostName Impact = 'Any low-priv-enrollable client-auth template on this CA becomes ESC1-like via the san: request attribute.' } $findings += & $newFinding ` ("ESC6 - CA '{0}' honours request-supplied SAN (EDITF_ATTRIBUTESUBJECTALTNAME2 set)" -f $ca.Name) ` 'Critical' 'Vulnerable' $ca.Name $evidence @() 'High' ` 'Clear the flag: certutil -config "<host>\<CA>" -setreg policy\EditFlags -EDITF_ATTRIBUTESUBJECTALTNAME2, then restart CertSvc.' } } if ($findings.Count -eq 0) { return @(& $newFinding 'ESC6 - no CA honours request-supplied SAN' 'Critical' 'NotVulnerable' 'N/A' ` ([pscustomobject]@{ CasEvaluated = $caConfigs.Count }) @() 'Theoretical' 'No action required for ESC6.') } return $findings } |