Repository navigation
Expand file tree
/
Copy pathInstall-ExploitProtectionPolicy.ps1
More file actions
250 lines (213 loc) · 9.84 KB
/
Copy pathInstall-ExploitProtectionPolicy.ps1
File metadata and controls
250 lines (213 loc) · 9.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
<#
.SYNOPSIS
Installs and enforces Exploit Protection policy profiles.
.DESCRIPTION
Applies an Exploit Protection policy to the local system and configures the
Group Policy registry key for persistence. Automatically detects Windows 10
vs Windows 11 and supports Enforce and Audit modes.
Optionally merges the Hardened Services profile (EP-HardenedServices.xml) on
top of the baseline policy into a unified policy file before application.
.PARAMETER Mode
Deployment mode:
- 'Enforce' (Default): Active mitigation enforcement (blocks violations).
- 'Audit': Telemetry-only mode (logs violations to Event Log without blocking).
.PARAMETER IncludeHardenedServices
Optional switch to merge the Hardened Services profile (EP-HardenedServices.xml)
on top of the baseline policy. Defaults to false unless explicitly specified.
.PARAMETER CustomPolicyFilePath
Explicit path to a custom Exploit Protection XML file, overriding auto-detection.
.EXAMPLE
.\Install-ExploitProtectionPolicy.ps1
Auto-detects OS and installs baseline in Enforce mode (Hardened Services omitted by default).
.EXAMPLE
.\Install-ExploitProtectionPolicy.ps1 -IncludeHardenedServices
Installs the baseline policy merged with the Hardened Services profile in Enforce mode.
.EXAMPLE
.\Install-ExploitProtectionPolicy.ps1 -Mode Audit
Installs only the baseline profile in Audit mode.
.EXAMPLE
.\Install-ExploitProtectionPolicy.ps1 -Mode Audit -IncludeHardenedServices
Installs baseline merged with Hardened Services in Audit mode.
.EXAMPLE
.\Install-ExploitProtectionPolicy.ps1 -WhatIf
Simulates the installation and merging without altering system state.
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter()]
[ValidateSet('Enforce', 'Audit')]
[string]$Mode = 'Enforce',
[Parameter()]
[switch]$IncludeHardenedServices = $false,
[Parameter()]
[string]$CustomPolicyFilePath
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# Verify Administrative Privileges
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin -and -not $WhatIfPreference) {
throw "This script requires Administrator privileges. Please re-run from an elevated PowerShell console."
}
$scriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Path
$shouldIncludeHardened = [bool]$IncludeHardenedServices
# Determine source baseline XML policy file
if ($CustomPolicyFilePath) {
$sourcePolicyFilePath = if ([System.IO.Path]::IsPathRooted($CustomPolicyFilePath)) {
$CustomPolicyFilePath
} else {
Join-Path $scriptDirectory -ChildPath $CustomPolicyFilePath
}
}
else {
$isWin11 = [System.Environment]::OSVersion.Version.Build -ge 22000
$osLabel = if ($isWin11) { 'W11' } else { 'W10' }
$sourceFileName = if ($Mode -eq 'Audit') {
"EP-$osLabel-Audit.xml"
} else {
"EP-$osLabel.xml"
}
$sourcePolicyFilePath = Join-Path $scriptDirectory -ChildPath $sourceFileName
}
# Validate source baseline file exists
if (-not (Test-Path $sourcePolicyFilePath)) {
throw "Source policy file '$sourcePolicyFilePath' not found. Aborting..."
}
# Validate baseline XML syntax
Write-Verbose "Validating XML syntax of '$sourcePolicyFilePath'..."
try {
[xml]$baselineXml = Get-Content -Path $sourcePolicyFilePath -Raw
$baseAppCount = if ($baselineXml.MitigationPolicy.AppConfig) { $baselineXml.MitigationPolicy.AppConfig.Count } else { 0 }
Write-Verbose "Baseline XML is well-formed with $baseAppCount AppConfig definitions."
}
catch {
throw "Source file '$sourcePolicyFilePath' is not valid XML: $($_.Exception.Message)"
}
# Helper to auditify AppConfig nodes if deploying in Audit mode
function ConvertTo-AuditAppNode([System.Xml.XmlElement]$appNode) {
$dc = $appNode.SelectSingleNode("DynamicCode")
if ($dc) {
$dc.SetAttribute("BlockDynamicCode", "false")
$dc.SetAttribute("Audit", "true")
}
$cp = $appNode.SelectSingleNode("ChildProcess")
if ($cp) {
$cp.SetAttribute("DisallowChildProcessCreation", "false")
$cp.SetAttribute("Audit", "true")
}
$il = $appNode.SelectSingleNode("ImageLoad")
if ($il) {
if ($il.HasAttribute("BlockRemoteImageLoads")) {
$il.SetAttribute("BlockRemoteImageLoads", "false")
$il.SetAttribute("AuditRemoteImageLoads", "true")
}
if ($il.HasAttribute("BlockLowLabelImageLoads")) {
$il.SetAttribute("BlockLowLabelImageLoads", "false")
$il.SetAttribute("AuditLowLabelImageLoads", "true")
}
}
$sb = $appNode.SelectSingleNode("SignedBinaries")
if ($sb) {
if ($sb.GetAttribute("EnforceModuleDependencySigning") -eq "true") {
$sb.SetAttribute("EnforceModuleDependencySigning", "false")
$sb.SetAttribute("AuditEnforceModuleDependencySigning", "true")
}
if ($sb.GetAttribute("MicrosoftSignedOnly") -eq "true") {
$sb.SetAttribute("MicrosoftSignedOnly", "false")
$sb.SetAttribute("AuditMicrosoftSignedOnly", "true")
}
}
$f = $appNode.SelectSingleNode("Fonts")
if ($f) {
if ($f.GetAttribute("DisableNonSystemFonts") -eq "true") {
$f.SetAttribute("DisableNonSystemFonts", "false")
$f.SetAttribute("AuditOnly", "true")
$f.SetAttribute("Audit", "true")
}
}
$sc = $appNode.SelectSingleNode("SystemCalls")
if ($sc) {
if ($sc.GetAttribute("DisableWin32kSystemCalls") -eq "true") {
$sc.SetAttribute("DisableWin32kSystemCalls", "false")
$sc.SetAttribute("Audit", "true")
}
}
}
$tempMergedPath = $null
$policyToDeploy = $sourcePolicyFilePath
if ($shouldIncludeHardened) {
$hardenedFilePath = Join-Path $scriptDirectory -ChildPath 'EP-HardenedServices.xml'
if (-not (Test-Path $hardenedFilePath)) {
throw "Hardened Services policy file '$hardenedFilePath' not found. Aborting..."
}
Write-Verbose "Merging Hardened Services profile from '$hardenedFilePath' on top of baseline..."
try {
[xml]$hardenedXml = Get-Content -Path $hardenedFilePath -Raw
$mergedDoc = $baselineXml.Clone()
$hardenedCount = 0
if ($hardenedXml.MitigationPolicy.AppConfig) {
foreach ($serviceNode in $hardenedXml.MitigationPolicy.AppConfig) {
$serviceClone = $serviceNode.Clone()
if ($Mode -eq 'Audit') {
ConvertTo-AuditAppNode $serviceClone
}
$imported = $mergedDoc.ImportNode($serviceClone, $true)
$mergedDoc.MitigationPolicy.AppendChild($imported) | Out-Null
$hardenedCount++
}
}
# Create temporary merged file
$tempMergedPath = Join-Path ([System.IO.Path]::GetTempPath()) "EP-Merged-$([Guid]::NewGuid().ToString('N')).xml"
$mergedDoc.Save($tempMergedPath)
$policyToDeploy = $tempMergedPath
Write-Host "Merged $hardenedCount hardened services into baseline policy (Total: $($baseAppCount + $hardenedCount) apps)." -ForegroundColor Cyan
}
catch {
throw "Failed to merge Hardened Services profile: $($_.Exception.Message)"
}
}
Write-Host "Target Baseline File : $sourcePolicyFilePath" -ForegroundColor Cyan
Write-Host "Include Hardened Svc : $shouldIncludeHardened" -ForegroundColor Cyan
Write-Host "Deployment Mode : $Mode" -ForegroundColor Cyan
# Destination paths
$destinationFolder = Join-Path $Env:windir -ChildPath 'EP'
$destinationPolicyFilePath = Join-Path $destinationFolder -ChildPath 'EP.xml'
$exploitProtectionRegPath = 'HKLM:\Software\Policies\Microsoft\Windows Defender ExploitGuard\Exploit Protection'
$actionDescription = if ($shouldIncludeHardened) {
"Apply Exploit Protection policy (Baseline + Hardened Services, Mode: $Mode) to '$destinationPolicyFilePath'"
} else {
"Apply Exploit Protection baseline policy (Mode: $Mode) from '$sourcePolicyFilePath' to '$destinationPolicyFilePath'"
}
try {
if ($PSCmdlet.ShouldProcess("Local System", $actionDescription)) {
# Ensure destination folder exists
if (-not (Test-Path $destinationFolder)) {
Write-Verbose "Creating destination folder '$destinationFolder'..."
New-Item -Path $destinationFolder -ItemType Directory -Force | Out-Null
}
# Copy policy to standard destination
Write-Verbose "Writing policy to '$destinationPolicyFilePath'..."
Copy-Item -Path $policyToDeploy -Destination $destinationPolicyFilePath -Force
# Ensure registry key exists
if (-not (Test-Path $exploitProtectionRegPath)) {
Write-Verbose "Creating registry key '$exploitProtectionRegPath'..."
New-Item -Path $exploitProtectionRegPath -Force | Out-Null
}
# Apply policy via Set-ProcessMitigation
Write-Host "Applying process mitigations from '$destinationPolicyFilePath'..." -ForegroundColor Yellow
Set-ProcessMitigation -PolicyFilePath $destinationPolicyFilePath
# Configure GPO persistence registry value
Write-Verbose "Configuring GPO registry value 'ExploitProtectionSettings'..."
Set-ItemProperty -Path $exploitProtectionRegPath -Name 'ExploitProtectionSettings' -Value $destinationPolicyFilePath -Force | Out-Null
Write-Host "Policy deployment successful ($Mode mode)!" -ForegroundColor Green
if ($shouldIncludeHardened) {
Write-Host "Hardened Services profile successfully merged and enforced." -ForegroundColor Green
}
Write-Host "Please restart your computer for all system-level mitigations to take effect." -ForegroundColor Green
}
}
finally {
if ($tempMergedPath -and (Test-Path $tempMergedPath)) {
Remove-Item -Path $tempMergedPath -Force -ErrorAction SilentlyContinue
}
}