Private/Find-ESC4e1.ps1
|
function Find-ESC4e1 { <# .SYNOPSIS Finds Critical ESC4 vulnerabilities with templates that are enabled on one or more CAs for a specific principal or current user. .DESCRIPTION This function analyzes ESCalatorIssue objects to identify Critical ESC4 (Vulnerable Certificate Template Access Control) vulnerabilities where the vulnerable templates are enabled on one or more Certificate Authorities. If no principal is provided, the function will analyze Critical ESC4 issues that apply to the current user. If a specific principal DirectoryEntry is provided, it will analyze issues for that principal instead. The function filters for Critical severity ESC4 issues and additionally verifies that the affected certificate templates are actually enabled/published on at least one Certificate Authority, making them actively exploitable. .PARAMETER Issues An array of ESCalatorIssue objects to analyze. These should be the output from Find-ESC4Issue function that has been processed and expanded. .PARAMETER Principal Optional. A DirectoryEntry object representing a specific security principal to analyze. If provided, the function will only return Critical ESC4 issues that apply to this principal. If not provided, returns Critical ESC4 issues that apply to the current user. .INPUTS ESCalatorIssue[] Array of ESCalatorIssue objects from ESC4 vulnerability scans. .OUTPUTS PSCustomObject[] Returns an array of custom objects representing Critical ESC4 vulnerabilities with enabled templates. Each object contains details about the vulnerability, affected principal, and enabled templates. .EXAMPLE # Analyze Critical ESC4 issues with enabled templates for current user (no principal specified) $esc4Issues = Find-ESC4Issue -AdcsObjects $AdcsObjects $expandedIssues = $esc4Issues | Expand-Issue $enabledESC4 = Find-ESC4e1 -Issues $expandedIssues .EXAMPLE # Analyze Critical ESC4 issues with enabled templates for a specific user $userPrincipal = Get-AdcsObjects | Where-Object { $_.Properties['sAMAccountName'].Value -eq 'testuser' } $enabledESC4 = Find-ESC4e1 -Issues $expandedIssues -Principal $userPrincipal .EXAMPLE # Pipeline usage $expandedIssues | Find-ESC4e1 .LINK https://posts.specterops.io/certified-pre-owned-d95910965cd2 .NOTES This function focuses specifically on Critical severity ESC4 issues where the vulnerable certificate templates are enabled on one or more CAs, representing actively exploitable vulnerabilities. The function requires that issues have been properly expanded using Expand-Issue to ensure individual principal analysis is possible. Templates must be enabled/published on at least one CA to be considered exploitable, as disabled templates cannot be used for certificate enrollment attacks. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [object[]]$Issues, [Parameter()] [System.DirectoryServices.DirectoryEntry]$Principal ) begin { Write-Verbose "[$(Get-Date -Format 'yyyy-MM-dd hh:mm:ss')] Starting $($MyInvocation.MyCommand) on $env:COMPUTERNAME..." # Initialize results array $enabledESC4Issues = @() # Determine target principal for analysis if ($Principal) { # Get the principal name properly $principalDisplayName = $null if ($Principal.Properties['sAMAccountName'].Value) { $principalDisplayName = $Principal.Properties['sAMAccountName'].Value } elseif ($Principal.Properties['name'].Value) { $principalDisplayName = $Principal.Properties['name'].Value } elseif ($Principal.Properties['distinguishedName'].Value) { $principalDisplayName = $Principal.Properties['distinguishedName'].Value } else { $principalDisplayName = "Unknown" } Write-Verbose "Analyzing Critical ESC4 issues with enabled templates for specific principal: $principalDisplayName" } else { # No principal specified - analyze for current user $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent() $currentUserSid = $currentUser.User.Value $currentUserName = $currentUser.Name Write-Verbose "No principal specified - analyzing Critical ESC4 issues with enabled templates for current user: $currentUserName (SID: $currentUserSid)" # Store current user info for comparison $targetUserSid = $currentUserSid $targetUserName = $currentUserName } } process { # Process all issues in the current pipeline input foreach ($issue in $Issues) { # Validate that this is an ESCalatorIssue object if ($issue.PSObject.TypeNames[0] -ne 'ESCalatorIssue') { Write-Warning "Skipping non-ESCalatorIssue object: $($issue.GetType().Name)" continue } # Filter for Critical ESC4 issues only if ($issue.Technique -ne 'ESC4') { Write-Verbose "Skipping non-ESC4 issue: $($issue.Technique)" continue } if ($issue.Severity -ne 'Critical') { Write-Verbose "Skipping non-Critical ESC4 issue: $($issue.Severity) severity" continue } # Check if the template is enabled on one or more CAs $templateEnabled = $false if ($issue.DirectoryEntry -and $issue.DirectoryEntry.SchemaClassName -eq 'pKICertificateTemplate') { try { # Check if the template has the Enabled property set by Set-EnabledTemplateStatus $templateName = $issue.DirectoryEntry.Properties['name'].Value if ($issue.DirectoryEntry.PSObject.Properties['Enabled']) { $templateEnabled = $issue.DirectoryEntry.Enabled if ($templateEnabled) { Write-Verbose "Template '$templateName' is enabled on one or more CAs" } else { Write-Verbose "Template '$templateName' is not enabled on any CAs, skipping" continue } } else { Write-Warning "Template '$templateName' does not have Enabled property set - ensure Set-EnabledTemplateStatus was called" continue } } catch { Write-Warning "Failed to check template enabled status: $($_.Exception.Message)" continue } } else { Write-Verbose "Issue does not target a certificate template, skipping" continue } # Filter for issues that apply to the target principal (specific principal or current user) if ($Principal) { # Specific principal provided - use existing logic $principalSid = $null $principalName = $null # Get principal identifiers for comparison if ($Principal.Properties['objectSid'].Value) { $principalSid = (New-Object System.Security.Principal.SecurityIdentifier($Principal.Properties['objectSid'].Value, 0)).Value } # Fix the boolean issue with -or operator $principalName = $null if ($Principal.Properties['sAMAccountName'].Value) { $principalName = $Principal.Properties['sAMAccountName'].Value } elseif ($Principal.Properties['name'].Value) { $principalName = $Principal.Properties['name'].Value } $principalDN = $Principal.Properties['distinguishedName'].Value Write-Verbose "Checking issue against principal - SID: $principalSid, Name: $principalName, DN: $principalDN" Write-Verbose "Issue details - Principal: '$($issue.Principal)', IdentityReferenceSID: '$($issue.IdentityReferenceSID)'" # Check if the issue applies to this principal $appliesToPrincipal = $false # Check by SID if available if ($principalSid -and $issue.IdentityReferenceSID) { if ($issue.IdentityReferenceSID -eq $principalSid) { $appliesToPrincipal = $true Write-Verbose "Issue matches principal by SID: $principalSid" } } # Check by name if SID match fails if (-not $appliesToPrincipal -and $principalName -and $issue.Principal) { if ($issue.Principal -like "*$principalName*" -or $issue.Principal -eq $principalName) { $appliesToPrincipal = $true Write-Verbose "Issue matches principal by name: $principalName" } } # Check by DN if other matches fail if (-not $appliesToPrincipal -and $principalDN -and $issue.Principal) { if ($issue.Principal -eq $principalDN) { $appliesToPrincipal = $true Write-Verbose "Issue matches principal by DN: $principalDN" } } # Skip if this issue doesn't apply to the specified principal if (-not $appliesToPrincipal) { Write-Verbose "Issue does not apply to specified principal, skipping" continue } } else { # No principal specified - check if issue applies to current user $appliesToCurrentUser = $false # Check by SID if available if ($issue.IdentityReferenceSID -and $issue.IdentityReferenceSID -eq $targetUserSid) { $appliesToCurrentUser = $true Write-Verbose "Issue matches current user by SID: $targetUserSid" } # Check by name if SID match fails if (-not $appliesToCurrentUser -and $issue.Principal) { # Extract just the username from domain\username format $currentUserShortName = $targetUserName -replace '^.*\\', '' if ($issue.Principal -like "*$currentUserShortName*" -or $issue.Principal -like "*$targetUserName*") { $appliesToCurrentUser = $true Write-Verbose "Issue matches current user by name: $targetUserName" } } # Skip if this issue doesn't apply to the current user if (-not $appliesToCurrentUser) { Write-Verbose "Issue does not apply to current user, skipping" continue } } # This is a Critical ESC4 issue with an enabled template that matches our criteria Write-Verbose "Found Critical ESC4 issue with enabled template: $($issue.Name) - $($issue.Principal)" $enabledESC4Issues += $issue } } end { Write-Verbose "Found $($enabledESC4Issues.Count) Critical ESC4 issue(s) with enabled templates" # Group issues by principal and create structured objects $principalGroups = @{} $structuredResults = @() # Group issues by principal foreach ($issue in $enabledESC4Issues) { $principalKey = $issue.IdentityReferenceSID -or $issue.IdentityReference -or "Unknown" if (-not $principalGroups[$principalKey]) { $principalGroups[$principalKey] = @{ PrincipalSID = $issue.IdentityReferenceSID PrincipalName = $issue.IdentityReference Issues = @() } } $principalGroups[$principalKey].Issues += $issue } # Create structured objects for each principal foreach ($principalKey in $principalGroups.Keys) { $group = $principalGroups[$principalKey] $structuredObject = [PSCustomObject]@{ PSTypeName = 'ESC4e1_Result' PrincipalSID = $group.PrincipalSID PrincipalName = $group.PrincipalName ESC4e1Count = $group.Issues.Count ESC4e1Issues = $group.Issues VulnerableTemplates = ($group.Issues | ForEach-Object { if ($_.DirectoryEntry -and $_.DirectoryEntry.Properties['name'].Value) { $_.DirectoryEntry.Properties['name'].Value } } | Sort-Object -Unique) EnabledTemplateCount = ($group.Issues | ForEach-Object { if ($_.DirectoryEntry -and $_.DirectoryEntry.Properties['name'].Value) { $_.DirectoryEntry.Properties['name'].Value } } | Sort-Object -Unique | Measure-Object).Count RiskLevel = "Critical" Attack = "Template Modification (ESC4e1)" Technique = "ESC4" EnabledStatus = "Enabled" } $structuredResults += $structuredObject Write-Verbose "Created ESC4e1 result for principal: $($group.PrincipalName) (Issues: $($group.Issues.Count), Templates: $($structuredObject.EnabledTemplateCount))" } if ($structuredResults.Count -gt 0) { $totalIssues = ($structuredResults | Measure-Object -Property ESC4e1Count -Sum).Sum $uniquePrincipals = ($structuredResults | Select-Object -ExpandProperty PrincipalName | Sort-Object -Unique).Count $allTemplates = $structuredResults | ForEach-Object { $_.VulnerableTemplates } | Sort-Object -Unique Write-Verbose " Total ESC4e1 issues: $totalIssues" Write-Verbose " Affected principals: $uniquePrincipals" if ($allTemplates) { Write-Verbose " Enabled vulnerable templates: $($allTemplates -join ', ')" } } Write-Verbose "[$(Get-Date -Format 'yyyy-MM-dd hh:mm:ss')] Finishing $($MyInvocation.MyCommand) on $env:COMPUTERNAME..." # Return the structured results return $structuredResults } } |