noid-privacy/Modules/ASR/Public/Invoke-ASRRules.ps1

571 lines
29 KiB
PowerShell

<#
.SYNOPSIS
Apply all 19 Microsoft Defender ASR rules
.DESCRIPTION
Enables all 19 Attack Surface Reduction rules in Block mode for comprehensive protection.
Rules Applied:
- All 19 ASR rules in Block mode (Action = 1)
- Includes 4 rules missing from Security Baseline
- Upgrades 1 rule from Audit to Block (PSExec/WMI - with SCCM check)
Features:
- SCCM/Configuration Manager detection (PSExec/WMI rule warning)
- Cloud protection verification
- BACKUP/APPLY/VERIFY/RESTORE pattern
- DryRun mode for testing
- Security Baseline overlap detection
.PARAMETER DryRun
Preview changes without applying them
.PARAMETER SkipBackup
Skip backup creation (not recommended)
.PARAMETER SkipVerify
Skip post-application verification
.PARAMETER Force
Apply even if validation warnings occur (SCCM, Cloud Protection)
.PARAMETER AllowPSExecWMI
Force enable PSExec/WMI rule even if SCCM detected (use with caution)
.EXAMPLE
Invoke-ASRRules
Apply all 19 ASR rules with full backup and verification
.EXAMPLE
Invoke-ASRRules -DryRun
Preview what changes would be made
.EXAMPLE
Invoke-ASRRules -AllowPSExecWMI -Force
Force enable PSExec/WMI rule despite SCCM detection
.OUTPUTS
PSCustomObject with results including success status, rules applied, and any errors
#>
function Invoke-ASRRules {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun,
[Parameter(Mandatory = $false)]
[switch]$SkipBackup,
[Parameter(Mandatory = $false)]
[switch]$SkipVerify,
[Parameter(Mandatory = $false)]
[switch]$Force,
[Parameter(Mandatory = $false)]
[switch]$AllowPSExecWMI
)
begin {
$moduleName = "ASR"
$startTime = Get-Date
# Ensure core functions are available when the module is imported directly (outside Framework.ps1)
if (-not (Get-Command Initialize-BackupSystem -ErrorAction SilentlyContinue)) {
try {
$frameworkRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
$coreFiles = @(
"Core\Logger.ps1",
"Core\Config.ps1",
"Core\Validator.ps1",
"Core\Rollback.ps1",
"Utils\Compatibility.ps1"
)
foreach ($file in $coreFiles) {
$corePath = Join-Path $frameworkRoot $file
if (Test-Path $corePath) {
. $corePath
}
}
}
catch {
Write-Host "ERROR: Failed to load core dependencies for ASR module: $_" -ForegroundColor Red
}
}
# Initialize result object
$result = [PSCustomObject]@{
ModuleName = $moduleName
Success = $false
RulesApplied = 0
Errors = @()
Warnings = @()
BackupCreated = $false
VerificationPassed = $false
ConfigMgrDetected = $false
CloudProtectionEnabled = $false
Duration = $null
Details = @{
TotalRules = 19
BlockMode = 0
AuditMode = 0
DisabledMode = 0
}
}
Write-Log -Level INFO -Message "Starting ASR rules application (all 19 rules)" -Module $moduleName
if ($DryRun) {
Write-Log -Level INFO -Message "DRY RUN MODE - No changes will be applied" -Module $moduleName
}
}
process {
try {
# Step 1: Prerequisites validation
Write-Log -Level INFO -Message "Validating prerequisites..." -Module $moduleName
if (-not (Test-IsAdmin)) {
throw "Administrator privileges required"
}
if (-not (Test-WindowsVersion -MinimumBuild 22000)) {
throw "Windows 11 or later required"
}
# Check Windows Defender status and third-party AV
$defenderService = Get-Service -Name "WinDefend" -ErrorAction SilentlyContinue
$defenderRunning = $defenderService -and $defenderService.Status -eq "Running"
# Check for third-party antivirus (they disable Defender)
$thirdPartyAV = $null
try {
$avProducts = Get-CimInstance -Namespace "root/SecurityCenter2" -ClassName "AntiVirusProduct" -ErrorAction SilentlyContinue
$thirdPartyAV = $avProducts | Where-Object { $_.displayName -notmatch "Windows Defender|Microsoft Defender" } | Select-Object -First 1
}
catch {
# SecurityCenter2 not available - continue with Defender check only
$null = $null
}
if (-not $defenderRunning) {
if ($thirdPartyAV) {
# Third-party AV detected - skip ASR gracefully (not an error!)
$avName = $thirdPartyAV.displayName
Write-Host ""
Write-Host "========================================" -ForegroundColor Yellow
Write-Host " ASR Module Skipped" -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "Third-party antivirus detected: $avName" -ForegroundColor Cyan
Write-Host ""
Write-Host "ASR rules require Windows Defender to be active." -ForegroundColor Yellow
Write-Host "Your antivirus ($avName) has its own protection features." -ForegroundColor Yellow
Write-Host ""
Write-Host "This is NOT an error - ASR will be skipped." -ForegroundColor Green
Write-Host ""
Write-Log -Level WARNING -Message "ASR skipped: Third-party AV detected ($avName). Defender disabled." -Module $moduleName
$result.Success = $true # Not an error - intentional skip
$result.Warnings += "ASR skipped: Third-party antivirus detected ($avName). Your AV provides similar protection."
$result.RulesApplied = 0
return $result
}
else {
# No third-party AV but Defender not running - this IS a problem
throw "Windows Defender service is not running and no third-party antivirus detected. ASR rules require Defender to be active."
}
}
# Load ASR rule definitions
Write-Log -Level INFO -Message "Loading ASR rule definitions..." -Module $moduleName
$asrRules = Get-ASRRuleDefinitions
# Step 2: Check for Remote Management Tools (SCCM/Intune/etc.)
Write-Log -Level INFO -Message "Checking for remote management tools..." -Module $moduleName
# Automatic detection
$configMgrDetected = Test-ConfigMgrPresence
$result.ConfigMgrDetected = $configMgrDetected
# Check for management tools - NonInteractive or Interactive
$usesManagementTools = $false
if (Test-NonInteractiveMode) {
# NonInteractive mode (GUI) - use config value
$usesManagementTools = Get-NonInteractiveValue -Module "ASR" -Key "usesManagementTools" -Default $false
Write-NonInteractiveDecision -Module $moduleName -Decision "Management tools setting" -Value $(if ($usesManagementTools) { "Yes (1 AUDIT)" } else { "No (ALL BLOCK)" })
}
elseif (-not $Force -and -not $AllowPSExecWMI -and -not $DryRun) {
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Remote Management Tool Check" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
if ($configMgrDetected) {
Write-Host "DETECTED: SCCM/Configuration Manager is currently installed" -ForegroundColor Yellow
Write-Host ""
}
Write-Host "Do you use ANY of these remote management tools?" -ForegroundColor White
Write-Host ""
Write-Host " - Microsoft SCCM (Configuration Manager)" -ForegroundColor Gray
Write-Host " - Microsoft Intune / Endpoint Manager" -ForegroundColor Gray
Write-Host " - PDQ Deploy / PDQ Inventory" -ForegroundColor Gray
Write-Host " - ManageEngine Desktop Central" -ForegroundColor Gray
Write-Host " - Any other WMI/PSExec based management tools" -ForegroundColor Gray
Write-Host ""
Write-Host "These tools use PSExec and WMI for remote management." -ForegroundColor Yellow
Write-Host "If you use them, one ASR rule must be set to AUDIT mode." -ForegroundColor Yellow
Write-Host ""
Write-Host "Options:" -ForegroundColor Cyan
Write-Host " [Y] Yes - I use management tools" -ForegroundColor Yellow
Write-Host " > 1 rule: AUDIT mode (PSExec/WMI only)" -ForegroundColor Gray
Write-Host " > 18 rules: BLOCK mode (full protection)" -ForegroundColor Gray
Write-Host ""
Write-Host " [N] No - I don't use any of these" -ForegroundColor Green
Write-Host " > ALL 19 rules: BLOCK mode (maximum protection)" -ForegroundColor Gray
Write-Host ""
do {
$choice = Read-Host "Select option [Y/N] (default: N)"
if ([string]::IsNullOrWhiteSpace($choice)) { $choice = "N" }
$choice = $choice.ToUpper()
if ($choice -notin @('Y', 'N')) {
Write-Host ""
Write-Host "Invalid input. Please enter Y or N." -ForegroundColor Red
Write-Host ""
}
} while ($choice -notin @('Y', 'N'))
switch ($choice) {
"Y" {
$usesManagementTools = $true
Write-Host ""
Write-Host "1 rule set to AUDIT (PSExec/WMI), 18 rules set to BLOCK" -ForegroundColor Yellow
Write-Log -Level INFO -Message "User confirmed use of management tools - 1 AUDIT + 18 BLOCK" -Module $moduleName
}
"N" {
$usesManagementTools = $false
Write-Host ""
Write-Host "ALL 19 rules will be set to BLOCK mode" -ForegroundColor Green
Write-Log -Level INFO -Message "User confirmed no management tools - ALL 19 BLOCK" -Module $moduleName
}
}
Write-Host ""
}
elseif ($Force -and -not $AllowPSExecWMI) {
# Force flag: Auto-detect or assume safe
$usesManagementTools = $configMgrDetected
Write-Log -Level INFO -Message "Force flag: Using detection result (ConfigMgr: $configMgrDetected)" -Module $moduleName
}
# Apply PSExec/WMI rule mode based on user choice or detection
if (($usesManagementTools -or $configMgrDetected) -and -not $AllowPSExecWMI) {
$psexecRule = $asrRules | Where-Object { $_.GUID -eq "d1e49aac-8f56-4280-b9ba-993a6d77406c" }
# Set PSExec/WMI to Audit mode (user confirmed or detected)
$psexecRule.Action = 2
$result.Warnings += "Management tools detected/confirmed: PSExec/WMI rule set to Audit mode"
Write-Log -Level INFO -Message "PSExec/WMI rule set to Audit mode (management tools in use)" -Module $moduleName
}
# Step 2b: Prevalence rule (new/unknown software) - NonInteractive or Interactive
if (-not $DryRun) {
$prevalenceRule = $asrRules | Where-Object { $_.GUID -eq "01443614-cd74-433a-b99e-2ecdc07bfc25" }
if ($prevalenceRule) {
$allowNewSoftware = $false
if (Test-NonInteractiveMode) {
# NonInteractive mode (GUI) - use config value
$allowNewSoftware = Get-NonInteractiveValue -Module "ASR" -Key "allowNewSoftware" -Default $false
if ($allowNewSoftware) {
$prevalenceRule.Action = 2
$result.Warnings += "ASR prevalence rule set to AUDIT (less restrictive; see README for details)."
} else {
$prevalenceRule.Action = 1
}
Write-NonInteractiveDecision -Module $moduleName -Decision "New/Unknown software rule" -Value $(if ($allowNewSoftware) { "AUDIT (allow)" } else { "BLOCK (secure)" })
}
else {
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " ASR Rule: New / Unknown Software" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Rule: Block executable files unless they meet prevalence, age, or trusted list" -ForegroundColor White
Write-Host "GUID: $($prevalenceRule.GUID)" -ForegroundColor DarkGray
Write-Host ""
Write-Host "This rule blocks very new or unknown executables that" -ForegroundColor Yellow
Write-Host "are not yet trusted by Microsoft's reputation systems." -ForegroundColor Yellow
Write-Host ""
Write-Host "Do you install NEW software frequently?" -ForegroundColor White
Write-Host ""
Write-Host " - Games from independent developers" -ForegroundColor Gray
Write-Host " - Beta software / Early access programs" -ForegroundColor Gray
Write-Host " - Custom/in-house business applications" -ForegroundColor Gray
Write-Host " - Open-source tools without Microsoft reputation" -ForegroundColor Gray
Write-Host ""
Write-Host "Options:" -ForegroundColor Cyan
Write-Host " [Y] Yes - I regularly install new software" -ForegroundColor Yellow
Write-Host " > AUDIT mode: Events logged, installs allowed" -ForegroundColor Gray
Write-Host " > Recommended if you install software from various sources" -ForegroundColor Gray
Write-Host ""
Write-Host " [N] No - I rarely install new software" -ForegroundColor Green
Write-Host " > BLOCK mode: Maximum security" -ForegroundColor Gray
Write-Host " > New/unknown installers may be blocked" -ForegroundColor Gray
Write-Host ""
do {
$prevalenceChoice = Read-Host "Select option [Y/N] (default: N)"
if ([string]::IsNullOrWhiteSpace($prevalenceChoice)) { $prevalenceChoice = "N" }
$prevalenceChoice = $prevalenceChoice.ToUpper()
if ($prevalenceChoice -notin @('Y', 'N')) {
Write-Host ""
Write-Host "Invalid input. Please enter Y or N." -ForegroundColor Red
Write-Host ""
}
} while ($prevalenceChoice -notin @('Y', 'N'))
switch ($prevalenceChoice) {
"N" {
$prevalenceRule.Action = 1
Write-Host ""
Write-Host "New/Unknown Software rule set to BLOCK mode (maximum security)" -ForegroundColor Green
Write-Log -Level INFO -Message "Prevalence rule configured to BLOCK (recommended)" -Module $moduleName
}
"Y" {
$prevalenceRule.Action = 2
Write-Host ""
Write-Host "New/Unknown Software rule set to AUDIT mode (developer/test)" -ForegroundColor Yellow
$result.Warnings += "ASR prevalence rule set to AUDIT (less restrictive; see README for details)."
Write-Log -Level INFO -Message "Prevalence rule configured to AUDIT (developer/compat mode)" -Module $moduleName
}
}
Write-Host ""
}
}
}
# Step 3: Check cloud protection
Write-Log -Level INFO -Message "Checking cloud-delivered protection..." -Module $moduleName
$cloudProtectionEnabled = Test-CloudProtection
$result.CloudProtectionEnabled = $cloudProtectionEnabled
if (-not $cloudProtectionEnabled) {
$cloudRules = $asrRules | Where-Object { $_.RequiresCloudProtection -eq $true }
$result.Warnings += "Cloud protection disabled: $($cloudRules.Count) rules require it for optimal operation"
Write-Log -Level WARNING -Message "$($cloudRules.Count) ASR rules require cloud protection for full functionality" -Module $moduleName
if (Test-NonInteractiveMode) {
# NonInteractive mode (GUI) - use config value
$continueWithoutCloud = Get-NonInteractiveValue -Module "ASR" -Key "continueWithoutCloud" -Default $true
if (-not $continueWithoutCloud) {
Write-NonInteractiveDecision -Module $moduleName -Decision "Cloud protection required - aborting"
throw "ASR application cancelled (cloud protection required, continueWithoutCloud=false)"
}
Write-NonInteractiveDecision -Module $moduleName -Decision "Continuing without cloud protection (limited functionality)"
}
elseif (-not $Force -and -not $DryRun) {
# Interactive prompt for cloud protection
Write-Host ""
Write-Host "========================================" -ForegroundColor Yellow
Write-Host " Cloud Protection Not Enabled!" -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "$($cloudRules.Count) ASR rules require cloud-delivered protection for optimal functionality:" -ForegroundColor Yellow
Write-Host ""
foreach ($cloudRule in $cloudRules) {
Write-Host " - $($cloudRule.Name)" -ForegroundColor Gray
}
Write-Host ""
Write-Host "These rules will work in limited capacity without cloud protection." -ForegroundColor Yellow
Write-Host ""
Write-Host "Options:" -ForegroundColor Cyan
Write-Host " [C] Continue - Apply rules anyway (limited functionality)" -ForegroundColor Green
Write-Host " [A] Abort - Cancel ASR rule application" -ForegroundColor Yellow
Write-Host ""
do {
$choice = Read-Host "Select option [C/A] (default: A)"
if ([string]::IsNullOrWhiteSpace($choice)) { $choice = "A" }
$choice = $choice.ToUpper()
if ($choice -notin @('C', 'A')) {
Write-Host ""
Write-Host "Invalid input. Please enter C or A." -ForegroundColor Red
Write-Host ""
}
} while ($choice -notin @('C', 'A'))
switch ($choice) {
"C" {
Write-Host ""
Write-Host "Continuing with cloud protection disabled" -ForegroundColor Yellow
Write-Log -Level INFO -Message "User chose to continue despite cloud protection disabled" -Module $moduleName
}
"A" {
Write-Host ""
Write-Host "ASR rule application cancelled by user" -ForegroundColor Yellow
Write-Log -Level INFO -Message "ASR application cancelled due to cloud protection requirement" -Module $moduleName
throw "ASR application cancelled by user due to cloud protection requirement"
}
}
Write-Host ""
}
elseif ($Force) {
# Force flag - continue silently
Write-Log -Level INFO -Message "Continuing despite cloud protection disabled (Force flag)" -Module $moduleName
}
}
# Step 3a: Initialize and start module backup
if (-not $SkipBackup -and -not $DryRun) {
try {
Initialize-BackupSystem
$null = Start-ModuleBackup -ModuleName $moduleName
Write-Log -Level INFO -Message "Session backup initialized" -Module $moduleName
}
catch {
$result.Warnings += "Failed to initialize/start module backup: $_"
Write-Log -Level WARNING -Message "Failed to initialize/start module backup: $_" -Module $moduleName
}
}
# Step 4: Create backup
if (-not $SkipBackup -and -not $DryRun) {
Write-Log -Level INFO -Message "Creating backup..." -Module $moduleName
$backupResult = Backup-ASRRegistry
if ($backupResult.Errors.Count -gt 0) {
foreach ($err in $backupResult.Errors) {
$result.Warnings += $err
}
}
else {
# Register backup in session manifest
Complete-ModuleBackup -ItemsBackedUp 1 -Status "Success"
$result.BackupCreated = $true
}
}
# Step 5: Apply ASR rules via PowerShell
# Note: Set-ASRViaPowerShell logs internally, no need to log here
$applyResult = Set-ASRViaPowerShell -Rules $asrRules -DryRun:$DryRun
# In DryRun mode, no rules are actually applied, so keep RulesApplied = 0
if (-not $DryRun) {
$result.RulesApplied = $applyResult.Applied
}
# Add errors and warnings individually to avoid nested arrays
foreach ($err in $applyResult.Errors) {
$result.Errors += $err
}
foreach ($warn in $applyResult.Warnings) {
$result.Warnings += $warn
}
# Count rule modes from actual system state
$mpPref = Get-MpPreference
$currentActions = $mpPref.AttackSurfaceReductionRules_Actions
if ($currentActions) {
$result.Details.BlockMode = @($currentActions | Where-Object { $_ -eq 1 }).Count
$result.Details.AuditMode = @($currentActions | Where-Object { $_ -eq 2 }).Count
$result.Details.DisabledMode = @($currentActions | Where-Object { $_ -eq 0 }).Count
} else {
# Fallback to array count
$result.Details.BlockMode = @($asrRules | Where-Object { $_.Action -eq 1 }).Count
$result.Details.AuditMode = @($asrRules | Where-Object { $_.Action -eq 2 }).Count
$result.Details.DisabledMode = @($asrRules | Where-Object { $_.Action -eq 0 }).Count
}
# Step 6: Verification
if (-not $SkipVerify -and -not $DryRun) {
Write-Log -Level INFO -Message "Verifying applied ASR rules..." -Module $moduleName
$verificationResult = Test-ASRCompliance -ExpectedRules $asrRules
$result.VerificationPassed = $verificationResult.Passed
if (-not $verificationResult.Passed) {
$result.Warnings += "Verification found $($verificationResult.FailedCount) rules not applied correctly"
Write-Log -Level WARNING -Message "Verification found $($verificationResult.FailedCount) failed rules" -Module $moduleName
}
else {
Write-Log -Level INFO -Message "Verification passed - all $($verificationResult.CheckedCount) rules confirmed" -Module $moduleName
}
}
# Log baseline overlap
$baselineRules = $asrRules | Where-Object { $_.BaselineStatus -in @("Block", "Audit") }
Write-Log -Level INFO -Message "Security Baseline overlap: $($baselineRules.Count) rules already in baseline" -Module $moduleName
$newRules = $asrRules | Where-Object { $_.BaselineStatus -eq "Missing" }
if ($newRules.Count -gt 0) {
Write-Log -Level INFO -Message "Added $($newRules.Count) rules not in Security Baseline:" -Module $moduleName
foreach ($newRule in $newRules) {
Write-Log -Level INFO -Message " + $($newRule.Name)" -Module $moduleName
}
}
$upgradedRules = $asrRules | Where-Object { $_.BaselineStatus -eq "Audit" -and $_.Action -eq 1 }
if ($upgradedRules.Count -gt 0) {
Write-Log -Level INFO -Message "Upgraded $($upgradedRules.Count) rules from Audit to Block:" -Module $moduleName
foreach ($upgradedRule in $upgradedRules) {
Write-Log -Level INFO -Message " [UPGRADE] $($upgradedRule.Name)" -Module $moduleName
}
}
# Mark as successful if no critical errors
if ($result.Errors.Count -eq 0) {
$result.Success = $true
Write-Log -Level INFO -Message "ASR rules applied successfully" -Module $moduleName
}
else {
Write-Log -Level ERROR -Message "ASR application completed with $($result.Errors.Count) errors" -Module $moduleName
}
}
catch {
$result.Success = $false
$result.Errors += $_.Exception.Message
Write-Log -Level ERROR -Message "ASR application failed: $($_.Exception.Message)" -Module $moduleName
}
}
end {
$result.Duration = (Get-Date) - $startTime
Write-Log -Level INFO -Message "ASR application completed in $($result.Duration.TotalSeconds) seconds" -Module $moduleName
$blockCount = $result.Details.BlockMode
$auditCount = $result.Details.AuditMode
Write-Log -Level INFO -Message "Rules applied: $($result.RulesApplied) ($blockCount Block, $auditCount Audit)" -Module $moduleName
Write-Log -Level INFO -Message "Errors: $($result.Errors.Count), Warnings: $($result.Warnings.Count)" -Module $moduleName
# Log warning details for transparency
if ($result.Warnings.Count -gt 0) {
foreach ($warn in $result.Warnings) {
Write-Log -Level INFO -Message " Warning: $warn" -Module $moduleName
}
}
# GUI parsing marker for settings count (19 ASR rules)
Write-Log -Level SUCCESS -Message "Applied 19 settings" -Module "ASR"
return $result
}
}