Private/AD/Checks/Invoke-ADAttackPathChecks.ps1
|
# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0 # https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/ # AI/LLM use: see AI-USAGE.md for required attribution function Invoke-ADAttackPathChecks { [CmdletBinding()] param( [Parameter(Mandatory)] [hashtable]$AuditData ) $checkDefs = Get-AuditCategoryDefinitions -Category 'ADAttackPathChecks' $findings = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($check in $checkDefs.checks) { $funcName = "Test-Recon$($check.id -replace '-', '')" if (Get-Command $funcName -ErrorAction SilentlyContinue) { try { $finding = & $funcName -AuditData $AuditData -CheckDefinition $check if ($finding) { $findings.Add($finding) } } catch { $findings.Add((New-AuditFinding -CheckDefinition $check -Status 'ERROR' ` -CurrentValue "Check failed: $_")) } } else { $findings.Add((New-AuditFinding -CheckDefinition $check -Status 'SKIP' ` -CurrentValue 'Check not yet implemented')) } } return @($findings) } # ── ADPATH-001: Escalation Paths to Tier-0 ───────────────────────────────── function Test-ReconADPATH001 { [CmdletBinding()] param( [Parameter(Mandatory)][hashtable]$AuditData, [Parameter(Mandatory)][hashtable]$CheckDefinition ) $analysis = Get-ADAttackPath -AuditData $AuditData if (-not $analysis.DataAvailable) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'ACL data not available — run with the ACLDelegation (or All) category enabled to compute attack paths' } $paths = @($analysis.Paths) # Expected paths are by-design Tier-0 service accounts (Azure AD Connect MSOL_*), # already tracked by ADTIER-001 — they don't constitute a finding on their own. $genuine = @($paths | Where-Object { -not $_.Expected }) $expected = @($paths | Where-Object { $_.Expected }) if ($genuine.Count -eq 0) { $cv = 'No non-default principals have control over Tier-0 objects — no one-hop escalation paths found' if ($expected.Count -gt 0) { $cv += " ($($expected.Count) expected service-account path(s) — e.g. Azure AD Connect — are tracked by ADTIER-001)" } return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'PASS' ` -CurrentValue $cv ` -Details @{ PathCount = 0; ExpectedCount = $expected.Count; Paths = @($paths) } } $nonPriv = @($genuine | Where-Object { -not $_.SourceIsPrivileged }) $preview = @($genuine | Select-Object -First 6 | ForEach-Object { $_.Path }) -join ' | ' $currentValue = "$($genuine.Count) escalation path(s) to Tier-0" if ($nonPriv.Count -gt 0) { $currentValue += " — $($nonPriv.Count) from NON-privileged principals (highest risk)" } if ($expected.Count -gt 0) { $currentValue += "; plus $($expected.Count) expected service-account path(s) (see ADTIER-001)" } $currentValue += ": $preview" return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'FAIL' ` -CurrentValue $currentValue ` -Details @{ PathCount = $genuine.Count NonPrivilegedCount = $nonPriv.Count ExpectedCount = $expected.Count AffectedLabel = 'Escalation paths to Tier-0' AffectedItems = @($genuine | ForEach-Object { $_.Path }) Paths = @($paths) } } |