Script Enforcement and PowerShell Constrained Language Mode in WDAC App Control Policies¶
Introduction¶
This article explores some of the technical details of how to deploy an App Control policy that uses Script Enforcement and forces PowerShell to run in Constrained Language Mode. It expands aspects of this topic that are not covered enough in the official docs.
Tip
Check out these 2 documents from Microsoft for more info and basics: * PowerShell Constrained Language Mode
Below are the required steps to enable Script Enforcement and allow a PowerShell module to run in Constrained Language Mode, if its code meets the requirements of it.
Signing the PowerShell Module files¶
The PowerShell module that you intend to use in Constrained Language Mode must be completely signed, that means all of its .psm1
and .psd1
files must be signed by a code signing certificate.
Type of Certificate to Use to Sign the PowerShell Module Files¶
The Code Signing certificate you're going to use to sign the PowerShell module files with can be a self-signed certificate or a certificate from a trusted certification authority (CA).
Making the System Trust the Certificate¶
If the certificate you used to sign the PowerShell module files with is from a trusted certification authority (CA) and the root certificate of that CA exists in the "Trusted Root Certification Authorities" store of either the Local Machine or Current User certificate store, then you're good to go, but if the certificate is self-signed, you need to add the certificate's root certificate to either of those locations.
For instance, if you generated a Code Signing certificate from Windows Server Active Directory Certificate Services, and you used that certificate to sign the PowerShell module files, you need to export the root certificate containing the public key, to a .cer
file and then add it to one of the locations mentioned earlier. Adding the leaf certificate, which is the one you used to sign the module files with, to those locations, will not count and won't allow the signed PowerShell module to run in Constrained Language Mode.
Base Policy Requirements¶
The App Control base policy you're going to deploy must have 0 Enabled:UMCI
and it must not have the 11 Disabled:Script Enforcement
rule options.
How to Create a Supplemental Policy to Allow the Certificate(s)¶
The root certificate's details must be added as a Signer rule in an App Control policy in the User-Mode Signing Scenario.
Adding the Certificate's Signer rule to the Kernel-mode Signing Scenario does not allow the modules signed by that certificate to run, which is expected.
For better management, you should allow the certificate in a new supplemental policy.
Here is an example of a valid Supplemental policy that allows a root certificate as a signer.
<?xml version="1.0" encoding="utf-8"?>
<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy" PolicyType="Supplemental Policy">
<VersionEx>1.0.0.0</VersionEx>
<PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID>
<Rules>
<Rule>
<Option>Enabled:Unsigned System Integrity Policy</Option>
</Rule>
</Rules>
<EKUs />
<FileRules />
<Signers>
<Signer ID="ID_SIGNER_S_1_1" Name="Root Certificate">
<CertRoot Type="TBS" Value="e3fbf9a3dc3022eab22b5e961bc6fee45782ae8aaed1d8402f2101a5f393db876444ef1d0e302f03b64463bae816f701cc5cda41068a8bf1954a0cd262eb9d6f" />
</Signer>
</Signers>
<SigningScenarios>
<SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS_1" FriendlyName="Auto generated policy on 04-26-2024">
<ProductSigners />
</SigningScenario>
<SigningScenario Value="12" ID="ID_SIGNINGSCENARIO_WINDOWS" FriendlyName="Auto generated policy on 04-26-2024">
<ProductSigners>
<AllowedSigners>
<AllowedSigner SignerId="ID_SIGNER_S_1_1" />
</AllowedSigners>
</ProductSigners>
</SigningScenario>
</SigningScenarios>
<UpdatePolicySigners />
<CiSigners>
<CiSigner SignerId="ID_SIGNER_S_1_1" />
</CiSigners>
<HvciOptions>2</HvciOptions>
<BasePolicyID>{7558D4BF-69E3-45CA-9C52-915E48A7C50E}</BasePolicyID>
<PolicyID>{32C551AF-C243-477A-9955-D37EDF435414}</PolicyID>
<Settings>
<Setting Provider="PolicyInfo" Key="Information" ValueName="Name">
<Value>
<String>Supplemental Policy</String>
</Value>
</Setting>
</Settings>
</SiPolicy>
As you can see, we need the TBS Hash value of the root certificate.
Use the WDACConfig Module to Automatically Allow Certificates¶
You can use the WDACConfig module to create a supplemental policy that allows the certificates you select to be allowed by App Control. To do that, you can use the following command:
New-SupplementalWDACConfig -Certificates -CertificatePaths "certificate.cer" -SuppPolicyName '<Certificate Name>' -PolicyPath "<Path to Base policy XML file>"
More info regarding the cmdlet is available here
A manual way to get the TBS Hash value of a certificate is using the following command, which also works for signed files and will show the details of the certificates in the chain as well.
PowerShell Engine Behavior¶
When an App Control policy with script enforcement is deployed and you try to import an unauthorized module, you might see different errors. For instance, you might see an error about classes not being allowed or other reasons for a module not being able to load, but in essence, the PowerShell engine is trying to load the module in Constrained Language Mode and the module is failing to meet the requirements, most likely because:
- The module you're trying to load is not signed
- The module you're trying to load is signed but the certificate's root is not trusted by the system
- The module you're trying to load is signed but at least one of its files is tampered with and has a hash mismatch. Even adding a single space on an empty line causes hash mismatch, which is expected.
Important
WDACConfig module is currently not compatible with constrained language mode due to using advanced PowerShell features that are not allowed in that mode.