Hey folks, in the last 8 years I have been helpimg a lot of companies to deploy what is now known as BeyondTrust Privilege Management for Windows (PMfW) and was formerly known as Avecto Defendpoint or Privilege Guard. In this post I want to show you how to harden your Group Policy based configurations against unwanted access.
Problem
The PMfW Agent uses a human readable XML configuration file which in most cases is deployed via Group Policy. The locally cached configuration is protected against unelevated access with NTFS permissions, but this does not apply to the default configuration of the Group Policy. The XML configuration file stored in the policy directory in the SYSVOL-Share of the Domain is readable for every authenticated user. A clever user or an attacker could use the read-only access to find a loophole in the configuration to elevate processes he is not allowed to or to gain full admin access to the computer. It is very unlikely, due to the Anti-Tampering mechanisms implemented into the Agent, but not impossible.
As you can see in the picture above the access to the local policy cache in %ProgramData%\Avecto is prohibited (1). But the user can load the XML configuration file from the GPO folder in the SYSVOL-Share and for example look up the application definitions of an Application Group (2).
Solution
If you are only using computer policies for PMfW, which is quite common, the solution is easy. Just replace the Authenticated Users entry in the Security Filtering of the relevant Group Policies with the Domain Computers group.
Changing the Security Filtering of a GPO is the same as setting NTFS permissions on the folder of the policy in the SYSVOL-Share. As you can see in the picture below accessing the GPO from the network is no longer possible for the user. However, the System Account of the device, which is automatically part of the Domain Computers group of the Active Directory Domain, is still able to access it during policy updates.
I tried to create images of Windows 7 and Windows 10 (1607, 1703, 1709) with a SCCM Build and Capture Task Sequence. I deployed the January Windows Updates to the imaging clients so that the images should include the fixes for the Meltdown and Spectre vulnerabilities. But unfortunately this did not work. The reason is that the Antivirus compatibility Registrykey mentioned in this article had not been set before the updates were installed.
Update: After testing Build and Capture of Windows 10 with MDT I have added the necessary steps to the article. Update 2: Thanks to @manelrodero for pointing out that a reboot is not required between setting the key and the Install Update step. Update 3: Microsoft announced that this is not longer necessary beginning with the Cumulative Update 03-2018
Solution
You just have to add the registry in your Build and Capture sequence right before the update step performs the update scan.
SCCM
Add a Run Command Line Step to your Build and Capture Task Sequence before the Install Updates step containing the following line
2. Make sure that the box Evaluate software updates from cached scan results is not checked in the first Install Updates step.
MDT
Add a Run Command Line Step to your Build and Capture Task Sequence before the Windows Update (Pre-Application Installation) step containing the following line
How to align the rollout of the Microsoft Security Baselines Group Policies with the Windows 10 servicing model
Update: Added WMI-Filter for Windows 10/11 21H2
The Problem
Microsoft released security baselines in form of a Group Policy backup set for its operating systems in the recent years. Many enterprises are using these baselines as a security foundation. Enterprises have to adopt new settings on a lot higher frequency with the change of the servicing model and the additional release speed of Windows 10. New security baselines are now available with every release of Windows 10 every 6 months.
Note: If you want to learn more about Windows as a Service look here
The nature of Group Policies where small changes can have a huge impact on your client landscape made it necessary for enterprises to build solid change processes around them to document and verify any change. These processes are normally slow and inflexible which makes it very hard to combine them with the fast speed of new security baselines.
Another challenge for enterprises is the complexity of testing each baseline setting against a variety of several hundred applications. The traditional way was to do this in an OS upgrade project.
First, the complete baseline was activated and then redefined them during application testing. But with Windows 10 branch upgrades there are no upgrade projects and to validate a baseline with over 50 changed settings against your client landscape on a regular basis is not a feasible scenario for many companies.
Solution
In order to help the security settings keeping track with the speed of the baseline releases I am using a layered approach.
What does “layered” mean?
I distinguish between two sorts of Group Policies, the Baseline-GPOs and Custom-GPOs. The main difference between these two are that Baseline-GPOs are not changed by me at all. Every setting which differs from the baselines is made in a Custom-GPO.
Another difference between the Baseline-GPOs and the Custom-GPOs is that the baselines are filtered via WMI-Filter to the corresponding Build version of Windows 10. In contrast the Custom-GPOs are filtered to apply on all Windows 10 clients.
The WMI-Filters
We need a WMI Filter for Windows 10 and for every active Build currently used. Microsoft supports the last three Build versions so you should have a maximum of three (maybe four) active builds and WMI-Filters.
Windows 10 Select * from Win32_OperatingSystem WHERE Version LIKE "10.0%" and ProductType = "1"
Windows 10 1607 Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.14393%" and ProductType = "1"
Windows 10 1703 Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.15063%" and ProductType = "1"
Windows 10 1709 Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.16299%" and ProductType = "1"
Windows 10 1803 Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.17134%" and ProductType = "1"
Windows 10 1809 Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.17763%" and ProductType = "1"
Windows 10 1903 Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.18362%" and ProductType = "1"
Windows 10 1909 Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.18363%" and ProductType = "1"
Windows 10 2004 Select Version,ProductType from Win32_OperatingSystem WHERE Version LIKE "10.0.19041%" and ProductType = "1"
Windows 10 20H2 Select Version,ProductType from Win32_OperatingSystem WHERE Version LIKE "10.0.19042%" and ProductType = "1"
Windows 10 21H1 Select Version,ProductType from Win32_OperatingSystem WHERE Version LIKE "10.0.19043%" and ProductType = "1"
Windows 10 21H2 Select Version,ProductType from Win32_OperatingSystem WHERE Version LIKE "10.0.19044%" and ProductType = "1"
Windows 11 21H2 Select Version,ProductType from Win32_OperatingSystem WHERE Version LIKE "10.0.22000%" and ProductType = "1"
The WMI-Filters contain a query about the Windows Version and the ProductType. The latter is defined as follows
1 – Client Computer
2 – Domain Controller
3 – Member Server
With these filters we make sure that the Windows 10 GPOs will only apply on Windows 10 client devices (of the defined Build Version).
I have written a short PowerShell function to import all baselines at once. You just have to export them in one folder and add ‘-Version’ (e.g. ‘-1709’) to the folder name.
Then change the ExportPath to your folder path in the following script and execute it. You will need to import the Group Policy WMI filter cmdlet module prior to successfully running the script.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The script will create or update the GPOs and name them as you can see in the picture below. Additionally it will set the corresponding WMI-Filter if it includes the Build number (e.g. 1709)
Create the Custom-GPOs
You can create a Custom-GPO for each corresponding type of baseline (Defender, Computer, …) or as I did in the example below just one Custom-GPO for all baselines.
Linking the GPOs
After having everything in place we can now link the GPOs to the OU(s). In the next picture, you can see the GPO Link order of my Windows 10 OU.
The Custom-GPOs have to be linked with a lower order number or to a Sub-OU to apply at last and overwrite the Baseline-GPO if needed.
Example
A common baseline setting which many of my customers perceive as too strict is the UAC configuration in the baseline for Standard Users which is set to Automatically deny elevation requests.
In the Custom-GPO I changed that setting to Prompt for credentials on the Secure Desktop
As you can see in the screenshot of a Group Policy Result of a Windows 10 1709 client the baselines are applied as described and the UAC setting is overwritten by the Custom-GPO.
What is the advantage?
Instead of integrating and validating every single new baseline setting you only have to import the new Baseline-GPOs and the corresponding WMI-Filter.
Microsoft released the baselines when the Windows 10 Build became available in the Semi-Annual-Channel (formerly known as Current Branch for Business). With the release of the Fall Creators Update the final version of baselines even became available with the release to the Semi-Annual-Channel(targeted) (formerly known as Current Branch). So, it is very unlikely that you have deployed a large number of clients with the newest build before the baselines are available.
Therefore, when you start to upgrade your clients to the newest build you will automatically test the new baselines along with the new OS Version without an effect on your productive clients.
If you have to change a setting in your Custom-GPOs because of the new baselines it is very unlikely that this setting will have a negative effect on your existing clients. Because it is either a new setting which isn’t applicable for the old builds or it isn’t set in the old baselines. If the latter is the case you will set it back to the default value in most cases which already worked.
It also makes it easier to find out which of your settings differ from the baselines. You do not have to compare different GPOs with the baselines. You only have to look at your Custom-GPOs or in a Group Policy Result Report which of the settings are applied from a Custom-GPO.