-
Notifications
You must be signed in to change notification settings - Fork 2
/
ApplySTIGBySCAPs.ps1
377 lines (344 loc) · 17.1 KB
/
ApplySTIGBySCAPs.ps1
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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
#========================================================================
#
# Title: ApplySTIGBySCAPs
# Created: 2018-02-26
# Author: Richard tracy
#
#
# GOALS:
# Parse ccdf file from STIG
# bind it with latest control tool
# build confguration files for all rules
#
# Inspiration: http://www.entelechyit.com/2017/01/02/powershell-and-disa-nist-stigs-part-1/
#
#Configuration Settings
# Place semicolon (;) in front for comments
#
# [Validate] = Section
# Ignore = True/False
# ScriptBlock = Powershell scriptblock; end with semicolon. Multiple Scriptblock keys will be combined
# Functon = Function script must be added in scripts folder. Mutiple function will be ignored; last one ran
# Argument =
# ScriptFile =
#
# [Remediate] = Section
# Ignore = True/False
# RunAlways = True/False
# LGPO = Uses LGPO.exe; Applies registry settings and updates local GPO
# GPTemplate = Uses LGPO.exe; Applies security template
#
#========================================================================
##*===============================================
##* PATH VARIABLE DECLARATION
##*===============================================
## Variables: Script Name and Script Paths
[string]$scriptPath = $MyInvocation.MyCommand.Definition
[string]$scriptName = [IO.Path]::GetFileNameWithoutExtension($scriptPath)
[string]$scriptFileName = Split-Path -Path $scriptPath -Leaf
[string]$scriptRoot = Split-Path -Path $scriptPath -Parent
[string]$invokingScript = (Get-Variable -Name 'MyInvocation').Value.ScriptName
# Get the invoking script directory
If ($invokingScript) {
# If this script was invoked by another script
[string]$scriptParentPath = Split-Path -Path $invokingScript -Parent
}
Else {
# If this script was not invoked by another script, fall back to the directory one level above this script
[string]$scriptParentPath = (Get-Item -LiteralPath $scriptRoot).Parent.FullName
}
#Get required folder and File paths
[string]$ExtensionsPath = Join-Path -Path $scriptRoot -ChildPath 'Extensions'
[string]$ModulesPath = Join-Path -Path $scriptRoot -ChildPath 'Modules'
[string]$ToolsPath = Join-Path -Path $scriptRoot -ChildPath 'Tools'
[string]$TempPath = Join-Path -Path $scriptRoot -ChildPath 'Temp'
[string]$LogsPath = Join-Path -Path $scriptRoot -ChildPath 'Logs'
[string]$BackupGPOPath = Join-Path -Path $scriptRoot -ChildPath 'GPO'
[string]$workingLogPath = Join-Path -Path $LogsPath -ChildPath $env:COMPUTERNAME
New-Item $workingLogPath -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
[string]$workingTempPath = Join-Path -Path $TempPath -ChildPath $env:COMPUTERNAME
New-Item $workingTempPath -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
## Dot source the required Functions
Try {
[string]$moduleToolkitMain = "$ExtensionsPath\STIGSCAPToolMainExtension.ps1"
If (-not (Test-Path -Path $moduleToolkitMain -PathType Leaf)) { Throw "Extension script does not exist at the specified location [$moduleToolkitMain]." }
Else{
. $moduleToolkitMain
Write-Host "Loading main extension: $moduleToolkitMain" -ForegroundColor Green
}
}
Catch {
[int32]$mainExitCode = 60008
Write-Error -Message "Module [$moduleToolkitMain] failed to load: `n$($_.Exception.Message)`n `n$($_.InvocationInfo.PositionMessage)" -ErrorAction 'Continue'
Exit $mainExitCode
}
#try to load any additional scripts
$extensions = Get-ChildItem -Path $ExtensionsPath -Recurse -Include *.ps1 -Exclude STIGSCAPToolMainExtension.ps1
foreach($extension in $extensions){
Try{
Write-Host "Loading additional extension: $($extension.FullName)" -ForegroundColor Cyan
Import-Module $extension.FullName -ErrorAction SilentlyContinue
}
Catch {
[int32]$mainExitCode = 60008
Write-Error -Message "Module [$_] failed to load: `n$($_.Exception.Message)`n `n$($_.InvocationInfo.PositionMessage)" -ErrorAction 'Continue'
}
}
#try to load any additional modules
$modules = Get-ChildItem -Path $ModulesPath -Recurse -Include *.psd1
foreach($module in $modules){
Try{
Write-Host "Loading additional module: $($module.FullName)" -ForegroundColor Cyan
Import-Module $module.FullName -ErrorAction SilentlyContinue -DisableNameChecking -NoClobber
}
Catch {
Write-Host "Unable to import the module." $_.Exception.Message -ForegroundColor White -BackgroundColor Red
}
}
##*===============================================
##* MAIN ROUTINE
##*===============================================
# download and unzip your benchmark from DIA NISTA
# from: http://iase.disa.mil/stigs/compilations/Pages/index.aspx
$BenchMarkFilePath = "$scriptRoot\STIGS\U_Windows_Server_2016_V1R3_STIG\U_Windows_Server_2016_V1R3_Manual_STIG\U_Windows_Server_2016_STIG_V1R3_Manual-xccdf.xml"
# Download and unzip the latest control list
# from: http://iase.disa.mil/stigs/cci/Pages/index.aspx
$CCIControlFile = "$scriptRoot\CCI\U_CCI_List.xml"
# This is the NIST Revision we are wanting to reference:
$CCIFilter = 'NIST SP 800-53 Revision 4'
$ToolOption = "Remediate" #ValidateOnly or Remediate
##*===============================================
##* MAIN
##*===============================================
# Load the content as XML
[xml]$Stigx = Get-Content -Path $BenchMarkFilePath -EA Stop
[xml]$CCIx = Get-Content -Path $CCIControlFile -EA Stop
# start by parsing the xccdf benchmark
if($Stigx){
$StigCollection = @()
# loop through the xccdf benchmark collecting data into an object collection
foreach ($rule in $StigX.Benchmark.Group.Rule){
# create a new PSObject collecting and stripping out as required.
$STIG = New-Object -TypeName PSObject -Property ([ordered]@{
RuleID = $rule. id
Version = $rule.version
RuleTitle = $rule.title
Severity = $rule.severity
VulnerabilityDetails = $($($($rule.description) -split '</VulnDiscussion>')[0] -replace '<VulnDiscussion>', '')
Check = $rule.check.'check-content'
Fix = $rule.fixtext.'#text'
ControlIdentifier = $rule.ident.'#text'
Control = $null # control is null as it will be added from the CCI List
})
$StigCollection += $STIG
}# close foreach
}# close if
# loop through the Stig Collection updating the Control information pulled from the U_CCI_List.xml
foreach($StigObj in $StigCollection){
foreach($CciItem in $CCIX.cci_list.cci_items.cci_item){
if($CciItem.Id -EQ $StigObj.ControlIdentifier){
# filter the control version by the title
if($CciItem.references.reference.title -EQ $CCIFilter){
$StigObj.Control = $CciItem.references.reference.index
}
}
}
}
$evalcnt = 0
$notevalcnt = 0
$passedeval = 0
$failedeval = 0
$appliedcnt = 0
$missingcnt = 0
$ignoredcnt = 0
$errorcnt = 0
$result = $false
# loop through the Stig Collection to pull Version info; then find the config ini for it
foreach($StigObj in $StigCollection){
$File = "$scriptRoot\Configs\$($StigObj.Version).ini"
If (Test-Path -Path $File -ErrorAction SilentlyContinue){
Write-Host "Configuration file was found for: $($StigObj.Version)" -ForegroundColor Cyan
#if File found, parse the validation section
$ConfigFile = Get-IniContent -filePath $File
If ($ConfigFile.Validate.Ignore -eq "False"){
#if scriptblock was found
If ($ConfigFile.Validate.ScriptBlock){
$scriptBlock = [Scriptblock]::Create($ConfigFile.Validate.ScriptBlock)
try{
$result = Invoke-Command -ScriptBlock $scriptBlock
Write-Verbose " RUNNING COMMAND: Invoke-Command -ScriptBlock $scriptBlock"
Write-Verbose $result
$evalcnt ++
}
Catch {
Write-Host " VALIDATE: ScriptBlock failed to run. Check syntax in file: $File" -ForegroundColor DarkMagenta
$notevalcnt ++
}
}
#if function was found
If ($ConfigFile.Validate.Function){
$scriptBlock = [Scriptblock]::Create($ConfigFile.Validate.Function)
try{
$result = Invoke-Command -ScriptBlock $scriptBlock
Write-Verbose " RUNNING COMMAND: Invoke-Command -ScriptBlock $scriptBlock"
Write-Verbose $result
$evalcnt ++
}
Catch {
Write-Host " VALIDATE: Function failed to run. Check syntax in file: $File" -ForegroundColor DarkMagenta
$notevalcnt ++
}
}
If ($result){
Write-Host " VALIDATE: Results passed. Policy already implemented!" -ForegroundColor DarkGreen
$passedeval ++
}
Else{
Write-Host " VALIDATE: Results did not pass. Policy is not already implemented" -ForegroundColor DarkYellow
$failedeval ++
}
}
Else {
Write-Host " VALIDATE: Set to be ignored." -ForegroundColor DarkYellow
$notevalcnt ++
}
<#
QUERY: if validation results are TRUE and ignore is TRUE or RunAlways is TRUE. ACTION: run the remediate section
QUERY: if validation results are FALSE and ignore is FALSE or RunAlways is TRUE. ACTION: run the remediate section
QUERY: if validation results are TRUE and ignore is FALSE or RunAlways is TRUE. ACTION: run the remediate section
QUERY: if validation results are FALSE and ignore is TRUE or RunAlways is TRUE. ACTION: run the remediate section
QUERY: if validation results are FALSE and ignore is FALSE or RunAlways is NULL. ACTION: run the remediate section
QUERY: if validation results are TRUE and ignore is FALSE or RunAlways is NULL. ACTION: Do Nothing
QUERY: if validation results are TRUE and ignore is TRUE or RunAlways is NULL. ACTION: Do Nothing
#>
$runRemediate = 0
If ((!$result) -and ($ConfigFile.Remediate.Ignore -eq "False")){$runRemediate ++}
If($ConfigFile.Remediate.RunAlways -eq "True"){$runRemediate ++}
If($runRemediate -gt 0){
If ($ConfigFile.Remediate.LGPO){
#build LGPO script file
$Outfile = "$backupTempPath\$($StigObj.Version)_LGPO.stdOut"
$ErrorFile = "$backupTempPath\$($StigObj.Version)_LGPO.stdError"
$ConfigFile.Remediate.LGPO | Out-File $backupTempPath\LGPO.txt -Force
$result = Start-Process "$ToolsPath\LGPO.exe" -ArgumentList "/t $backupTempPath\LGPO.txt /v" -PassThru -Wait -NoNewWindow -RedirectStandardOutput $Outfile -RedirectStandardError $ErrorFile -Verbose
If ($result.ExitCode -eq 0){
Write-Host " REMEDIATE: Successfully applied policy: $($StigObj.RuleID)" -ForegroundColor Green
$appliedcnt ++
}
Else{
Write-Host " REMEDIATE: Unable to apply policy: $($StigObj.RuleID), exit code: $($result.ExitCode)" -ForegroundColor Red
Write-Host " View '$ErrorFile' for more details" -ForegroundColor Red
$errorcnt ++
}
}
If ($ConfigFile.Remediate.GPTemplate){
#build LGPO script file
$Outfile = "$backupTempPath\$($StigObj.Version)_GPTemplate.stdOut"
$ErrorFile = "$backupTempPath\$($StigObj.Version)_GPTemplate.stdError"
"[Unicode]
Unicode=yes
[Version]
signature=`"`$CHICAGO`$`"
Revision=1" | Out-File | Out-File $backupTempPath\GPTemplate.inf -Force
$ConfigFile.Remediate.GPTemplate | Out-File $backupTempPath\GPTemplate.inf -Append
$result = Start-Process "$ToolsPath\LGPO.exe" -ArgumentList "/s $backupTempPath\GPTemplate.inf /v" -PassThru -Wait -NoNewWindow -RedirectStandardOutput $Outfile -RedirectStandardError $ErrorFile -Verbose
If ($result.ExitCode -eq 0){
Write-Host " REMEDIATE: Successfully applied policy: $($StigObj.RuleID)" -ForegroundColor Green
$appliedcnt ++
}
Else{
Write-Host " REMEDIATE: Unable to apply policy: $($StigObj.RuleID), exit code: $($result.ExitCode)" -ForegroundColor Red
Write-Host " View '$ErrorFile' for more details" -ForegroundColor Red
$errorcnt ++
}
}
If ($ConfigFile.Remediate.SecPol){
#build LGPO script file
$Outfile = "$backupTempPath\$($StigObj.Version)_SecPol.stdOut"
$ErrorFile = "$backupTempPath\$($StigObj.Version)_SecPol.stdError"
#get each SecPol line in INI
$SecPols = $ConfigFile.Remediate.SecPol
Foreach ($SecPol in $SecPols){
$Part = $SecPol.split(",")
$Area = $Part[0]
$Key = $Part[1]
$Value = $Part[2]
}
#backup Security Policy
$SeceditResults = secedit /export /areas $Area /cfg "$backupTempPath\SecPol.cfg"
Copy-Item "$backupTempPath\SecPol.cfg" $backupTempPath\SecPol-backup-$Dated.cfg -Force -ErrorAction SilentlyContinue
If($Area -eq "SECURITYPOLICY"){$CfgSection = 'System Access'}
switch($Area){
#Includes Account Policies, Audit Policies, Event Log Settings and Security Options.
"SECURITYPOLICY" {$CfgSection = 'System Access'}
#Includes Restricted Group settings
"GROUP_MGMT" {}
#Includes User Rights Assignment
"USER_RIGHTS" {}
#Includes Registry Permissions
"REGKEYS" {}
#Includes File System permissions
"FILESTORE" {}
#Includes System Service settings
"SERVICES" {}
}
$currentValue = (Get-IniContent "$backupTempPath\SecPol.cfg").$CfgSection.$key
(Get-Content "$backupTempPath\SecPol.cfg").replace("$Key = $currentValue", "$Key = $Value") | Out-File "$backupTempPath\SecPol.cfg"
$result = Start-Process secedit -ArgumentList "/configure /db secedit.sdb /cfg ""$backupTempPath\SecPol.cfg""" -PassThru -Wait -NoNewWindow -RedirectStandardOutput $Outfile -RedirectStandardError $ErrorFile -Verbose
If ($result.ExitCode -eq 0){
Write-Host " REMEDIATE: Successfully applied policy: $($StigObj.RuleID)" -ForegroundColor Green
$appliedcnt ++
}
Else{
Write-Host " REMEDIATE: Unable to apply policy: $($StigObj.RuleID), exit code: $($result.ExitCode)" -ForegroundColor Red
Write-Host " View '$ErrorFile' for more details" -ForegroundColor Red
$errorcnt ++
}
}
}
#QUERY: if validation results are FALSE and ignore is TRUE. ACTION: Get reason
If ($ConfigFile.Remediate.Ignore -eq "True"){
If (!$result){
Write-Host " REMEDIATE: Policy: $($StigObj.RuleID) is configured to be ignored" -ForegroundColor Gray
If ($ConfigFile.Remediate.Reason){
$reason = $ConfigFile.Remediate.Reason
Write-Host " REASON: $reason" -ForegroundColor Gray
$ignoredcnt ++
}
Else{
Write-Host " REASON: Nothing specified in config, counted as an error" -ForegroundColor Red
$errorcnt ++
}
}
}
}
Else{
Write-Host "No configuration file was found for: $($StigObj.Version)" -ForegroundColor Yellow
$missingcnt ++
}
}
# Launch text
write-host ""
write-host "--------------------------------"
write-host "| " -NoNewLine
write-host "STIG Tool Summary" -NoNewLine -ForegroundColor Green
write-host " |"
write-host "--------------------------------"
write-host ""
write-host "Total Policies evaluated: " -NoNewLine
write-host $evalcnt -foregroundcolor green
write-host "Total Policies not evaluated: " -NoNewLine
write-host $notevalcnt -foregroundcolor yellow
write-host "Total Policies already set: " -NoNewLine
write-host $passedeval -foregroundcolor yellow
write-host "Total Policies not set: " -NoNewLine
write-host $failedeval -foregroundcolor yellow
write-host "Total Policies applied: " -NoNewLine
write-host $appliedcnt -foregroundcolor green
write-host "Total Policies not applied: " -NoNewLine
write-host $errorcnt -foregroundcolor yellow
write-host "Total Policies missing: " -NoNewLine
write-host $missingcnt -foregroundcolor red
write-host "Total Policies ignored: " -NoNewLine
write-host $ignoredcnt -foregroundcolor gray
write-host ""