Private/Show-ESCAnalysisMenu.ps1

function Show-ESCAnalysisMenu {
    <#
        .SYNOPSIS
        Displays ESC vulnerability analysis results in a simple menu format.
 
        .DESCRIPTION
        This function accepts ESCalator issue objects, runs Find-ESC4e1, Find-ESC4p5Combo, and Find-ESC5p5Combo
        analysis functions, and presents the results in a menu showing attack descriptions.
 
        .PARAMETER Issues
        Mandatory array of one or more ESCalatorIssue objects to analyze.
 
        .PARAMETER Principal
        Optional DirectoryEntry object representing a specific security principal to analyze.
        If not provided, analyzes for the current user.
 
        .INPUTS
        ESCalatorIssue[] - Array of ESCalatorIssue objects
        System.DirectoryServices.DirectoryEntry - Optional principal object
 
        .OUTPUTS
        None - Interactive menu display
 
        .EXAMPLE
        Show-ESCAnalysisMenu -Issues $AllIssues
         
        Analyzes all issues for the current user and displays results menu.
 
        .EXAMPLE
        Show-ESCAnalysisMenu -Issues $AllIssues -Principal $userPrincipal
         
        Analyzes all issues for a specific principal and displays results menu.
 
        .NOTES
        This function provides a simplified interface for ESC vulnerability analysis,
        focusing on attack descriptions rather than detailed technical information.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Issues,
        
        [Parameter()]
        [System.DirectoryServices.DirectoryEntry]$Principal
    )

    # PowerShell 5.1 has no `e escape; use the ESC character directly for ANSI sequences.
    $esc = [char]0x1b


    Write-Verbose "[$(Get-Date -Format 'yyyy-MM-dd hh:mm:ss')] Starting $($MyInvocation.MyCommand) on $env:COMPUTERNAME..."

    # Initialize result collections
    $esc1Results = @()
    $esc2Results = @()
    $esc4e1Results = @()
    $esc4p5ComboResults = @()
    $esc5p5ComboResults = @()

    try {
        # Run Find-ESC1
        Write-Verbose "Running Find-ESC1..."
        if ($Principal) {
            $esc1Results = @(Find-ESC1 -Issues $Issues -Principal $Principal)
        } else {
            $esc1Results = @(Find-ESC1 -Issues $Issues)
        }
        Write-Verbose "Find-ESC1 found $($esc1Results.Count) results"

        # Run Find-ESC2
        Write-Verbose "Running Find-ESC2..."
        if ($Principal) {
            $esc2Results = @(Find-ESC2 -Issues $Issues -Principal $Principal)
        } else {
            $esc2Results = @(Find-ESC2 -Issues $Issues)
        }
        Write-Verbose "Find-ESC2 found $($esc2Results.Count) results"

        # Run Find-ESC4e1
        Write-Verbose "Running Find-ESC4e1..."
        if ($Principal) {
            $esc4e1Results = @(Find-ESC4e1 -Issues $Issues -Principal $Principal)
        } else {
            $esc4e1Results = @(Find-ESC4e1 -Issues $Issues)
        }
        Write-Verbose "Find-ESC4e1 found $($esc4e1Results.Count) results"

        # Run Find-ESC4p5Combo
        Write-Verbose "Running Find-ESC4p5Combo..."
        if ($Principal) {
            $esc4p5ComboResults = @(Find-ESC4p5Combo -Issues $Issues -Principal $Principal)
        } else {
            $esc4p5ComboResults = @(Find-ESC4p5Combo -Issues $Issues)
        }
        Write-Verbose "Find-ESC4p5Combo found $($esc4p5ComboResults.Count) results"

        # Run Find-ESC5p5Combo
        Write-Verbose "Running Find-ESC5p5Combo..."
        if ($Principal) {
            $esc5p5ComboResults = @(Find-ESC5p5Combo -Issues $Issues -Principal $Principal)
        } else {
            $esc5p5ComboResults = @(Find-ESC5p5Combo -Issues $Issues)
        }
        Write-Verbose "Find-ESC5p5Combo found $($esc5p5ComboResults.Count) results"

    } catch {
        Write-Error "Error occurred during ESC analysis: $($_.Exception.Message)"
        return
    }

    # Calculate totals
    $totalVulnerabilities = $esc1Results.Count + $esc2Results.Count + $esc4e1Results.Count + $esc4p5ComboResults.Count + $esc5p5ComboResults.Count

    # Display analysis header
    Write-Host ""
    Write-Host "=== ESC Vulnerability Analysis Results ===" -ForegroundColor Cyan
    
    if ($Principal) {
        $principalName = $null
        if ($Principal.Properties['sAMAccountName'].Value) {
            $principalName = $Principal.Properties['sAMAccountName'].Value
        } elseif ($Principal.Properties['name'].Value) {
            $principalName = $Principal.Properties['name'].Value
        } else {
            $principalName = "Unknown Principal"
        }
        Write-Host "Principal: $principalName" -ForegroundColor White
    } else {
        $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        Write-Host "Principal: $($currentUser.Name) (Current User)" -ForegroundColor White
    }
    
    Write-Host "Analysis Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray
    Write-Host ""

    # Check if any vulnerabilities were found
    if ($totalVulnerabilities -eq 0) {
        Write-Host "[+] No ESC vulnerabilities found for this principal!" -ForegroundColor Green
        Write-Host ""
        Write-Host "The analyzed principal does not have any of the following vulnerability combinations:" -ForegroundColor Gray
        Write-Host " - ESC1: SAN Spoofing (enabled templates with enrollee-supplied subject)" -ForegroundColor Gray
        Write-Host " - ESC2: Any Purpose EKU / No EKU (Enroll On Behalf Of)" -ForegroundColor Gray
        Write-Host " - ESC4e1: ESC4 (enabled templates)" -ForegroundColor Gray
        Write-Host " - ESC4p5: ESC4 (disabled templates) + ESC5 (pKIEnrollmentService certificateTemplates attribute) combinations" -ForegroundColor Gray
        Write-Host " - ESC5p5: ESC5 (Certificate Templates container) + ESC5 (pKIEnrollmentService certificateTemplates attribute) combinations" -ForegroundColor Gray
        Write-Host ""
        Read-Host "Press Enter to continue"
        return
    }

    # Build attack menu dynamically: each entry has a label and an action key
    $attackMenu = @()

    if ($esc1Results.Count -gt 0) {
        $attackMenu += [PSCustomObject]@{
            Key = 'ESC1'
            Label = "ESC1: SAN spoofing attack`n - Can request certificates with arbitrary Subject Alternative Names from enabled templates"
        }
    }

    if ($esc2Results.Count -gt 0) {
        $attackMenu += [PSCustomObject]@{
            Key = 'ESC2'
            Label = "ESC2: Any Purpose EKU / No EKU attack (Enroll On Behalf Of)`n - Can use an Any-Purpose certificate as a Request Agent to enroll on behalf of other users"
        }
    }

    if ($esc4e1Results.Count -gt 0) {
        $attackMenu += [PSCustomObject]@{
            Key = 'ESC4e1'
            Label = "ESC4e1: Immediate template modification attack`n - Can modify one or more enabled certificate templates for instant privilege escalation"
        }
    }

    if ($esc4p5ComboResults.Count -gt 0) {
        $attackMenu += [PSCustomObject]@{
            Key = 'ESC4p5'
            Label = "ESC4p5: Combined template control attack`n - Can modify one or more disabled certificate templates AND enable disabled templates"
        }
    }

    if ($esc5p5ComboResults.Count -gt 0) {
        $attackMenu += [PSCustomObject]@{
            Key = 'ESC5p5'
            Label = "ESC5p5: Full PKI infrastructure control`n - Can create new certificate templates AND enabled disabled templates"
        }
    }

    do {
        # Display attack descriptions
        Write-Host "Available Attacks:" -ForegroundColor Yellow
        Write-Host ""

        for ($i = 0; $i -lt $attackMenu.Count; $i++) {
            $optionNumber = $i + 1
            Write-Host "$optionNumber. $($attackMenu[$i].Label)" -ForegroundColor White
        }

        Write-Host ""
        Write-Host "q. Quit" -ForegroundColor Red
        Write-Host ""

        # Get user choice
        Write-Host "${esc}[1mSelect an attack to explore${esc}[0m" -NoNewline
        Write-Host " (1-$($attackMenu.Count), q=quit): " -NoNewline
        $choice = Read-Host

        $choice = $choice.Trim().ToLower()

        if ($choice -eq 'q') {
            Write-Host ""
            Write-Host "Goodbye!" -ForegroundColor Green
            return
        }

        # Try to parse as integer
        $numericChoice = 0
        if ([int]::TryParse($choice, [ref]$numericChoice)) {
            if ($numericChoice -ge 1 -and $numericChoice -le $attackMenu.Count) {
                Write-Host ""
                $selectedAttack = $attackMenu[$numericChoice - 1].Key

                switch ($selectedAttack) {
                    'ESC1' {
                        Show-ESC1AttackDetails -Results $esc1Results -Principal $Principal
                    }
                    'ESC2' {
                        Show-ESC2AttackDetails -Results $esc2Results -Principal $Principal
                    }
                    'ESC4e1' {
                        Show-ESC4e1AttackDetails -Results $esc4e1Results -Principal $Principal
                    }
                    'ESC4p5' {
                        Show-ESC4p5AttackDetails -Results $esc4p5ComboResults
                        Invoke-InteractiveAttack -AttackType "ESC4p5" -AttackResult $esc4p5ComboResults[0] -Principal $Principal
                    }
                    'ESC5p5' {
                        Show-ESC5p5AttackDetails -Results $esc5p5ComboResults
                        Invoke-InteractiveAttack -AttackType "ESC5p5" -AttackResult $esc5p5ComboResults[0] -Principal $Principal
                    }
                }
                Write-Host ""
                Read-Host "Press Enter to continue"
            } else {
                Write-Host "${esc}[38;5;196m[x] Invalid choice. Please enter a number between 1 and $($attackMenu.Count) or 'q' to quit.${esc}[0m" -ForegroundColor Red
                Write-Host ""
            }
        } else {
            Write-Host "${esc}[38;5;196m[x] Invalid input. Please enter a number (1-$($attackMenu.Count)) or 'q' to quit.${esc}[0m" -ForegroundColor Red
            Write-Host ""
        }

    } while ($choice -ne 'q')

    Write-Verbose "[$(Get-Date -Format 'yyyy-MM-dd hh:mm:ss')] Finishing $($MyInvocation.MyCommand) on $env:COMPUTERNAME..."
}