|
| 1 | +function New-LS2Dashboard { |
| 2 | + <# |
| 3 | + .SYNOPSIS |
| 4 | + Generates an interactive HTML dashboard for Locksmith2 scan results. |
| 5 | + |
| 6 | + .DESCRIPTION |
| 7 | + Creates a comprehensive HTML dashboard with left navigation menu showing: |
| 8 | + - All issues with expanded principals |
| 9 | + - Issues filtered by type (Template, CA, Object) |
| 10 | + - Risky principals analysis |
| 11 | + |
| 12 | + Supports light/dark mode toggle and interactive filtering/sorting. |
| 13 | + Requires PSWriteHTML module (Install-Module PSWriteHTML). |
| 14 | + |
| 15 | + .PARAMETER FilePath |
| 16 | + Path where the HTML dashboard will be saved. |
| 17 | + Default: $env:TEMP\Locksmith2-Dashboard.html |
| 18 | + |
| 19 | + .PARAMETER Show |
| 20 | + Opens the dashboard in default browser after generation. |
| 21 | + |
| 22 | + .PARAMETER ExpandGroups |
| 23 | + Expands group principals into individual member issues. |
| 24 | + |
| 25 | + .PARAMETER Online |
| 26 | + Uses online CDN resources instead of embedding CSS/JS. |
| 27 | + Results in smaller file size but requires internet connection to view. |
| 28 | + |
| 29 | + .INPUTS |
| 30 | + None. This function does not accept pipeline input. |
| 31 | + |
| 32 | + .OUTPUTS |
| 33 | + None. Generates an HTML file at the specified path. |
| 34 | + |
| 35 | + .EXAMPLE |
| 36 | + Invoke-Locksmith2 |
| 37 | + New-LS2Dashboard -FilePath C:\Reports\locksmith-dashboard.html -Show |
| 38 | + |
| 39 | + Runs a scan and generates an interactive dashboard. |
| 40 | + |
| 41 | + .EXAMPLE |
| 42 | + New-LS2Dashboard -ExpandGroups -Show |
| 43 | + |
| 44 | + Generates dashboard with group memberships expanded to individual principals. |
| 45 | + |
| 46 | + .EXAMPLE |
| 47 | + New-LS2Dashboard -FilePath C:\Reports\report.html -Online |
| 48 | + |
| 49 | + Generates a smaller dashboard file using online CDN resources. |
| 50 | + |
| 51 | + .NOTES |
| 52 | + Author: Jake Hildreth (@jakehildreth) |
| 53 | + Requires: PSWriteHTML module (https://github.com/EvotecIT/PSWriteHTML) |
| 54 | + Requires: PowerShell 5.1 or later |
| 55 | + |
| 56 | + The dashboard reads from the current IssueStore. Run Invoke-Locksmith2 or |
| 57 | + Find-LS2Vulnerable* functions first to populate the store with scan data. |
| 58 | + |
| 59 | + .LINK |
| 60 | + Invoke-Locksmith2 |
| 61 | + |
| 62 | + .LINK |
| 63 | + Find-LS2RiskyPrincipal |
| 64 | + |
| 65 | + .LINK |
| 66 | + Get-FlattenedIssues |
| 67 | + #> |
| 68 | + [CmdletBinding()] |
| 69 | + param( |
| 70 | + [Parameter()] |
| 71 | + [string]$FilePath = "$env:TEMP\Locksmith2-Dashboard.html", |
| 72 | + |
| 73 | + [Parameter()] |
| 74 | + [switch]$Show, |
| 75 | + |
| 76 | + [Parameter()] |
| 77 | + [switch]$ExpandGroups, |
| 78 | + |
| 79 | + [Parameter()] |
| 80 | + [switch]$Online |
| 81 | + ) |
| 82 | + |
| 83 | + #requires -Version 5.1 |
| 84 | + |
| 85 | + # Check for PSWriteHTML module |
| 86 | + if (-not (Get-Module -ListAvailable -Name PSWriteHTML)) { |
| 87 | + Write-Error "PSWriteHTML module is required. Install with: Install-Module PSWriteHTML" |
| 88 | + return |
| 89 | + } |
| 90 | + |
| 91 | + Import-Module PSWriteHTML -ErrorAction Stop |
| 92 | + |
| 93 | + # Check if IssueStore is populated |
| 94 | + if (-not $script:IssueStore -or $script:IssueStore.Count -eq 0) { |
| 95 | + Write-Warning "IssueStore is empty. Run Invoke-Locksmith2 or Find-LS2Vulnerable* functions first." |
| 96 | + Write-Warning "Generating empty dashboard..." |
| 97 | + } |
| 98 | + |
| 99 | + # Get all issues from IssueStore |
| 100 | + $allIssues = Get-FlattenedIssues |
| 101 | + |
| 102 | + # Expand groups if requested |
| 103 | + if ($ExpandGroups) { |
| 104 | + Write-Verbose "Expanding group memberships for dashboard..." |
| 105 | + $allIssues = $allIssues | ForEach-Object { Expand-IssueByGroup $_ } |
| 106 | + } |
| 107 | + |
| 108 | + # Filter issues by category |
| 109 | + $templateTechniques = @('ESC1', 'ESC2', 'ESC3c1', 'ESC3c2', 'ESC4a', 'ESC4o', 'ESC9') |
| 110 | + $caTechniques = @('ESC6', 'ESC7a', 'ESC7m', 'ESC11', 'ESC16') |
| 111 | + $objectTechniques = @('ESC5a', 'ESC5o') |
| 112 | + |
| 113 | + $templateIssues = $allIssues | Where-Object { $_.Technique -in $templateTechniques } |
| 114 | + $caIssues = $allIssues | Where-Object { $_.Technique -in $caTechniques } |
| 115 | + $objectIssues = $allIssues | Where-Object { $_.Technique -in $objectTechniques } |
| 116 | + |
| 117 | + # Get risky principals |
| 118 | + Write-Verbose "Calculating principal risk scores..." |
| 119 | + $riskyPrincipals = Find-LS2RiskyPrincipal |
| 120 | + |
| 121 | + # Prepare data for tables - select key properties for display |
| 122 | + $allIssuesTable = $allIssues | Select-Object ` |
| 123 | + Technique, |
| 124 | + Forest, |
| 125 | + Name, |
| 126 | + IdentityReference, |
| 127 | + ActiveDirectoryRights, |
| 128 | + @{N='Enabled';E={if($null -ne $_.Enabled){$_.Enabled}else{'-'}}}, |
| 129 | + @{N='CAFullName';E={if($_.CAFullName){$_.CAFullName}else{'-'}}}, |
| 130 | + @{N='Owner';E={if($_.Owner){$_.Owner}else{'-'}}}, |
| 131 | + @{N='Members';E={if($_.MemberCount){$_.MemberCount}else{'-'}}} |
| 132 | + |
| 133 | + $templateIssuesTable = $templateIssues | Select-Object ` |
| 134 | + Technique, Name, IdentityReference, ActiveDirectoryRights, Enabled, MemberCount |
| 135 | + |
| 136 | + $caIssuesTable = $caIssues | Select-Object ` |
| 137 | + Technique, Name, CAFullName, IdentityReference, ActiveDirectoryRights, MemberCount |
| 138 | + |
| 139 | + $objectIssuesTable = $objectIssues | Select-Object ` |
| 140 | + Technique, Name, Owner, IdentityReference, ActiveDirectoryRights, MemberCount |
| 141 | + |
| 142 | + $principalsTable = $riskyPrincipals | Select-Object ` |
| 143 | + Principal, |
| 144 | + IssueCount, |
| 145 | + @{N='Techniques';E={$_.Techniques -join ', '}}, |
| 146 | + @{N='VulnerableObjects';E={$_.VulnerableObjects.Count}} |
| 147 | + |
| 148 | + $forestName = if ($script:Forest) { $script:Forest } else { 'Unknown Forest' } |
| 149 | + |
| 150 | + # Generate HTML Dashboard |
| 151 | + New-HTML -TitleText "Locksmith2 Security Dashboard - $forestName" -Online:$Online -FilePath $FilePath -Show:$Show { |
| 152 | + |
| 153 | + # Use tabs for single-page navigation with content switching |
| 154 | + New-HTMLTabStyle -SlimTabs -Transition -SelectorColor Magenta |
| 155 | + |
| 156 | + New-HTMLTab -Name 'All Issues' -IconSolid exclamation-triangle -IconColor Red { |
| 157 | + New-HTMLSection -Invisible { |
| 158 | + New-HTMLPanel -Width 10% { |
| 159 | + New-HTMLText -Text "All Issues - Expanded Principals ($($allIssues.Count) total)" -FontSize 20 -FontWeight bold |
| 160 | + New-HTMLText -Text @" |
| 161 | +This view shows all discovered AD CS vulnerabilities with group memberships expanded to individual principals. |
| 162 | +Issues are marked with the ESC technique and show which principals can exploit each configuration. |
| 163 | +"@ -Color '#888' -FontSize 14 |
| 164 | + } |
| 165 | + New-HTMLPanel { |
| 166 | + New-HTMLTable -DataTable $allIssuesTable ` |
| 167 | + -Filtering ` |
| 168 | + -PagingLength 25 ` |
| 169 | + -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'searchBuilder', 'searchPanes') ` |
| 170 | + -Title 'All AD CS Security Issues' { |
| 171 | + New-TableButtonSearchBuilder -ButtonName 'Search Builder' |
| 172 | + New-HTMLTableCondition -Name 'Technique' -ComparisonType string -Operator like -Value 'ESC1*' -BackgroundColor '#ffebee' -Color Black |
| 173 | + New-HTMLTableCondition -Name 'Technique' -ComparisonType string -Operator like -Value 'ESC7*' -BackgroundColor '#fff3e0' -Color Black |
| 174 | + New-HTMLTableCondition -Name 'Members' -ComparisonType string -Operator ne -Value '-' -BackgroundColor '#e8f5e9' -Color Black |
| 175 | + } |
| 176 | + } |
| 177 | + } |
| 178 | + } |
| 179 | + |
| 180 | + New-HTMLTab -Name 'Templates' -IconSolid file-contract -IconColor Orange { |
| 181 | + New-HTMLSection -Invisible { |
| 182 | + New-HTMLPanel -Width 10% { |
| 183 | + New-HTMLText -Text "Certificate Template Issues ($($templateIssues.Count) issues)" -FontSize 20 -FontWeight bold |
| 184 | + New-HTMLText -Text @" |
| 185 | +Certificate templates are the most common source of AD CS vulnerabilities. These issues allow principals to request |
| 186 | +certificates with dangerous permissions, subject alternative names, or enrollment agent capabilities. |
| 187 | +"@ -Color '#888' -FontSize 14 |
| 188 | + } |
| 189 | + New-HTMLPanel { |
| 190 | + New-HTMLTable -DataTable $templateIssuesTable ` |
| 191 | + -Filtering ` |
| 192 | + -PagingLength 25 ` |
| 193 | + -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5') ` |
| 194 | + -Title 'Template Vulnerabilities' { |
| 195 | + New-HTMLTableCondition -Name 'Technique' -Value 'ESC1' -BackgroundColor '#ffcdd2' -Color Black |
| 196 | + New-HTMLTableCondition -Name 'Enabled' -Value $true -BackgroundColor '#fff9c4' -Color Black |
| 197 | + } |
| 198 | + } |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + New-HTMLTab -Name 'CAs' -IconSolid certificate -IconColor Yellow { |
| 203 | + New-HTMLSection -Invisible { |
| 204 | + New-HTMLPanel -Width 10% { |
| 205 | + New-HTMLText -Text "Certification Authority Issues ($($caIssues.Count) issues)" -FontSize 20 -FontWeight bold |
| 206 | + New-HTMLText -Text @" |
| 207 | +CA-level issues involve dangerous role assignments (ESC7) or insecure CA configurations (ESC6, ESC11, ESC16). |
| 208 | +These vulnerabilities grant principals excessive control over certificate issuance. |
| 209 | +"@ -Color '#888' -FontSize 14 |
| 210 | + } |
| 211 | + New-HTMLPanel { |
| 212 | + New-HTMLTable -DataTable $caIssuesTable ` |
| 213 | + -Filtering ` |
| 214 | + -PagingLength 25 ` |
| 215 | + -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5') ` |
| 216 | + -Title 'CA Configuration Issues' { |
| 217 | + New-HTMLTableCondition -Name 'Technique' -Value 'ESC7a' -BackgroundColor '#ffccbc' -Color Black |
| 218 | + New-HTMLTableCondition -Name 'Technique' -Value 'ESC7m' -BackgroundColor '#ffe0b2' -Color Black |
| 219 | + } |
| 220 | + } |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + New-HTMLTab -Name 'Objects' -IconSolid folder -IconColor Blue { |
| 225 | + New-HTMLSection -Invisible { |
| 226 | + New-HTMLPanel -Width 10% { |
| 227 | + New-HTMLText -Text "PKI Object Issues ($($objectIssues.Count) issues)" -FontSize 20 -FontWeight bold |
| 228 | + New-HTMLText -Text @" |
| 229 | +ESC5 vulnerabilities involve dangerous ownership or write permissions on PKI infrastructure objects. |
| 230 | +These allow principals to modify templates, CAs, or other critical AD CS components. |
| 231 | +"@ -Color '#888' -FontSize 14 |
| 232 | + } |
| 233 | + New-HTMLPanel { |
| 234 | + New-HTMLTable -DataTable $objectIssuesTable ` |
| 235 | + -Filtering ` |
| 236 | + -PagingLength 25 ` |
| 237 | + -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5') ` |
| 238 | + -Title 'Infrastructure Object Issues' { |
| 239 | + New-HTMLTableCondition -Name 'Technique' -Value 'ESC5a' -BackgroundColor '#c5e1a5' -Color Black |
| 240 | + New-HTMLTableCondition -Name 'Technique' -Value 'ESC5o' -BackgroundColor '#fff59d' -Color Black |
| 241 | + } |
| 242 | + } |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + New-HTMLTab -Name 'Risky Principals' -IconSolid user-shield -IconColor Purple { |
| 247 | + New-HTMLSection -Invisible { |
| 248 | + New-HTMLPanel -Width 10% { |
| 249 | + New-HTMLText -Text "Principal Risk Analysis ($($principalsTable.Count) principals)" -FontSize 20 -FontWeight bold |
| 250 | + New-HTMLText -Text @" |
| 251 | +This analysis shows which principals have access to the most AD CS vulnerabilities. Principals with high issue counts |
| 252 | +represent concentrated risk and should be prioritized for remediation or monitoring. |
| 253 | +"@ -Color '#888' -FontSize 14 |
| 254 | + } |
| 255 | + New-HTMLPanel { |
| 256 | + New-HTMLTable -DataTable $principalsTable ` |
| 257 | + -Filtering ` |
| 258 | + -PagingLength 25 ` |
| 259 | + -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5') ` |
| 260 | + -Title 'Principals by Risk Score' ` |
| 261 | + -DefaultSortColumn 'IssueCount' ` |
| 262 | + -DefaultSortOrder Descending { |
| 263 | + New-HTMLTableCondition -Name 'IssueCount' -ComparisonType number -Operator gt -Value 10 -BackgroundColor '#ef5350' -Color White |
| 264 | + New-HTMLTableCondition -Name 'IssueCount' -ComparisonType number -Operator ge -Value 5 -BackgroundColor '#ff9800' -Color White |
| 265 | + New-HTMLTableCondition -Name 'IssueCount' -ComparisonType number -Operator ge -Value 1 -BackgroundColor '#fdd835' -Color Black |
| 266 | + } |
| 267 | + } |
| 268 | + } |
| 269 | + } |
| 270 | + } |
| 271 | + |
| 272 | + Write-Verbose "Dashboard generated: $FilePath" |
| 273 | + if (-not $Show) { |
| 274 | + Write-Host "Dashboard saved to: $FilePath" |
| 275 | + } |
| 276 | +} |
0 commit comments