Edit-WDACConfig.psm1
#requires -version 7.3.3 Function Test-IsAdmin { $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal $identity $principal.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) } if (-NOT (Test-IsAdmin)) { write-host "Administrator privileges Required" -ForegroundColor Magenta break } function Edit-WDACConfig { [CmdletBinding( DefaultParameterSetName = "set1", HelpURI = "https://github.com/HotCakeX/Harden-Windows-Security/wiki/WDAC-Module", SupportsShouldProcess = $true, PositionalBinding = $false, ConfirmImpact = 'High' )] Param( [Parameter(Mandatory = $false, ParameterSetName = "set1", Position = 0, ValueFromPipeline = $true)][switch]$AllowNewApps_AuditEvents, [Parameter(Mandatory = $false, ParameterSetName = "set2", Position = 0, ValueFromPipeline = $true)][switch]$AllowNewApps, [Parameter(Mandatory = $true, ParameterSetName = "set1", ValueFromPipelineByPropertyName = $true)] [Parameter(Mandatory = $true, ParameterSetName = "set2", ValueFromPipelineByPropertyName = $true)] [string]$SuppPolicyName, [Parameter(Mandatory = $true, ParameterSetName = "set1", ValueFromPipelineByPropertyName = $true)] [Parameter(Mandatory = $true, ParameterSetName = "set2", ValueFromPipelineByPropertyName = $true)] [string[]]$PolicyPaths, [Parameter(Mandatory = $false, ParameterSetName = "set1")] [switch]$Debugmode, [ValidateRange(1024KB, [int64]::MaxValue)] [Parameter(Mandatory = $false, ParameterSetName = "set1")] [Int64]$LogSize, [Parameter(Mandatory = $false)][switch]$SkipVersionCheck ) $ErrorActionPreference = 'Stop' if (-NOT $SkipVersionCheck) { # Make sure the latest version of the module is installed and if not, automatically update it, clean up any old versions $currentversion = (Test-modulemanifest "$psscriptroot\WDACConfig.psd1").Version.ToString() try { $latestversion = Invoke-RestMethod -Uri "https://raw.githubusercontent.com/HotCakeX/Harden-Windows-Security/main/WDACConfig/version.txt" } catch { Write-Error "Couldn't verify if the latest version of the module is installed, please check your Internet connection. You can optionally bypass the online check by using -SkipVersionCheck parameter." break } if (-NOT ($currentversion -eq $latestversion)) { Write-Host "The currently installed module's version is $currentversion while the latest version is $latestversion - Auto Updating the module now and will run your command after that 💓" Remove-Module -Name WDACConfig -Force Uninstall-Module -Name WDACConfig -AllVersions -Force Install-Module -Name WDACConfig -RequiredVersion $latestversion -Force Import-Module -Name WDACConfig -RequiredVersion $latestversion -Force -Global } } #region Misc-Functions # Increase Code Integrity Operational Event Logs size from the default 1MB to user defined size function Set-LogSize { [CmdletBinding()] param ([int64]$LogSize) $logName = 'Microsoft-Windows-CodeIntegrity/Operational' $log = New-Object System.Diagnostics.Eventing.Reader.EventLogConfiguration $logName $log.MaximumSizeInBytes = $LogSize $log.IsEnabled = $true $log.SaveChanges() } function Update-BasePolicyToEnforcement { Set-RuleOption -FilePath $PolicyPath -Option 3 -Delete ConvertFrom-CIPolicy $PolicyPath "$PolicyID.cip" | Out-Null CiTool --update-policy ".\$PolicyID.cip" -json Remove-Item ".\$PolicyID.cip" -Force Write-host "`n`nThe Base policy with the following details has been Re-Deployed in Enforcement Mode:" -ForegroundColor Green Write-Output "PolicyName = $PolicyName" Write-Output "PolicyGUID = $PolicyID`n" } #endregion Misc-Functions if ($AllowNewApps) { # remove any possible files from previous runs Remove-Item -Path ".\ProgramDir_ScanResults*.xml" -Force -ErrorAction SilentlyContinue Remove-Item -Path ".\SupplementalPolicy$SuppPolicyName.xml" -Force -ErrorAction SilentlyContinue $ProgramDir_ScanResultsArray = @() #Initiate Live Audit Mode foreach ($PolicyPath in $PolicyPaths) { # defining Base policy $xml = [xml](Get-Content $PolicyPath) $PolicyID = $xml.SiPolicy.PolicyID $PolicyName = ($xml.SiPolicy.Settings.Setting | Where-Object { $_.provider -eq "PolicyInfo" -and $_.valuename -eq "Name" -and $_.key -eq "Information" }).value.string # Remove any cip file if there is any Remove-Item -Path ".\$PolicyID.cip" -ErrorAction SilentlyContinue Set-RuleOption -FilePath $PolicyPath -Option 3 ConvertFrom-CIPolicy $PolicyPath "$PolicyID.cip" | Out-Null CiTool --update-policy ".\$PolicyID.cip" -json Remove-Item ".\$PolicyID.cip" -Force Write-host "`n`nThe Base policy with the following details has been Re-Deployed in Audit Mode:" -ForegroundColor Green Write-Output "PolicyName = $PolicyName" Write-Output "PolicyGUID = $PolicyID" #User Interaction Write-host "`nAudit mode deployed, start installing your programs now" -ForegroundColor Magenta Write-Host "When you've finished installing programs, Press Enter to start selecting program directories to scan`n" -ForegroundColor Blue Pause $ProgramsPaths = @() Write-host "`nSelect program directories to scan`n" -ForegroundColor Cyan do { [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null $OBJ = New-Object System.Windows.Forms.FolderBrowserDialog $OBJ.InitialDirectory = "$env:SystemDrive" $OBJ.Description = $Description $Spawn = New-Object System.Windows.Forms.Form -Property @{TopMost = $true } $Show = $OBJ.ShowDialog($Spawn) If ($Show -eq "OK") { $ProgramsPaths += $OBJ.SelectedPath } Else { break } } while ($true) if (-NOT ($ProgramsPaths.count -eq 0)) { Write-Host "Here are the paths you selected:" -ForegroundColor Yellow $ProgramsPaths | ForEach-Object { $_ } #Process Program Folders From User input for ($i = 0; $i -lt $ProgramsPaths.Count; $i++) { New-CIPolicy -FilePath ".\ProgramDir_ScanResults$($i).xml" -ScanPath $ProgramsPaths[$i] -Level SignedVersion -Fallback FilePublisher, Hash -UserPEs -MultiplePolicyFormat -UserWriteablePaths } # merge-cipolicy accept arrays - collecting all the policy files created by scanning user specified folders $ProgramDir_ScanResults = Get-ChildItem ".\" | Where-Object { $_.Name -like 'ProgramDir_ScanResults*.xml' } foreach ($file in $ProgramDir_ScanResults) { $ProgramDir_ScanResultsArray += $file.FullName } Merge-CIPolicy -PolicyPaths $ProgramDir_ScanResultsArray -OutputFilePath ".\SupplementalPolicy$SuppPolicyName.xml" | Out-Null #Re-Deploy-Basepolicy-in-Enforcement-mode Update-BasePolicyToEnforcement Remove-Item -Path ".\ProgramDir_ScanResults*.xml" -Force #Supplemental-policy-processing-and-deployment $SuppPolicyPath = ".\SupplementalPolicy$SuppPolicyName.xml" $SuppPolicyID = Set-CIPolicyIdInfo -FilePath $SuppPolicyPath -PolicyName "Supplemental Policy $SuppPolicyName made on $(Get-Date -Format 'MM-dd-yyyy')" -ResetPolicyID -BasePolicyToSupplementPath $PolicyPath $SuppPolicyID = $SuppPolicyID.Substring(11) # Make sure policy rule options that don't belong to a Supplemental policy don't exit @(0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 15, 16, 17, 19, 20) | ForEach-Object { Set-RuleOption -FilePath $SuppPolicyPath -Option $_ -Delete } Set-HVCIOptions -Strict -FilePath $SuppPolicyPath Set-CIPolicyVersion -FilePath $SuppPolicyPath -Version "1.0.0.0" ConvertFrom-CIPolicy $SuppPolicyPath "$SuppPolicyID.cip" | Out-Null CiTool --update-policy ".\$SuppPolicyID.cip" -json Remove-Item ".\$SuppPolicyID.cip" -Force Write-host "`nSupplemental policy with the following details has been Deployed in Enforcement Mode.`n" -ForegroundColor Green [PSCustomObject]@{ SupplementalPolicyName = $SuppPolicyName SupplementalPolicyGUID = $SuppPolicyID } } # If no program path was provied else { Write-Host "`nNo program folder was selected, reverting the changes and quitting...`n" -ForegroundColor Magenta #Re-Deploy-Basepolicy-in-Enforcement-mode Update-BasePolicyToEnforcement break } } } if ($AllowNewApps_AuditEvents) { if ($AllowNewApps_AuditEventsSCRIPTBLOCK -and $LogSize) { Set-LogSize -LogSize $LogSize } Remove-Item -Path ".\ProgramDir_ScanResults*.xml" -Force -ErrorAction SilentlyContinue Remove-Item -Path ".\SupplementalPolicy$SuppPolicyName.xml" -Force -ErrorAction SilentlyContinue $Date = Get-Date $ProgramDir_ScanResultsArray = @() #Initiate Live Audit Mode foreach ($PolicyPath in $PolicyPaths) { # defining Base policy $xml = [xml](Get-Content $PolicyPath) $PolicyID = $xml.SiPolicy.PolicyID $PolicyName = ($xml.SiPolicy.Settings.Setting | Where-Object { $_.provider -eq "PolicyInfo" -and $_.valuename -eq "Name" -and $_.key -eq "Information" }).value.string # Remove any cip file if there is any Remove-Item -Path ".\$PolicyID.cip" -ErrorAction SilentlyContinue Set-RuleOption -FilePath $PolicyPath -Option 3 ConvertFrom-CIPolicy $PolicyPath "$PolicyID.cip" | Out-Null CiTool --update-policy ".\$PolicyID.cip" -json Remove-Item ".\$PolicyID.cip" -Force Write-host "`n`nThe Base policy with the following details has been Re-Deployed in Audit Mode:" -ForegroundColor Green Write-Output "PolicyName = $PolicyName" Write-Output "PolicyGUID = $PolicyID" #User Interaction Write-host "`nAudit mode deployed, start installing your programs now" -ForegroundColor Magenta Write-Host "When you've finished installing programs, Press Enter to start selecting program directories to scan`n" -ForegroundColor Blue Pause $ProgramsPaths = @() Write-host "`nSelect program directories to scan`n" -ForegroundColor Cyan do { [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null $OBJ = New-Object System.Windows.Forms.FolderBrowserDialog $OBJ.InitialDirectory = "$env:SystemDrive" $OBJ.Description = $Description $Spawn = New-Object System.Windows.Forms.Form -Property @{TopMost = $true } $Show = $OBJ.ShowDialog($Spawn) If ($Show -eq "OK") { $ProgramsPaths += $OBJ.SelectedPath } Else { break } } while ($true) if (-NOT ($ProgramsPaths.count -eq 0)) { Write-Host "Here are the paths you selected:" -ForegroundColor Yellow $ProgramsPaths | ForEach-Object { $_ } # EventCapturing # produce policy xml file from event viewer logs Write-host "Scanning Windows Event logs and creating a policy file, please wait..." -ForegroundColor Cyan # Get Event viewer logs for code integrity $block2 = { foreach ($event in Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-CodeIntegrity/Operational'; ID = 3076 } | Where-Object { $_.TimeCreated -ge $Date } ) { $xml = [xml]$event.toxml() $xml.event.eventdata.data | ForEach-Object { $hash = @{} } { $hash[$_.name] = $_.'#text' } { [pscustomobject]$hash } | ForEach-Object { $_ | Select-Object FileVersion, 'File Name', PolicyGUID, 'SHA256 Hash', 'SHA256 Flat Hash', 'SHA1 Hash', 'SHA1 Flat Hash' } } } $block2results = Invoke-Command -ScriptBlock $block2 if ($block2results) { # Create File Rules based on hash of the files and store them in the $Rules variable $i = 1 $imax = ($block2results).count while ($i -le $imax) { $block2results | ForEach-Object { $Rules += Write-Output "`n<Allow ID=`"ID_ALLOW_AA_$i`" FriendlyName=`"$($_.'File Name') SHA256 Hash`" Hash=`"$($_.'SHA256 Hash')`" />" $Rules += Write-Output "`n<Allow ID=`"ID_ALLOW_AB_$i`" FriendlyName=`"$($_.'File Name') SHA256 Flat Hash`" Hash=`"$($_.'SHA256 Flat Hash')`" />" $Rules += Write-Output "`n<Allow ID=`"ID_ALLOW_AC_$i`" FriendlyName=`"$($_.'File Name') SHA1 Hash`" Hash=`"$($_.'SHA1 Hash')`" />" $Rules += Write-Output "`n<Allow ID=`"ID_ALLOW_AD_$i`" FriendlyName=`"$($_.'File Name') SHA1 Flat Hash`" Hash=`"$($_.'SHA1 Flat Hash')`" />" $i++ } } # Create File Rule Refs based on the ID of the File Rules above and store them in the $RulesRefs variable $i = 1 $imax = ($block2results).count while ($i -le $imax) { $block2results | ForEach-Object { $RulesRefs += Write-Output "`n<FileRuleRef RuleID=`"ID_ALLOW_AA_$i`" />" $RulesRefs += Write-Output "`n<FileRuleRef RuleID=`"ID_ALLOW_AB_$i`" />" $RulesRefs += Write-Output "`n<FileRuleRef RuleID=`"ID_ALLOW_AC_$i`" />" $RulesRefs += Write-Output "`n<FileRuleRef RuleID=`"ID_ALLOW_AD_$i`" />" $i++ } } # Save the the File Rules and File Rule Refs to the Out-File FileRulesAndFileRefs.txt in the current working directory for debugging purposes $Rules + $RulesRefs | Out-File FileRulesAndFileRefs.txt $EmptyPolicy = @" <?xml version="1.0" encoding="utf-8"?> <SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy" PolicyType="Base Policy"> <VersionEx>10.0.0.0</VersionEx> <PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID> <Rules> <Rule> <Option>Enabled:Unsigned System Integrity Policy</Option> </Rule> <Rule> <Option>Enabled:Audit Mode</Option> </Rule> <Rule> <Option>Enabled:Advanced Boot Options Menu</Option> </Rule> <Rule> <Option>Required:Enforce Store Applications</Option> </Rule> </Rules> <!--EKUS--> <EKUs /> <!--File Rules--> <FileRules> $Rules </FileRules> <!--Signers--> <Signers /> <!--Driver Signing Scenarios--> <SigningScenarios> <SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS_1" FriendlyName="Auto generated policy on $(Get-Date -Format 'MM-dd-yyyy')"> <ProductSigners /> </SigningScenario> <SigningScenario Value="12" ID="ID_SIGNINGSCENARIO_WINDOWS" FriendlyName="Auto generated policy on $(Get-Date -Format 'MM-dd-yyyy')"> <ProductSigners> <FileRulesRef> $RulesRefs </FileRulesRef> </ProductSigners> </SigningScenario> </SigningScenarios> <UpdatePolicySigners /> <CiSigners /> <HvciOptions>0</HvciOptions> <BasePolicyID>{B163125F-E30A-43FC-ABEC-E30B4EE88FA8}</BasePolicyID> <PolicyID>{B163125F-E30A-43FC-ABEC-E30B4EE88FA8}</PolicyID> </SiPolicy> "@ $EmptyPolicy | Out-File .\EventsSupplementalPolicy.xml # adding the policy file that consists of rules from audit even logs, to the array $ProgramDir_ScanResultsArray += "EventsSupplementalPolicy.xml" } #Process Program Folders From User input for ($i = 0; $i -lt $ProgramsPaths.Count; $i++) { New-CIPolicy -FilePath ".\ProgramDir_ScanResults$($i).xml" -ScanPath $ProgramsPaths[$i] -Level SignedVersion -Fallback FilePublisher, Hash -UserPEs -MultiplePolicyFormat -UserWriteablePaths } # merge-cipolicy accept arrays - collecting all the policy files created by scanning user specified folders $ProgramDir_ScanResults = Get-ChildItem ".\" | Where-Object { $_.Name -like 'ProgramDir_ScanResults*.xml' } foreach ($file in $ProgramDir_ScanResults) { $ProgramDir_ScanResultsArray += $file.FullName } Merge-CIPolicy -PolicyPaths $ProgramDir_ScanResultsArray -OutputFilePath ".\SupplementalPolicy$SuppPolicyName.xml" | Out-Null } else { Write-Host "`nNo program folder was selected, reverting the changes and quitting...`n" -ForegroundColor Magent #Re-Deploy-Basepolicy-in-Enforcement-mode Update-BasePolicyToEnforcement break } if (-NOT $Debugmode) { Remove-Item -Path ".\FileRulesAndFileRefs.txt" -Force -ErrorAction SilentlyContinue Remove-Item -Path "EventsSupplementalPolicy.xml" -Force -ErrorAction SilentlyContinue Remove-Item -Path ".\ProgramDir_ScanResults*.xml" -Force -ErrorAction SilentlyContinue } #Re-Deploy-Basepolicy-in-Enforcement-mode Update-BasePolicyToEnforcement #Supplemental-policy-processing-and-deployment $SuppPolicyPath = ".\SupplementalPolicy$SuppPolicyName.xml" $SuppPolicyID = Set-CIPolicyIdInfo -FilePath $SuppPolicyPath -PolicyName "Supplemental Policy $SuppPolicyName made on $(Get-Date -Format 'MM-dd-yyyy')" -ResetPolicyID -BasePolicyToSupplementPath $PolicyPath $SuppPolicyID = $SuppPolicyID.Substring(11) # Make sure policy rule options that don't belong to a Supplemental policy don't exit @(0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 15, 16, 17, 19, 20) | ForEach-Object { Set-RuleOption -FilePath $SuppPolicyPath -Option $_ -Delete } Set-HVCIOptions -Strict -FilePath $SuppPolicyPath Set-CIPolicyVersion -FilePath $SuppPolicyPath -Version "1.0.0.0" ConvertFrom-CIPolicy $SuppPolicyPath "$SuppPolicyID.cip" | Out-Null CiTool --update-policy ".\$SuppPolicyID.cip" -json Remove-Item ".\$SuppPolicyID.cip" -Force Write-host "`nSupplemental policy with the following details has been Deployed in Enforcement Mode.`n" -ForegroundColor Green [PSCustomObject]@{ SupplementalPolicyName = $SuppPolicyName SupplementalPolicyGUID = $SuppPolicyID } } } } # Set PSReadline tab completion to complete menu for easier access to available parameters - Only for the current session Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete # argument tab auto-completion for Policy Paths to show only .xml files and only base policies $ArgumentCompleterPolicyPaths = { Get-ChildItem | where-object { $_.extension -like '*.xml' } | foreach-object { return "`"$_`"" } } Register-ArgumentCompleter -CommandName "Edit-WDACConfig" -ParameterName "PolicyPaths" -ScriptBlock $ArgumentCompleterPolicyPaths |