In Windows 1903, Microsoft has added support in Windows Defender Application Control (WDAC) for file path rules as a basis for whitelisting. This is how to create a WDAC policy with file path rules.
Background
Before file path support, implementing a WDAC policy for the desktop was almost impractical. A policy was based on a scan of files. For a normal desktop environment it would be difficult to maintain. With file path rules, we can allow executable files in the Program Files and Windows directories.
A previous post, Getting Started with WDAC, covered the tools, objective and an outline of the steps to create a working MDAC policy. This post covers the first step: to create a policy with file path rules. The Microsoft documentation on file path rules is confusing, and sometimes incorrect. Here’s how it really works.
File Path Rules
The Microsoft documentation on this was very confused. Since an update in November 2019, it is less confused, but still not at all clear.
Before Windows 1903 and the introduction of file path rules, a WDAC policy would normally be created first by performing a full scan of a standard desktop, with all production software installed. A WDAC file scan is performed by using the New-CIPolicy cmdlet with a Level parameter. This parameter specified at what level to identify a file. At a very low level, you could identify a file by hash. At higher levels you could identify it by filename, publisher, certificate provider, or various combinations. The optimum choice is another topic.
Windows 1903 adds a “FilePath” option for the Level parameter. What this does, in the scan, is to produce a list of every single file in the path, identified by fully qualified path name. This is NOT what we are looking for.
Windows 1903 also adds a new concept: a File Path Rule. This is different from a File Rule. It adds a path where executable files in the path are allowed, without a scan.
This rule is not enough to use on it’s own. We need to merge other policies and configure options before we can use it in production. One thing I don’t see documented: a File Path Rule does not authorise every type of executable file in a path. It does not authorise:
- MSI’s
- Scripts
- Drivers (SYS)
But these file path rules authorise any application executable (EXE or DLL) in the specified paths, so we don’t need to change the WDAC policy every time we install a new application.
Creating a policy with File Path Rules is a two stage process. First, we need to create the set of rules. Second, we need to add them to a policy.
Create Set of Rules
- To create a rule, we use the New-CIPolicyRule cmdlet.
- It is quite strange, but the documentation for New-CIPolicyRule does not refer to the new FilePathRule parameter. The PowerShell Get-Help does not mention it, either.
- The syntax for a single rule is:
New-CIPolicyRule -FilePathRule [path to allow]
. - We need to assign the rule to a variable:
$rules = New-CIPolicyRule [path to allow]
. - To add multiple File Path Rules to the rule set we use the syntax:
$rules += New-CIPolicyRule [path to allow]
to add each new path. This creates an array of rules.
Path Syntax
- For the path to allow, we need to use a wildcard to specify all files and folders under a root folder, e.g. “C:\Program Files\*”. Without a wildcard, the rule will allow only the specified folder.
- We can also use one of three custom variables (the documentation calls them macros): %WINDIR%, %SYSTEM32%, and %OSDRIVE%. These look like environment variables, but other variables are not supported. They also look like the variables supported by AppLocker, but WDAC does not support the same variables. Only these three are supported.
Example Rules
- To create a variable containing the set of rules for typical paths:
$rules = New-CIPolicyRule -FilePathRule "C:\Windows\*"
$rules += New-CIPolicyRule -FilePathRule “C:\Program Files\*”
$rules += New-CIPolicyRule -FilePathRule "C:\Program Files (x86)\*"
- We can then add this array of rules to a policy.
Add Rules to Policy
- To add the set of rules to a policy, we use the New-CIPolicy cmdlet.
- The syntax is:
New-CIPolicy –FilePath [path to save the new policy file] –Rules $rules
where $rules is the array of path rules we created. - My approach is to save this policy with a name like FilePathRules.xml so it can be re-used.
Set Options
There is one more step to make this policy usable as a standalone policy. This is to set the option to enable User-Mode Code Integrity (UMCI). In production we would add this later, but we need to add this if we want to test the file path rules policy on its own.
- By default, a new WDAC policy evaluates kernel-mode executable files only. This is no use to us in controlling user-mode applications. We need to enable Option 0: UMCI.
- There are two ways to do this, with the same result. One is to add the switch –UserPEs to the New-CIPolicy line, when we created the policy. Another is to use the Set-RuleOption cmdlet to set Option 0 in the policy. There is a difference between these two routes, but the practical result is the same in this case.
- This is where you need to use naming conventions to keep track of policies and versions. The Set-RuleOption cmdlet changes options in a policy file. It is hard to keep track of policies unless you rename them. In this example I would copy and rename the policy as FilePathRules_Option-0.xml, then set the option.
- To set the option:
Set-RuleOption –FilePath [path to the policy file] –Option 0
- You do not need to set other options yet. By default, a new policy is in Audit mode.
You can open the policy in Visual Studio Code (or an alternative XML editor) to see how it is structured. You can see that the policy is in Audit mode (by default) and UMCI is enabled.
<?xml version="1.0" encoding="utf-8"?>
<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy">
<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>
<Rule>
<Option>Enabled:UMCI</Option>
</Rule>
</Rules>
<!--EKUS-->
<EKUs />
<!--File Rules-->
<FileRules>
<Allow ID="ID_ALLOW_A_1" FriendlyName="C:\Windows\* FileRule" MinimumFileVersion="0.0.0.0" FilePath="C:\Windows\*" />
<Allow ID="ID_ALLOW_A_2" FriendlyName="C:\Program Files\* FileRule" MinimumFileVersion="0.0.0.0" FilePath="C:\Program Files\*" />
<Allow ID="ID_ALLOW_A_3" FriendlyName="C:\Program Files (x86)\* FileRule" MinimumFileVersion="0.0.0.0" FilePath="C:\Program Files (x86)\*" />
</FileRules>
<!--Signers-->
<Signers />
<!--Driver Signing Scenarios-->
<SigningScenarios>
<SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS_1" FriendlyName="Auto generated policy on 11-16-2019">
<ProductSigners />
</SigningScenario>
<SigningScenario Value="12" ID="ID_SIGNINGSCENARIO_WINDOWS" FriendlyName="Auto generated policy on 11-16-2019">
<ProductSigners>
<FileRulesRef>
<FileRuleRef RuleID="ID_ALLOW_A_1" />
<FileRuleRef RuleID="ID_ALLOW_A_2" />
<FileRuleRef RuleID="ID_ALLOW_A_3" />
</FileRulesRef>
</ProductSigners>
</SigningScenario>
</SigningScenarios>
<UpdatePolicySigners />
<CiSigners />
<HvciOptions>0</HvciOptions>
<PolicyTypeID>{A244370E-44C9-4C06-B551-F6016E563076}</PolicyTypeID>
</SiPolicy>