Blocking desktop apps with M365 E5

Background

I recently came across a request from a customer to block specific applications on their Windows clients. More specifically, the requirements were as follows:

  1. We want to be able to block Java being installed on Windows clients
  2. We also want to block Java being used (if it’s already installed)
  3. We need to be able to make exceptions to the rule (some users still need it)
  4. We need to be able to test it first in audit mode (minimize disruption)
  5. We need to be able to monitor when Java is being blocked
  6. The solution should be easy to maintain
  7. The solution does not need to be perfect, i.e. it is targeted for casual, mainly non-technical users.

The customer had the following setup:

  • Microsoft 365 E5 licenses for all users
  • Microsoft Defender for Endpoint (MDE) deployed to all Windows clients
  • Clients have Windows 10 (or Windows 11) and are hybrid joined (Entra ID + Active Directory)
  • Most clients are enrolled into Intune, but the rest are managed via Group Policies (and SCCM)
  • Scale: thousands of users (and Windows clients)

So the question is: what is the best solution?

A few options come to mind, and those are explored in the sections below.

Option 1: Use MDE file indicators

The first idea that came to mind was to use the file indicators in Microsoft Defender for Endpoint (MDE). In MDE, you can define file hashes or signing certificates for files that you want to block in your environment.

File hashes are out of the question, because it would require constantly updating the list with different versions of Java installers, executables, etc. (requirement 6).

Signing certificates would be more promising, because presumably they don’t change as often. However, the issue with them is that strangely you cannot add them in audit mode (you can do that for file hashes).

There is actually another reason why MDE indicators are out of the question: the indicators are targeted to machine groups, and a client can only belong to one machine group. So you cannot really make any exceptions. Especially, if you want to add another application to the list of blocked application (which has different exclusions).

Therefore, option 1 is out.

Option 2: Use AppLocker

AppLocker has been there forever. I remember using it 10 years ago for whitelisting applications in a very restricted server environment. And I remember it was a pain to manage. While AppLocker is still part of Windows 10 and 11, this statement from Microsoft is quite telling:

Generally, it’s recommended that customers, who are able to implement application control using Windows Defender Application Control rather than AppLocker, do so. WDAC is undergoing continual improvements, and is getting added support from Microsoft management platforms. Although AppLocker continues to receive security fixes, it isn’t getting new feature improvements. [source]

Because the client base consisted of only Windows 10/11 clients, WDAC started to look like a much better approach.

So option 2 is out as well.

Option 3: Use Windows Defender Application Control (WDAC)

There’s an abundance of documentation about WDAC, so I’m not going to explain what it is. Instead, I will focus on the practical setup, and conclude by reflecting how well it satisfies the original requirements.

WDAC policies and rules

WDAC operates through policies, and each policy consists of rules (what is allowed, and what is not allowed). What we want to achieve, is to create a WDAC policy that has the following rules:

  1. Block Java
  2. Allow everything else

Each rule has a level, which can be one of the following: None, Hash, FileName, FilePath, SignedVersion, PFN, Publisher, FilePublisher, LeafCertificate, PcaCertificate, RootCertificate, WHQL, WHQLPublisher, WHQLFilePublisher [source].

Now we have several options to choose from (as opposed to just file hash or signing certificate). The question is: which of these options to use?

I started first inspecting different versions of Java installers, and compared them also to some random Oracle software I downloaded. We want to block the Java installers with as few rules as possible, without blocking anything else. And we want to have as few rules as possible, because we want to make it easy to maintain (requirement 6 again).

When comparing the JRE and JDK installers, we can already see that they are using a different certificate for the signature:

I also compared with some older versions of JRE, and it used a different certificate as well (which is natural, as they only have limited validity). The issuer for all these signing certificates is DigiCert, so there is no Oracle sub CA in between that we could use either. So, I decided to try other file properties.

When looking at the file properties, I noticed that the File description seems promising. There are basically just two variations in these Java files as you can see from the picture: Java(TM) Platform SE binary and Java Platform SE binary. The file description is clearly different for the Oracle Client for Microsoft Tools setup, which is expected, as the description for the Java files clearly points into Java platform. And as this is a property that is written when the binary is compiled, you cannot trivially change it. You can change it with developer tools, but that doesn’t really worry us (requirement 7). The Product name property could be useful as well. However, it has the version number in it, so it can only be useful if we can use wildcards with it.

The next question is, can we use the File description property in WDAC rules? Yes, we can, by using the -SpecificFileNameLevel parameter:

Next, let’s see how we can create the policy.

Btw: I also tried the product name, but it doesn’t work with wild cards (wild cards work only with file path).

Creating the WDAC policy

The easiest way to create the WDAC policy is by using PowerShell. Windows 10 (and later) includes the ConfigCI module, which contains all the commandlets needed for policy creation. There is also a UI tool that can be used, but I found it a bit confusing. And you really don’t want to make mistakes in the policy creation, because otherwise you may end up blocking all sorts of applications you didn’t want to block.

I used the following script to create the policy. It’s based on the following Microsoft documentation:

# Name of the file where the policy will be stored

$PolicyFile = ".\DenyJavaPolicyFileDescription.xml"

$DenyRules = @()

# Add deny rule based on the file description on JRE installer

$DenyRules += New-CIPolicyRule -Level FileName -SpecificFileNameLevel FileDescription -DriverFilePath ".\JavaSetup8u381.exe" -Deny

# Add deny rule based on the file description on JDK installer

$DenyRules += New-CIPolicyRule -Level FileName -SpecificFileNameLevel FileDescription -DriverFilePath ".\jdk-21_windows-x64_bin.exe" -Deny

# Allow all policy. Without this you may end up blocking all applications

$AllowAllPolicy = $Env:windir + "\schemas\CodeIntegrity\ExamplePolicies\AllowAll.xml"

# Merge the allow all policy with our rules and reset policy ID

Merge-CIPolicy -PolicyPaths $AllowAllPolicy -OutputFilePath $PolicyFile  -Rules $DenyRules

Set-CiPolicyIdInfo -FilePath $PolicyFile -PolicyName "Deny Java based on File Description" -ResetPolicyID

## To use this policy in audit mode, uncomment the following line

# Set-RuleOption -FilePath $DenyPolicy -Option 3

## Convert the policy to binary format (needed for Intune/GPO)

$WDACPolicyXMLFile = $PolicyFile  
[xml]$WDACPolicy = Get-Content -Path $WDACPolicyXMLFile
if (($WDACPolicy.SiPolicy.PolicyID) -ne $null) ## Multiple policy format (For Windows builds 1903+ only, including Server 2022)
{
     $PolicyID = $WDACPolicy.SiPolicy.PolicyID
     $PolicyBinary = $PolicyID+".cip"
}
else ## Single policy format (Windows Server 2016 and 2019, and Windows 10 1809 LTSC)
{
     $PolicyBinary = "SiPolicy.p7b"
}
 
 ## Export file

 ConvertFrom-CIPolicy -XmlFilePath $WDACPolicyXMLFile -BinaryFilePath ".\$PolicyBinary"


Deploying the WDAC policy

Once you’ve created the policy, you can deploy it to the clients. When you’re using Intune or Group Policies, you need to use the binary version of the policy (in this example I’m using Intune). Note, that you need to rename the binary file extension from .p7b -> .bin. There is good documentation by Microsoft available about the deployment, but I’ll summarize the process here.

You deploy the policy via Intune using a custom configuration profile.

In the Configuration settings section, add the following information:

  • Name and description
  • OMA-URI, which is ./Vendor/MSFT/ApplicationControl/Policies/<Policy GUID>/Policy (in my case ./Vendor/MSFT/ApplicationControl/Policies/F5575FD7-6684-43E7-9D2D-D809886F97BA/Policy). The GUID can be found in the file name of the binary policy file created by the script.
  • Data type: Base64 (file)

Upload the file (remember to change the extension to .bin).

Use the Assignments section to target the policy. I recommend using device groups, so you can easily deploy the policy in phases.

Once you’ve finalized the wizard, you just need to wait for it to be applied (or you can trigger Intune sync manually from the clients).

Verifying that the policy works

Once the policy has been deployed, it’s time to verify that it works. We’ll try three different Java installers, and one Oracle installer that has nothing to do with Java.

Latest JRE installer is blocked:

Latest JDK installer is blocked:

A very old version of JDK is blocked:

Random Oracle application is allowed, as it should be:

So it does indeed work! The policy also blocks the executables for an installed version of java (such as java.exe and javaw.exe).

Monitoring the policy, and using the audit mode

We now have a policy that works in block mode. But how can we use it in audit mode (requirement 4)? And how can we monitor when Java is being blocked (requirement 5)?

To use the policy in audit mode, you need to use the following command when creating the policy (this is also included in the comments of the script above).

Set-RuleOption -FilePath $DenyPolicy -Option 3 

The audit mode events are logged into Windows event log under Applications and Services logs > Microsoft > Windows > CodeIntegrity. Obviously, we want to be able to monitor the policy impact centrally, so this is not very useful, unless we have a means to centrally gather the event logs.

But luckily, we do have Microsoft Defender for Endpoint, and it actually gathers the WDAC policy events. MDE logs both the audit events, and block events, and they are logged in the DeviceEvents table.

Here’s a KQL query that can be used in Advanced Hunting to show all audit and block events from WDAC during the last 24 hours:

DeviceEvents
| where Timestamp > ago(1d)
| where ActionType == "AppControlCodeIntegrityPolicyBlocked" or ActionType == "AppControlCodeIntegrityPolicyAudited"
| extend WDACPolicyDetails = todynamic(AdditionalFields)
| extend WDACPolicyName = WDACPolicyDetails.PolicyName
| where WDACPolicyName startswith "Deny Java Policy File Description"
| project Timestamp, DeviceName, FileName, ActionType, WDACPolicyName, FolderPath, InitiatingProcessFolderPath, InitiatingProcessAccountUpn, InitiatingProcessVersionInfoFileDescription, SHA256
| sort by Timestamp desc

In my case, I had the audit policy and block policy deployed at the same time, but targeting different clients. The results look like this:

If you’re using Microsoft Sentinel (and you’re sending device events there), you could even build a nice workbook to visualize this data. But that wasn’t really in the scope here. The key thing is that we are able to centrally monitor whenever an application gets blocked by our policy (or would be blocked, if we’re using the audit mode).

Conclusion

Finally, I wanted to conclude, how well this solution fulfills the original requirements:

1. We want to be able to block Java being installed on Windows clients

Yes, we can block Java installers (old and new).

2. We also want to block Java being used (if it’s already installed)

Yes, our solution also blocks installed Java executables (and DLLs).

3. We need to be able to make exceptions to the rule (some users still need it)

Yes, we can use include and exclude rules in the Intune assignments for the configuration profile.

4. We need to be able to test it first in audit mode (minimize disruption)

Yes, we can use the policy in audit mode, and use MDE to monitor the possible impact.

5. We need to be able to monitor when Java is being blocked

Yes, we can see the block events in MDE.

6. The solution should be easy to maintain

Yes and no, I’m a bit mixed with this one. By using the file description property, we managed to create a policy that probably does not need to be changed very often (only in the case some future Java version uses a different file description). So this is definitely positive, compared to using file hashes or signing certificates. However, the policy creation itself is a bit of a hack to be honest. You need to be really careful when creating the policy (remember to include the allow all rules), otherwise you may end up blocking something you didn’t want to block. And this could have very disruptive results. I don’t like the fact that you need to use the binary version of the policy in Intune, because you basically have no way of seeing the policy contents from the configuration profile. It would be really nice if there was a ready-made template that you can use for these custom rules, or that you could at least use the XML file instead.

7. The solution does not need to be perfect, i.e. it is targeted for casual, mainly non-technical users.

Yes, the file description property is not trivial to change, so this solution meets the requirement.

That’s it! Any comments and feedback are more than welcome. And if you have other solutions that I didn’t consider, please let me know :).

5 thoughts on “Blocking desktop apps with M365 E5

  1. Hi Markus
    First of all, thank you for this very well written and interesting post!
    As i seen in the last days, there is a new way (App Control for Business (Preview)) to rollout those WDAC policies.
    In my honest opinion the only better thing is the rollout of the trusted installer and the management of the applications from this trusted installer.
    Did you already tested this new preview feature and if yes, what is your opinion?

    Like

    1. Hi Ben,
      Thanks for your comment! I had not heard of App Control for Business, but seems like a very interesting feature building on top of WDAC. Although using the trusted installer would be probably the way to go if you start from scratch, I’m not sure if it would solve the issue if you want to simply block individual applications (in this case Java) that have already been installed.

      Based on the Microsoft documentation, I’m not quite sure if the App Control for Business policies are exactly the same as WDAC policies. If that is the case, then it allows to deploy WDAC policies in XML format, which would be a big benefit over using WDAC directly.

      Like

      1. App Control for Business could be the way to go, unfortunately at least as of today a small issue still exist:

        You have to deploy the App Control for Business Policies BEFORE the implementation of any applications that you want to control. For example:

        7Zip is as of today installed, App Control Policy deployed and blocked 7Zip from running. The already installed instance of 7zip is not affected.
        If 7zip get uninstall and installed again, App Control Policy will block the app..

        🙂

        Liked by 1 person

  2. Thank you for the further explanation!
    Indeed, this is a big issue at this moment. I mean if you want the other thing and only allow apps from trusted installer in an existing enterprise environment. If i understood correctly, i have to reinstall all the apps or use the “old” way over device policies. Seems like fun ;-).
    I hope Microsoft will find a way to tag applications which are deployed already over intune AFTER the activation of an application control for business.
    Otherwise the implementation of WDAC is – at least for me – a little bit tricky.

    Like

Leave a comment