noid-privacy/Modules/ASR/Private/Set-ASRViaPowerShell.ps1

132 lines
5.6 KiB
PowerShell
Raw Normal View History

<#
.SYNOPSIS
Apply ASR rules using Set-MpPreference (PowerShell)
.DESCRIPTION
Uses Microsoft's recommended PowerShell cmdlet to apply ASR rules
Cleaner and more validated than direct registry manipulation
.PARAMETER Rules
Array of rule objects to apply
.PARAMETER DryRun
Preview changes without applying
.OUTPUTS
PSCustomObject with applied count and errors
#>
function Set-ASRViaPowerShell {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[Array]$Rules,
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
$result = [PSCustomObject]@{
Applied = 0
Errors = @()
Warnings = @()
}
try {
# Build arrays for Set-MpPreference
$ruleIds = @()
$ruleActions = @()
foreach ($rule in $Rules) {
$ruleIds += $rule.GUID
$ruleActions += $rule.Action
}
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would apply $($ruleIds.Count) ASR rules via Set-MpPreference" -Module "ASR"
$result.Applied = $ruleIds.Count
return $result
}
Write-Log -Level INFO -Message "Applying $($ruleIds.Count) ASR rules via Set-MpPreference..." -Module "ASR"
# Apply all rules at once
Set-MpPreference -AttackSurfaceReductionRules_Ids $ruleIds `
-AttackSurfaceReductionRules_Actions $ruleActions `
-ErrorAction Stop | Out-Null
$result.Applied = $ruleIds.Count
# WORKAROUND: GPO Registry has higher priority than Set-MpPreference
# We must set BOTH Set-MpPreference AND GPO Registry to ensure the rule is actually applied
# This applies to user-configurable rules (PSExec/WMI and Prevalence)
$asrRegistryPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Windows Defender Exploit Guard\ASR\Rules"
# User-configurable rules that need GPO Registry sync
$userConfigurableRules = @(
"d1e49aac-8f56-4280-b9ba-993a6d77406c", # PSExec/WMI (Management Tools)
"01443614-cd74-433a-b99e-2ecdc07bfc25" # Prevalence (New/Unknown Software)
)
foreach ($rule in $Rules) {
# For user-configurable rules: Always sync to GPO Registry (Block OR Audit)
if ($userConfigurableRules -contains $rule.GUID) {
try {
$currentValue = Get-ItemProperty -Path $asrRegistryPath -Name $rule.GUID -ErrorAction SilentlyContinue
$needsUpdate = $false
if ($currentValue) {
# Registry exists - check if value differs
if ([int]$currentValue.($rule.GUID) -ne $rule.Action) {
$needsUpdate = $true
}
} else {
# Registry doesn't exist - need to create it
$needsUpdate = $true
}
if ($needsUpdate) {
# Ensure path exists
if (-not (Test-Path $asrRegistryPath)) {
New-Item -Path $asrRegistryPath -Force | Out-Null
}
# Set the registry value (using string type like Security Baseline does)
Set-ItemProperty -Path $asrRegistryPath -Name $rule.GUID -Value $rule.Action.ToString() -Type String -Force -ErrorAction Stop | Out-Null
$modeName = switch ($rule.Action) { 1 { "Block" } 2 { "Audit" } default { "Unknown" } }
Write-Log -Level INFO -Message "Synced $($rule.Name) to GPO Registry: $modeName mode" -Module "ASR"
}
}
catch {
$result.Warnings += "Could not sync rule $($rule.Name) to GPO registry: $($_.Exception.Message)"
Write-Log -Level WARNING -Message "Could not sync $($rule.Name) to GPO registry: $($_.Exception.Message)" -Module "ASR"
}
}
# For non-configurable rules: Only override if user wants Block and Baseline had Audit
elseif ($rule.Action -eq 1 -and $rule.BaselineStatus -eq "Audit") {
try {
$currentValue = Get-ItemProperty -Path $asrRegistryPath -Name $rule.GUID -ErrorAction SilentlyContinue
if ($currentValue -and [int]$currentValue.($rule.GUID) -ne 1) {
Set-ItemProperty -Path $asrRegistryPath -Name $rule.GUID -Value "1" -Type String -Force -ErrorAction Stop | Out-Null
Write-Log -Level INFO -Message "Force-applied $($rule.Name) to Block mode via registry (was Audit from Baseline)" -Module "ASR"
}
}
catch {
$result.Warnings += "Could not force-apply rule $($rule.Name) via registry: $($_.Exception.Message)"
Write-Log -Level WARNING -Message "Could not force-apply $($rule.Name) via registry: $($_.Exception.Message)" -Module "ASR"
}
}
}
Write-Log -Level INFO -Message "Successfully applied $($ruleIds.Count) ASR rules" -Module "ASR"
}
catch {
$result.Errors += "Failed to apply ASR rules: $($_.Exception.Message)"
Write-Log -Level ERROR -Message "Set-MpPreference failed: $($_.Exception.Message)" -Module "ASR"
}
return $result
}