From e43d30c75a12e35e48bdf110dfc538d2704d2200 Mon Sep 17 00:00:00 2001 From: Violet Hansen Date: Sun, 22 Dec 2024 10:38:25 +0200 Subject: [PATCH] Removed unused PowerShell logic (#473) Removed unused PowerShell logic belonging to WDACConfig. No longer needed, implemented in C# in AppControl Manager --- .../Core/Assert-WDACConfigIntegrity.psm1 | 36 -- .../Core/Deploy-SignedWDACConfig.psm1 | 7 - .../Core/Edit-SignedWDACConfig.psm1 | 14 - .../Core/New-DenyWDACConfig.psm1 | 6 - .../Core/New-KernelModeWDACConfig.psm1 | 2 - .../Core/New-SupplementalWDACConfig.psm1 | 6 - .../Core/New-WDACConfig.psm1 | 38 --- .../Core/Remove-WDACConfig.psm1 | 309 ++++-------------- .../Public/MockConfigCIBootstrap.psm1 | 22 -- .../Shared/Test-KernelProtectedFiles.psm1 | 45 --- .../WDACConfig Module Files/WDACConfig.psd1 | 12 +- .../WDACConfig Module Files/WDACConfig.psm1 | 113 ------- 12 files changed, 68 insertions(+), 542 deletions(-) delete mode 100644 WDACConfig/WDACConfig Module Files/Core/Assert-WDACConfigIntegrity.psm1 delete mode 100644 WDACConfig/WDACConfig Module Files/Core/New-WDACConfig.psm1 delete mode 100644 WDACConfig/WDACConfig Module Files/Public/MockConfigCIBootstrap.psm1 delete mode 100644 WDACConfig/WDACConfig Module Files/Shared/Test-KernelProtectedFiles.psm1 diff --git a/WDACConfig/WDACConfig Module Files/Core/Assert-WDACConfigIntegrity.psm1 b/WDACConfig/WDACConfig Module Files/Core/Assert-WDACConfigIntegrity.psm1 deleted file mode 100644 index ba5df20bd..000000000 --- a/WDACConfig/WDACConfig Module Files/Core/Assert-WDACConfigIntegrity.psm1 +++ /dev/null @@ -1,36 +0,0 @@ -Function Assert-WDACConfigIntegrity { - [CmdletBinding()] - param ( - [Alias('S')][Parameter(Mandatory = $false)][switch]$SaveLocally, - [Alias('P')][Parameter(Mandatory = $false)][System.IO.DirectoryInfo]$Path = "$([WDACConfig.GlobalVars]::ModuleRootPath)\..\Utilities\" - ) - [WDACConfig.LoggerInitializer]::Initialize($VerbosePreference, $DebugPreference, $Host) - Update-WDACConfigPSModule -InvocationStatement $MyInvocation.Statement - return [WDACConfig.AssertWDACConfigIntegrity]::Invoke($SaveLocally, $Path) - <# -.SYNOPSIS - Gets the SHA2-512 hashes of files in the WDACConfig and compares them with the ones in the cloud and shows the differences. -.DESCRIPTION - The Assert-WDACConfigIntegrity function scans all the relevant files in the WDACConfig's folder and its subfolders, calculates their SHA2-512 hashes in hexadecimal format, - Then it downloads the cloud CSV file from the GitHub repository and compares the hashes of the local files with the ones in the cloud. - By doing so, you can ascertain that the files in your local WDACConfig folder are identical to the ones in the cloud and devoid of any interference. - If there is any indication of tampering, the outcomes will be displayed on the console. -.LINK - https://github.com/HotCakeX/Harden-Windows-Security/wiki/Assert-WDACConfigIntegrity -.PARAMETER SaveLocally - Indicates that the function should save the results to a CSV file locally. - You don't need to use this parameter. -.PARAMETER Path - Specifies the path to save the CSV file to. The default path is the Utilities folder in the WDACConfig's folder. - This is used before uploading to GitHub to renew the hashes. - You don't need to use this parameter. -.INPUTS - System.Management.Automation.SwitchParameter - System.IO.DirectoryInfo -.OUTPUTS - System.String - System.Object[] -.EXAMPLE - Assert-WDACConfigIntegrity -#> -} \ No newline at end of file diff --git a/WDACConfig/WDACConfig Module Files/Core/Deploy-SignedWDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/Core/Deploy-SignedWDACConfig.psm1 index 316da54ed..1fd905c19 100644 --- a/WDACConfig/WDACConfig Module Files/Core/Deploy-SignedWDACConfig.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/Deploy-SignedWDACConfig.psm1 @@ -38,13 +38,6 @@ Function Deploy-SignedWDACConfig { Begin { [WDACConfig.LoggerInitializer]::Initialize($VerbosePreference, $DebugPreference, $Host) - Update-WDACConfigPSModule -InvocationStatement $MyInvocation.Statement - - if ([WDACConfig.GlobalVars]::ConfigCIBootstrap -eq $false) { - Invoke-MockConfigCIBootstrap - [WDACConfig.GlobalVars]::ConfigCIBootstrap = $true - } - [System.IO.DirectoryInfo]$StagingArea = [WDACConfig.StagingArea]::NewStagingArea('Deploy-SignedWDACConfig') #Region User-Configurations-Processing-Validation diff --git a/WDACConfig/WDACConfig Module Files/Core/Edit-SignedWDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/Core/Edit-SignedWDACConfig.psm1 index e0c9b5559..69eb2e319 100644 --- a/WDACConfig/WDACConfig Module Files/Core/Edit-SignedWDACConfig.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/Edit-SignedWDACConfig.psm1 @@ -113,13 +113,6 @@ Function Edit-SignedWDACConfig { $ModulesToImport += ([WDACConfig.FileUtility]::GetFilesFast("$([WDACConfig.GlobalVars]::ModuleRootPath)\XMLOps", $null, '.psm1')).FullName Import-Module -FullyQualifiedName $ModulesToImport -Force - Update-WDACConfigPSModule -InvocationStatement $MyInvocation.Statement - - if ([WDACConfig.GlobalVars]::ConfigCIBootstrap -eq $false) { - Invoke-MockConfigCIBootstrap - [WDACConfig.GlobalVars]::ConfigCIBootstrap = $true - } - [System.IO.DirectoryInfo]$StagingArea = [WDACConfig.StagingArea]::NewStagingArea('Edit-SignedWDACConfig') #Region User-Configurations-Processing-Validation @@ -685,13 +678,6 @@ Function Edit-SignedWDACConfig { [System.UInt16]$TotalSteps = 5 [System.UInt16]$CurrentStep = 0 - $CurrentStep++ - Write-Progress -Id 17 -Activity 'Getting the block rules' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - - [WDACConfig.Logger]::Write('Getting the Use-Mode Block Rules') - # This shouldn't deploy the policy unsigned if it is already signed - requires build 24H2 features - New-WDACConfig -GetUserModeBlockRules -Deploy - $CurrentStep++ Write-Progress -Id 17 -Activity 'Determining the policy type' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) diff --git a/WDACConfig/WDACConfig Module Files/Core/New-DenyWDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/Core/New-DenyWDACConfig.psm1 index a1a7e545b..352fa6483 100644 --- a/WDACConfig/WDACConfig Module Files/Core/New-DenyWDACConfig.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/New-DenyWDACConfig.psm1 @@ -54,12 +54,6 @@ Function New-DenyWDACConfig { ) Begin { [WDACConfig.LoggerInitializer]::Initialize($VerbosePreference, $DebugPreference, $Host) - Update-WDACConfigPSModule -InvocationStatement $MyInvocation.Statement - - if ([WDACConfig.GlobalVars]::ConfigCIBootstrap -eq $false) { - Invoke-MockConfigCIBootstrap - [WDACConfig.GlobalVars]::ConfigCIBootstrap = $true - } [System.IO.DirectoryInfo]$StagingArea = [WDACConfig.StagingArea]::NewStagingArea('New-DenyWDACConfig') diff --git a/WDACConfig/WDACConfig Module Files/Core/New-KernelModeWDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/Core/New-KernelModeWDACConfig.psm1 index 57d2c9f21..db5a8dba2 100644 --- a/WDACConfig/WDACConfig Module Files/Core/New-KernelModeWDACConfig.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/New-KernelModeWDACConfig.psm1 @@ -20,8 +20,6 @@ Function New-KernelModeWDACConfig { [WDACConfig.Logger]::Write('Importing the required sub-modules') Import-Module -Force -FullyQualifiedName @("$([WDACConfig.GlobalVars]::ModuleRootPath)\Shared\Get-KernelModeDriversAudit.psm1") - Update-WDACConfigPSModule -InvocationStatement $MyInvocation.Statement - [System.IO.DirectoryInfo]$StagingArea = [WDACConfig.StagingArea]::NewStagingArea('New-KernelModeWDACConfig') # Create a directory to store the kernel mode drivers symbolic links for both modes diff --git a/WDACConfig/WDACConfig Module Files/Core/New-SupplementalWDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/Core/New-SupplementalWDACConfig.psm1 index 659d0ee4e..cee57156c 100644 --- a/WDACConfig/WDACConfig Module Files/Core/New-SupplementalWDACConfig.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/New-SupplementalWDACConfig.psm1 @@ -29,12 +29,6 @@ Function New-SupplementalWDACConfig { ) Begin { [WDACConfig.LoggerInitializer]::Initialize($VerbosePreference, $DebugPreference, $Host) - Update-WDACConfigPSModule -InvocationStatement $MyInvocation.Statement - - if ([WDACConfig.GlobalVars]::ConfigCIBootstrap -eq $false) { - Invoke-MockConfigCIBootstrap - [WDACConfig.GlobalVars]::ConfigCIBootstrap = $true - } [System.IO.DirectoryInfo]$StagingArea = [WDACConfig.StagingArea]::NewStagingArea('New-SupplementalWDACConfig') diff --git a/WDACConfig/WDACConfig Module Files/Core/New-WDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/Core/New-WDACConfig.psm1 deleted file mode 100644 index b4fab7c17..000000000 --- a/WDACConfig/WDACConfig Module Files/Core/New-WDACConfig.psm1 +++ /dev/null @@ -1,38 +0,0 @@ -Function New-WDACConfig { - [CmdletBinding()] - Param( - [Parameter(Mandatory = $false)][switch]$GetDriverBlockRules, - [Parameter(Mandatory = $false)][switch]$AutoUpdate - ) - [WDACConfig.LoggerInitializer]::Initialize($VerbosePreference, $DebugPreference, $Host) - [System.IO.DirectoryInfo]$StagingArea = [WDACConfig.StagingArea]::NewStagingArea('New-WDACConfig') - Update-WDACConfigPSModule -InvocationStatement $MyInvocation.Statement - Try { - Switch ($PSCmdlet.ParameterSetName) { - 'GetDriverBlockRules' { - [WDACConfig.BasePolicyCreator]::SetAutoUpdateDriverBlockRules() - break - } - default { Write-Warning -Message 'None of the main parameters were selected.'; break } - } - } - catch { - throw $_ - } - Finally { - if (![WDACConfig.GlobalVars]::DebugPreference) { - Remove-Item -Path $StagingArea -Recurse -Force - } - } - <# -.SYNOPSIS - Automate a lot of tasks related to App Control for Business -.LINK - https://github.com/HotCakeX/Harden-Windows-Security/wiki/New-WDACConfig -.PARAMETER GetDriverBlockRules - Gets the latest Microsoft Recommended Driver Block rules -.PARAMETER AutoUpdate - Creates a scheduled task that will keep the Microsoft Recommended Driver Block rules up to date by downloading and applying - the latest block list every 7 days on the system. -#> -} \ No newline at end of file diff --git a/WDACConfig/WDACConfig Module Files/Core/Remove-WDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/Core/Remove-WDACConfig.psm1 index 73ce92f1f..3ed6a7092 100644 --- a/WDACConfig/WDACConfig Module Files/Core/Remove-WDACConfig.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/Remove-WDACConfig.psm1 @@ -6,16 +6,13 @@ Function Remove-WDACConfig { ConfirmImpact = 'High' )] Param( - [Alias('S')][Parameter(Mandatory = $False, ParameterSetName = 'Signed Base')][switch]$SignedBase, - [Alias('U')][Parameter(Mandatory = $False, ParameterSetName = 'Unsigned Or Supplemental')][switch]$UnsignedOrSupplemental, - [ArgumentCompleter([WDACConfig.ArgCompleter.XmlFileMultiSelectPicker])] [ValidateScript({ $_ | ForEach-Object -Process { [WDACConfig.PolicyFileSigningStatusDetection]::Check($_) -eq [WDACConfig.PolicyFileSigningStatusDetection+SigningStatus]::Signed ? $true : $false } }, ErrorMessage = 'One of the selected XML policy files is unsigned. Please use Remove-WDACConfig cmdlet with -UnsignedOrSupplemental parameter instead.')] - [parameter(Mandatory = $true, ParameterSetName = 'Signed Base', ValueFromPipelineByPropertyName = $true)] + [parameter(Mandatory = $true)] [System.IO.FileInfo[]]$PolicyPaths, [ArgumentCompleter({ @@ -25,112 +22,9 @@ Function Remove-WDACConfig { } } })] - [parameter(Mandatory = $False, ParameterSetName = 'Signed Base', ValueFromPipelineByPropertyName = $true)] + [parameter(Mandatory = $False)] [System.String]$CertCN, - [ArgumentCompleter({ - # Define the parameters that this script block will accept. - param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters) - - # Get a list of policies using the CiTool, excluding system policies and policies that aren't on disk. - # by adding "{ $_.FriendlyName }" we make sure the auto completion works when at least one of the policies doesn't have a friendly name - $Policies = foreach ($Item in [WDACConfig.CiToolHelper]::GetPolicies($false, $true, $true)) { - if (($Item.IsOnDisk -eq 'True') -and $Item.FriendlyName) { - $Item - } - } - - # Create a hashtable mapping policy names to policy IDs. This will be used later to check if a policy ID already exists. - [System.Collections.Hashtable]$NameIDMap = @{} - foreach ($Policy in $Policies) { - $NameIDMap[$Policy.Friendlyname] = $Policy.policyID - } - - # Get the IDs of existing policies that are already being used in the current command. - $ExistingIDs = $FakeBoundParameters['PolicyIDs'] - - # Get the policy names that are currently being used in the command. This is done by looking at the abstract syntax tree (AST) - # of the command and finding all string literals, which are assumed to be policy names. - $Existing = $CommandAst.FindAll({ - $args[0] -is [System.Management.Automation.Language.StringConstantExpressionAst] - }, $False).Value - - # Filter out the policy names that are already being used or whose corresponding policy IDs are already being used. - # The resulting list of policy names is what will be shown as autocomplete suggestions. - $Candidates = foreach ($Item in $Policies.Friendlyname) { - if ($Item -notin $Existing -and $NameIDMap[$Item] -notin $ExistingIDs) { - $Item - } - } - - # Additionally, if the policy name contains spaces, it's enclosed in single quotes to ensure it's treated as a single argument. - # This is achieved using the Compare-Object cmdlet to compare the existing and candidate values, and outputting the resulting matches. - # For each resulting match, it checks if the match contains a space, if so, it's enclosed in single quotes, if not, it's returned as is. - foreach ($Item in (Compare-Object -ReferenceObject $Candidates -DifferenceObject $Existing -PassThru)) { - if ($Item.SideIndicator -eq '<=') { - if ($Item -match ' ') { - "'{0}'" -f $Item - } - else { - $Item - } - } - } - })] - [ValidateScript({ - if ( - !([System.Collections.Generic.HashSet[System.String]]@([PolicyNamezx]::new().GetValidValues())).Contains($_) - ) { - throw "Invalid policy name: $_" - } - $true - })] - [Parameter(Mandatory = $False, ParameterSetName = 'Unsigned Or Supplemental')] - [System.String[]]$PolicyNames, - - [ArgumentCompleter({ - param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters) - - # Get a list of policies using the CiTool, excluding system policies and policies that aren't on disk. - $Policies = foreach ($Item in [WDACConfig.CiToolHelper]::GetPolicies($false, $true, $true)) { - if ($Item.IsOnDisk -eq 'True') { - $Item - } - } - - # Create a hashtable mapping policy IDs to policy names. This will be used later to check if a policy name already exists. - [System.Collections.Hashtable]$IDNameMap = @{} - foreach ($Policy in $Policies) { - $IDNameMap[$Policy.policyID] = $Policy.Friendlyname - } - - # Get the names of existing policies that are already being used in the current command. - $ExistingNames = $FakeBoundParameters['PolicyNames'] - - # Get the policy IDs that are currently being used in the command. This is done by looking at the abstract syntax tree (AST) - # of the command and finding all string literals, which are assumed to be policy IDs. - $Existing = $CommandAst.FindAll({ - $args[0] -is [System.Management.Automation.Language.StringConstantExpressionAst] - }, $False).Value - - # Filter out the policy IDs that are already being used or whose corresponding policy names are already being used. - # The resulting list of policy IDs is what will be shown as autocomplete suggestions. - $Candidates = foreach ($Item in $Policies.policyID) { - if ($Item -notin $Existing -and $IDNameMap[$Item] -notin $ExistingNames) { - $Item - } - } - - # Return the candidates - return $Candidates - })] - [ValidateScript({ - if ($_ -notin [PolicyIDzx]::new().GetValidValues()) { throw "Invalid policy ID: $_" } - $true - })] - [Parameter(Mandatory = $False, ParameterSetName = 'Unsigned Or Supplemental')] - [System.String[]]$PolicyIDs, - [ArgumentCompleter([WDACConfig.ArgCompleter.ExeFilePathsPicker])] [parameter(Mandatory = $False, ParameterSetName = 'Signed Base', ValueFromPipelineByPropertyName = $true)] [System.IO.FileInfo]$SignToolPath, @@ -139,61 +33,31 @@ Function Remove-WDACConfig { ) Begin { [WDACConfig.LoggerInitializer]::Initialize($VerbosePreference, $DebugPreference, $Host) - Update-WDACConfigPSModule -InvocationStatement $MyInvocation.Statement [System.IO.DirectoryInfo]$StagingArea = [WDACConfig.StagingArea]::NewStagingArea('Remove-WDACConfig') #Region User-Configurations-Processing-Validation [WDACConfig.Logger]::Write('Validating and processing user configurations') - if ($PSCmdlet.ParameterSetName -eq 'Signed Base') { + # Get SignToolPath from user parameter or user config file or auto-detect it + [System.IO.FileInfo]$SignToolPathFinal = [WDACConfig.SignToolHelper]::GetSignToolPath($SignToolPath ?? ([WDACConfig.UserConfiguration]::Get().SignToolCustomPath)) - # Get SignToolPath from user parameter or user config file or auto-detect it - [System.IO.FileInfo]$SignToolPathFinal = [WDACConfig.SignToolHelper]::GetSignToolPath($SignToolPath ?? ([WDACConfig.UserConfiguration]::Get().SignToolCustomPath)) - - # If CertCN was not provided by user, check if a valid value exists in user configs, if so, use it, otherwise throw an error - if (!$CertCN) { - if ([WDACConfig.CertCNz]::new().GetValidValues() -contains ([WDACConfig.UserConfiguration]::Get().CertificateCommonName)) { - [System.String]$CertCN = [WDACConfig.UserConfiguration]::Get().CertificateCommonName - } - else { - throw 'CertCN parameter cannot be empty and no valid user configuration was found for it.' - } + # If CertCN was not provided by user, check if a valid value exists in user configs, if so, use it, otherwise throw an error + if (!$CertCN) { + if ([WDACConfig.CertCNz]::new().GetValidValues() -contains ([WDACConfig.UserConfiguration]::Get().CertificateCommonName)) { + [System.String]$CertCN = [WDACConfig.UserConfiguration]::Get().CertificateCommonName } else { - if ([WDACConfig.CertCNz]::new().GetValidValues() -notcontains $CertCN) { - throw "$CertCN does not belong to a subject CN of any of the deployed certificates" - } + throw 'CertCN parameter cannot be empty and no valid user configuration was found for it.' } } - #Endregion User-Configurations-Processing-Validation - - # ValidateSet for Policy names - Class PolicyNamezx : System.Management.Automation.IValidateSetValuesGenerator { - [System.String[]] GetValidValues() { - - $PolicyNamezx = [System.Collections.Generic.HashSet[System.String]]@(foreach ($Policy in (&'C:\Windows\System32\CiTool.exe' -lp -json | ConvertFrom-Json).Policies) { - if ( ($Policy.IsOnDisk -eq 'True') -and ($Policy.IsSystemPolicy -ne 'True')) { - $Policy.Friendlyname - } - }) - return $PolicyNamezx + else { + if ([WDACConfig.CertCNz]::new().GetValidValues() -notcontains $CertCN) { + throw "$CertCN does not belong to a subject CN of any of the deployed certificates" } } - # ValidateSet for Policy IDs - Class PolicyIDzx : System.Management.Automation.IValidateSetValuesGenerator { - [System.String[]] GetValidValues() { - - [System.String[]]$PolicyIDzx = foreach ($Item in (&'C:\Windows\System32\CiTool.exe' -lp -json | ConvertFrom-Json).Policies) { - if (($Item.IsOnDisk -eq 'True') -and ($Item.IsSystemPolicy -ne 'True')) { - $Item.PolicyID - } - } - - return $PolicyIDzx - } - } + #Endregion User-Configurations-Processing-Validation # Detecting if Confirm switch is used to bypass the confirmation prompts if ($Force -and -Not $Confirm) { @@ -204,112 +68,81 @@ Function Remove-WDACConfig { Try { - # If a signed policy is being removed - if ($SignedBase) { + [WDACConfig.Logger]::Write('Looping over each selected policy XML file') + foreach ($PolicyPath in $PolicyPaths) { - [WDACConfig.Logger]::Write('Looping over each selected policy XML file') - foreach ($PolicyPath in $PolicyPaths) { + # The total number of the main steps for the progress bar to render + $TotalSteps = 3us + $CurrentStep = 0us - # The total number of the main steps for the progress bar to render - $TotalSteps = 3us - $CurrentStep = 0us + $CurrentStep++ + Write-Progress -Id 18 -Activity 'Parsing the XML Policy' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - $CurrentStep++ - Write-Progress -Id 18 -Activity 'Parsing the XML Policy' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + [WDACConfig.Logger]::Write('Converting the XML file to an XML object') + [System.Xml.XmlDocument]$Xml = Get-Content -Path $PolicyPath - [WDACConfig.Logger]::Write('Converting the XML file to an XML object') - [System.Xml.XmlDocument]$Xml = Get-Content -Path $PolicyPath + [WDACConfig.Logger]::Write('Extracting the Policy ID from the XML object') + [System.String]$PolicyID = $Xml.SiPolicy.PolicyID + [WDACConfig.Logger]::Write("The policy ID of the currently processing xml file is $PolicyID") - [WDACConfig.Logger]::Write('Extracting the Policy ID from the XML object') - [System.String]$PolicyID = $Xml.SiPolicy.PolicyID - [WDACConfig.Logger]::Write("The policy ID of the currently processing xml file is $PolicyID") - - # Extracting the policy name from the selected XML policy file - [System.String]$PolicyName = foreach ($Item in $Xml.SiPolicy.Settings.Setting) { - if ($Item.Provider -eq 'PolicyInfo' -and $Item.ValueName -eq 'Name' -and $Item.Key -eq 'Information') { - $Item.Value.String - } + # Extracting the policy name from the selected XML policy file + [System.String]$PolicyName = foreach ($Item in $Xml.SiPolicy.Settings.Setting) { + if ($Item.Provider -eq 'PolicyInfo' -and $Item.ValueName -eq 'Name' -and $Item.Key -eq 'Information') { + $Item.Value.String } + } - # Prevent users from accidentally attempting to remove policies that aren't even deployed on the system - [WDACConfig.Logger]::Write('Making sure the selected XML policy is deployed on the system') + # Prevent users from accidentally attempting to remove policies that aren't even deployed on the system + [WDACConfig.Logger]::Write('Making sure the selected XML policy is deployed on the system') - Try { - [System.Guid[]]$CurrentPolicyIDs = foreach ($Item in [WDACConfig.CiToolHelper]::GetPolicies($false, $true, $true)) { - if ($Item.IsSystemPolicy -ne 'True') { - "{$($Item.PolicyID)}" - } + Try { + [System.Guid[]]$CurrentPolicyIDs = foreach ($Item in [WDACConfig.CiToolHelper]::GetPolicies($false, $true, $true)) { + if ($Item.IsSystemPolicy -ne 'True') { + "{$($Item.PolicyID)}" } } - catch { - Throw 'No policy is deployed on the system.' - } - - if ($CurrentPolicyIDs -notcontains $PolicyID) { - Throw 'The selected policy file is not deployed on the system.' - } - - $CurrentStep++ - Write-Progress -Id 18 -Activity 'Processing the policy' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - - [WDACConfig.Logger]::Write('Making sure SupplementalPolicySigners do not exist in the XML policy') - [WDACConfig.CiPolicyHandler]::RemoveSupplementalSigners($PolicyPath.FullName) - - [WDACConfig.CiRuleOptions]::Set($PolicyPath, $null, [WDACConfig.CiRuleOptions+PolicyRuleOptions]::EnabledUnsignedSystemIntegrityPolicy, $null, $null, $null, $null, $null, $null, $null, $null) + } + catch { + Throw 'No policy is deployed on the system.' + } - [System.IO.FileInfo]$PolicyCIPPath = Join-Path -Path $StagingArea -ChildPath "$PolicyID.cip" + if ($CurrentPolicyIDs -notcontains $PolicyID) { + Throw 'The selected policy file is not deployed on the system.' + } - # Converting the Policy XML file to CIP binary file - $null = ConvertFrom-CIPolicy -XmlFilePath $PolicyPath -BinaryFilePath $PolicyCIPPath + $CurrentStep++ + Write-Progress -Id 18 -Activity 'Processing the policy' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - $CurrentStep++ - Write-Progress -Id 18 -Activity 'Signing the policy' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + [WDACConfig.Logger]::Write('Making sure SupplementalPolicySigners do not exist in the XML policy') + [WDACConfig.CiPolicyHandler]::RemoveSupplementalSigners($PolicyPath.FullName) - [WDACConfig.SignToolHelper]::Sign($PolicyCIPPath, $SignToolPathFinal, $CertCN) + [WDACConfig.CiRuleOptions]::Set($PolicyPath, $null, [WDACConfig.CiRuleOptions+PolicyRuleOptions]::EnabledUnsignedSystemIntegrityPolicy, $null, $null, $null, $null, $null, $null, $null, $null) - # Fixing the extension name of the newly signed CIP file - Move-Item -Path (Join-Path -Path $StagingArea -ChildPath "$PolicyID.cip.p7") -Destination $PolicyCIPPath -Force + [System.IO.FileInfo]$PolicyCIPPath = Join-Path -Path $StagingArea -ChildPath "$PolicyID.cip" - # Deploying the newly signed CIP file + # Converting the Policy XML file to CIP binary file + $null = ConvertFrom-CIPolicy -XmlFilePath $PolicyPath -BinaryFilePath $PolicyCIPPath - # Prompt for confirmation before proceeding - if ($PSCmdlet.ShouldProcess('This PC', 'Deploying the signed policy')) { + $CurrentStep++ + Write-Progress -Id 18 -Activity 'Signing the policy' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - [WDACConfig.CiToolHelper]::UpdatePolicy($PolicyCIPPath) + [WDACConfig.SignToolHelper]::Sign($PolicyCIPPath, $SignToolPathFinal, $CertCN) - Write-ColorfulTextWDACConfig -Color Lavender -InputText "Policy with the following details has been Re-signed and Re-deployed in Unsigned mode.`nPlease restart your system." - Write-ColorfulTextWDACConfig -Color MintGreen -InputText "PolicyName = $PolicyName" - Write-ColorfulTextWDACConfig -Color MintGreen -InputText "PolicyGUID = $PolicyID" - } - Write-Progress -Id 18 -Activity 'Complete.' -Completed - } - } - - # If an unsigned policy is being removed - if ($UnsignedOrSupplemental) { + # Fixing the extension name of the newly signed CIP file + Move-Item -Path (Join-Path -Path $StagingArea -ChildPath "$PolicyID.cip.p7") -Destination $PolicyCIPPath -Force - # If IDs were supplied by user - foreach ($ID in $PolicyIDs ) { - [WDACConfig.CiToolHelper]::RemovePolicy($ID) - Write-ColorfulTextWDACConfig -Color Lavender -InputText "Policy with the ID $ID has been successfully removed." - } + # Deploying the newly signed CIP file - # If names were supplied by user - # HashSet to store Unique Policy IDs based on the input name, this will take care of the situations where multiple policies with the same name are deployed - $NameID = [System.Collections.Generic.HashSet[System.String]]@(foreach ($PolicyName in $PolicyNames) { - foreach ($Item in [WDACConfig.CiToolHelper]::GetPolicies($true, $true, $true)) { - if (($Item.IsOnDisk -eq 'True') -and ($Item.FriendlyName -eq $PolicyName)) { - $Item.PolicyID - } - } - }) + # Prompt for confirmation before proceeding + if ($PSCmdlet.ShouldProcess('This PC', 'Deploying the signed policy')) { - [WDACConfig.Logger]::Write("$($NameID.count) policy IDs have been gathered from the supplied policy names and are going to be removed from the system") + [WDACConfig.CiToolHelper]::UpdatePolicy($PolicyCIPPath) - foreach ($ID in $NameID) { - [WDACConfig.CiToolHelper]::RemovePolicy($ID) - Write-ColorfulTextWDACConfig -Color Lavender -InputText "Policy with the ID $ID has been successfully removed." + Write-ColorfulTextWDACConfig -Color Lavender -InputText "Policy with the following details has been Re-signed and Re-deployed in Unsigned mode.`nPlease restart your system." + Write-ColorfulTextWDACConfig -Color MintGreen -InputText "PolicyName = $PolicyName" + Write-ColorfulTextWDACConfig -Color MintGreen -InputText "PolicyGUID = $PolicyID" } + Write-Progress -Id 18 -Activity 'Complete.' -Completed } } catch { @@ -322,27 +155,15 @@ Function Remove-WDACConfig { } <# .SYNOPSIS - Removes Signed and unsigned deployed App Control for Business policies + Removes Signed deployed App Control for Business policies .LINK https://github.com/HotCakeX/Harden-Windows-Security/wiki/Remove-WDACConfig -.PARAMETER PolicyNames - Names of the deployed policies to be removed - https://stackoverflow.com/questions/76143006/how-to-prevent-powershell-validateset-argument-completer-from-suggesting-the-sam/76143269 - https://stackoverflow.com/questions/76267235/powershell-how-to-cross-reference-parameters-between-2-argument-completers -.PARAMETER PolicyIDs - IDs of the deployed policies to be removed - https://stackoverflow.com/questions/76143006/how-to-prevent-powershell-validateset-argument-completer-from-suggesting-the-sam/76143269 - https://stackoverflow.com/questions/76267235/powershell-how-to-cross-reference-parameters-between-2-argument-completers -.PARAMETER SignedBase - Remove Signed Base WDAC Policies .PARAMETER PolicyPaths Path to the XML policy file(s) of the deployed policies to be removed .PARAMETER CertCN Certificate common name to be used to sign the policy file(s) that are going to be removed in unsigned mode .PARAMETER SignToolPath Path to the SignTool.exe -.PARAMETER UnsignedOrSupplemental - Remove Unsigned deployed WDAC policies as well as Signed deployed Supplemental WDAC policies .PARAMETER Force Bypasses the confirmation prompt .INPUTS diff --git a/WDACConfig/WDACConfig Module Files/Public/MockConfigCIBootstrap.psm1 b/WDACConfig/WDACConfig Module Files/Public/MockConfigCIBootstrap.psm1 deleted file mode 100644 index c741fa593..000000000 --- a/WDACConfig/WDACConfig Module Files/Public/MockConfigCIBootstrap.psm1 +++ /dev/null @@ -1,22 +0,0 @@ -Function Invoke-MockConfigCIBootstrap { - <# -The reason behind this: -https://github.com/MicrosoftDocs/WDAC-Toolkit/pull/365 -https://github.com/MicrosoftDocs/WDAC-Toolkit/issues/362 - -Features: -Short-circuits the cmdlet and finishes in 2 seconds. -put in the preloader script so it only runs once in the runspace. -No output is shown whatsoever (warning, error etc.) -Any subsequent attempts to run New-CiPolicy cmdlet will work normally without any errors or warnings. -The path I chose exists in Windows by default, and it contains very few PEs, something that is required for that error to be produced. --PathToCatroot is used and set to the same path as -ScanPath, this combination causes the operation to gracefully end prematurely. -The XML file is never created. -XML file is created but then immediately deleted. Its file name is random to minimize name collisions. -#> - if ([System.IO.Directory]::Exists('C:\Program Files\Windows Defender\Offline')) { - [System.String]$RandomGUID = [System.Guid]::NewGuid().ToString() - New-CIPolicy -UserPEs -ScanPath 'C:\Program Files\Windows Defender\Offline' -Level hash -FilePath ".\$RandomGUID.xml" -NoShadowCopy -PathToCatroot 'C:\Program Files\Windows Defender\Offline' -WarningAction SilentlyContinue - Remove-Item -LiteralPath ".\$RandomGUID.xml" -Force - } -} \ No newline at end of file diff --git a/WDACConfig/WDACConfig Module Files/Shared/Test-KernelProtectedFiles.psm1 b/WDACConfig/WDACConfig Module Files/Shared/Test-KernelProtectedFiles.psm1 deleted file mode 100644 index e0afdfad6..000000000 --- a/WDACConfig/WDACConfig Module Files/Shared/Test-KernelProtectedFiles.psm1 +++ /dev/null @@ -1,45 +0,0 @@ -Function Test-KernelProtectedFiles { - <# - .SYNOPSIS - Detects kernel-protected files files such as the main executable of the games installed through Xbox app inside of the event logs - .DESCRIPTION - For these files, only Kernel can get their details such as hashes, it passes them to event viewer and we take them from event viewer logs - Any other attempts such as "Get-FileHash" or "Get-AuthenticodeSignature" fails and ConfigCI Module cmdlets totally ignore these files and do not create allow rules for them - .INPUTS - PSCustomObject[] - .OUTPUTS - PSCustomObject[] - #> - [CmdletBinding()] - Param( - [Parameter(Mandatory = $true)][PSCustomObject[]]$Logs - ) - Begin { - [WDACConfig.Logger]::Write('Test-KernelProtectedFiles: Checking for Kernel-Protected files') - } - Process { - # Looping through every existing file with .exe and .dll extensions to check if they are kernel protected - [PSCustomObject[]]$KernelProtectedFileLogs = foreach ($Log in $Logs) { - - if ( ([System.IO.Path]::GetExtension($Log.'Full Path') -in @('.exe', '.dll')) -and ([System.IO.Path]::Exists($Log.'Full Path'))) { - - try { - $Null = Get-FileHash -Path $Log.'Full Path' -ErrorAction Stop - } - # If the executable is protected, it will throw an exception and the module will continue to the next one - # Making sure only the right file is captured by narrowing down the error type. - # E.g., when get-filehash can't get a file's hash because its open by another program, the exception is different: System.IO.IOException - catch [System.UnauthorizedAccessException] { - $Log - } - catch { - [WDACConfig.Logger]::Write("Test-KernelProtectedFiles: An unexpected error occurred while checking the file: $($Log.'Full Path')") - } - } - } - } - End { - Return ($KernelProtectedFileLogs.Count -eq 0 ? $null : $KernelProtectedFileLogs) - } -} -Export-ModuleMember -Function 'Test-KernelProtectedFiles' \ No newline at end of file diff --git a/WDACConfig/WDACConfig Module Files/WDACConfig.psd1 b/WDACConfig/WDACConfig Module Files/WDACConfig.psd1 index 0cf99e93d..ea8eaa7aa 100644 --- a/WDACConfig/WDACConfig Module Files/WDACConfig.psd1 +++ b/WDACConfig/WDACConfig Module Files/WDACConfig.psd1 @@ -24,8 +24,6 @@ Please see the GitHub page for Full details and everything about the module: htt 🛡️ Here is the list of module's cmdlets -✔️ New-WDACConfig: https://github.com/HotCakeX/Harden-Windows-Security/wiki/New-WDACConfig - ✔️ New-SupplementalWDACConfig: https://github.com/HotCakeX/Harden-Windows-Security/wiki/New-SupplementalWDACConfig ✔️ Remove-WDACConfig: https://github.com/HotCakeX/Harden-Windows-Security/wiki/Remove-WDACConfig @@ -44,8 +42,7 @@ Please see the GitHub page for Full details and everything about the module: htt '@ - NestedModules = @('Core\New-WDACConfig.psm1', - 'Core\Remove-WDACConfig.psm1', + NestedModules = @('Core\Remove-WDACConfig.psm1', 'Core\Deploy-SignedWDACConfig.psm1', 'Core\Edit-SignedWDACConfig.psm1', 'Core\New-SupplementalWDACConfig.psm1', @@ -54,16 +51,13 @@ Please see the GitHub page for Full details and everything about the module: htt 'Core\Assert-WDACConfigIntegrity.psm1', 'Core\Test-CiPolicy.psm1') - FunctionsToExport = @('New-WDACConfig', - 'Remove-WDACConfig', + FunctionsToExport = @('Remove-WDACConfig', 'Deploy-SignedWDACConfig', 'Edit-SignedWDACConfig', 'New-SupplementalWDACConfig', 'New-DenyWDACConfig', 'New-KernelModeWDACConfig', - 'Assert-WDACConfigIntegrity', - 'Test-CiPolicy', - 'Update-WDACConfigPSModule') + 'Test-CiPolicy') PrivateData = @{ PSData = @{ diff --git a/WDACConfig/WDACConfig Module Files/WDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/WDACConfig.psm1 index d006a412e..309c86b3f 100644 --- a/WDACConfig/WDACConfig Module Files/WDACConfig.psm1 +++ b/WDACConfig/WDACConfig Module Files/WDACConfig.psm1 @@ -1,106 +1,7 @@ $global:ErrorActionPreference = 'Stop' -if (!$IsWindows) { - Throw [System.PlatformNotSupportedException] 'The WDACConfig module only runs on Windows operation systems. Download it from here: https://www.microsoft.com/software-download/windows11' -} - #Requires -RunAsAdministrator -Function Update-WDACConfigPSModule { - <# - .SYNOPSIS - Make sure the latest version of the module is installed and if not, automatically update it, clean up any old versions - .PARAMETER InvocationStatement - The command that was used to invoke the main function/cmdlet that invoked the Update-WDACConfigPSModule function, this is used to re-run the command after the module has been updated. - #> - [CmdletBinding()] - param( - [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] - [System.String]$InvocationStatement - ) - try { - [WDACConfig.Logger]::Write('Getting the last update check time') - [System.DateTime]$UserConfigDate = [WDACConfig.UserConfiguration]::Get().LastUpdateCheck - } - catch { - # If the User Config file doesn't exist then set this flag to perform online update check - [WDACConfig.Logger]::Write('No LastUpdateCheck was found in the user configurations, will perform online update check') - [System.Boolean]$PerformOnlineUpdateCheck = $true - # If it's the first time the module is running on a system, keep the auto-update enabled, unless user disables it. This maintains the same experience as before. - $null = [WDACConfig.UserConfiguration]::Set($null, $null, $null, $null, $null, $null, $null, $null , $null, $true) - } - - # If the AutoUpdateCheck is not set, set it to true. This only occurs when the user auto updates from a previous version that doesn't have AutoUpdateCheck context to a new one that utilizes it. - # This ensures that the auto update check experience remains familiar and the same unless user disables it explicitly. This step can be removed in the future once all users are moved to the new versions. - if ($null -eq [WDACConfig.UserConfiguration]::Get().AutoUpdateCheck) { - $null = [WDACConfig.UserConfiguration]::Set($null, $null, $null, $null, $null, $null, $null, $null , $null, $true) - } - - # Ensure these are run only if the User Config file exists and contains a date for last update check - if (!$PerformOnlineUpdateCheck) { - # Get the current time - [System.DateTime]$CurrentDateTime = Get-Date - # Calculate the minutes elapsed since the last online update check - [System.Int64]$TimeDiff = ($CurrentDateTime - $UserConfigDate).TotalMinutes - } - - # Only check for updates if the last attempt occurred more than 60 minutes ago And the AutoUpdateCheck is true, Or the User Config file for last update check doesn't exist - # This prevents the module from constantly doing an update check by fetching the version file from GitHub - if ((($TimeDiff -gt 60) -and ([WDACConfig.UserConfiguration]::Get().AutoUpdateCheck -eq $true)) -or $PerformOnlineUpdateCheck) { - - [WDACConfig.Logger]::Write("Performing online update check because the last update check was performed $($TimeDiff ?? [System.Char]::ConvertFromUtf32(8734)) minutes ago") - - [System.Version]$CurrentVersion = (Test-ModuleManifest -Path "$([WDACConfig.GlobalVars]::ModuleRootPath)\WDACConfig.psd1").Version.ToString() - try { - # First try the GitHub source - [System.Version]$LatestVersion = Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/HotCakeX/Harden-Windows-Security/main/WDACConfig/version.txt' -ProgressAction SilentlyContinue - } - catch { - try { - # If GitHub source is unavailable, use the Azure DevOps source which is the mirror of the GitHub repository - [System.Version]$LatestVersion = Invoke-RestMethod -Uri 'https://dev.azure.com/SpyNetGirl/011c178a-7b92-462b-bd23-2c014528a67e/_apis/git/repositories/5304fef0-07c0-4821-a613-79c01fb75657/items?path=/WDACConfig/version.txt' -ProgressAction SilentlyContinue - } - catch { - Throw [System.Security.VerificationException] 'Could not verify if the latest version of the module is installed, please check your Internet connection.' - } - } - - # Reset the last update timer to the current time - [WDACConfig.Logger]::Write('Resetting the last update timer to the current time') - $null = [WDACConfig.UserConfiguration]::Set($null, $null, $null, $null, $null, $null, $null, $(Get-Date) , $null, $null) - - if ($CurrentVersion -lt $LatestVersion) { - - Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,0,230))The currently installed module's version is $CurrentVersion while the latest version is $LatestVersion - Auto Updating the module...$($PSStyle.Reset)" - - # Remove the old module version from the current session - Remove-Module -Name 'WDACConfig' -Force -WarningAction SilentlyContinue - - try { - Uninstall-Module -Name 'WDACConfig' -AllVersions -Force -ErrorAction Ignore -WarningAction SilentlyContinue - } - catch {} - - Install-Module -Name 'WDACConfig' -RequiredVersion $LatestVersion -Scope AllUsers -Force - # Will not import the new module version in the current session because of the constant variables. New version is automatically imported when the main cmdlet is run in a new session. - - # Make sure the old version isn't run after update - Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(152,255,152))Update has been successful, running your command now$($PSStyle.Reset)" - - try { - # Try to re-run the command that invoked the Update-WDACConfigPSModule function in a new session after the module is updated. - pwsh.exe -NoProfile -NoLogo -NoExit -command $InvocationStatement - } - catch { - Throw 'Could not relaunch PowerShell after update. Please close and reopen PowerShell to run your command again.' - } - } - } - else { - [WDACConfig.Logger]::Write('Skipping online update check.') - } -} - # Unimportant actions that don't need to be terminating if they fail try { # Set PSReadline tab completion to complete menu for easier access to available parameters - Only for the current session @@ -110,20 +11,6 @@ try { } catch {} -<# -# This is required for the EKUs to work. -# Load all the DLLs in the PowerShell folder, providing .NET types for the module -# These types are required for the folder picker with multiple select options. Also the module manifest no longer handles assembly as it's not necessary anymore. -foreach ($Dll in (Convert-Path -Path ("$([psobject].Assembly.Location)\..\*.dll"))) { - try { - Add-Type -Path $Dll - } - catch {} -} -# Clear the Get-Error from Add-Type errors that are unnecessary -$Error.Clear() - -#> # Because we need it to construct Microsoft.Powershell.Commands.EnhancedKeyUsageProperty object for EKUs Add-Type -AssemblyName 'Microsoft.PowerShell.Security'