Saturday 16 November 2019

The Internals of AppLocker - Part 1 - Overview and Setup

This is part 1 in a short series on the internals of AppLocker (AL). Part 2 is here, part 3 here and part 4 here.

AppLocker (AL) is a feature added to Windows 7 Enterprise and above as a more comprehensive application white-listing solution over the older Software Restriction Policies (SRP). When configured it's capable of blocking the creation of processes using various different rules, such as the application path as well as optionally blocking DLLs, script code, MSI installers etc. Technology wise it's slowly being replaced by the more robust Windows Defender Application Control (WDAC) which was born out of User Mode Code Integrity (UMCI), however at the moment AL is still easier to configure in an enterprise environment. It's considered a "Defense in Depth" feature according to MSRC's security servicing criteria so unless you find a bug which gives you EoP or RCE it's not something Microsoft will fix with a security bulletin.

It's common to find documentation on configuring AL, even in bypassing it (see for example Oddvar Moe's case study series from his website) however the inner workings are usually harder to find. Some examples of documentation which go some way towards documenting AL internals that I could find are:
However even these articles don't really give the full details. Therefore, I thought I'd dig a little deeper into some of the inner workings of AL, specifically focusing on the relationship between user access tokens and the applied rules. I'm not going to talk about configuration (outside of a quick setup for demonstration purposes) and I'm not really going to talk about bypasses. However, I will also pass on some dumb tricks you can do with an AL configured system which might be "bypass-like". Also note that this is documenting the behavior on Windows 10 1909 Enterprise. The internals might and almost certainly are different on other versions of Windows.

Let's start with a basic overview of the various components and give a super quick setup guide for a basic AL enabled Windows 10 1909 Enterprise installation so that we can try things out in subsequent parts.

Component Overview

AL uses a combination of a kernel driver (APPID.SYS) and user mode service (APPIDSVC). The introduction of kernel code is what distinguishes it from the old SRP which was entirely enforced in user mode, and so wasn't too difficult to bypass. The kernel driver's primary role is to handle blocking process creation through a Process Notification Callback as well as provide some general services. The user mode service on the other hand is more of a helper to do things which are difficult or impractical in the kernel, such as comprehensive code signature verification. That said looking at the implementation I think the majority could be done entirely in kernel mode considering that's what the Code Integrity (CI) module already does.

For DLL, Script and MSI enforcement various user-mode components access the SAFER APIs to determine whether code should run. The SAFER APIs might then call into the kernel driver or into the service over RPC depending on what it needs to do. I've summarized the various links in the following diagram.

The various interactions between components in AppLocker.

Setting up a Test System

I started by installing Windows 10 1909 Enterprise from an MSDN ISO. If you don't have MSDN access you get a trial Dev Environment VM from Microsoft which runs Windows 10 Enterprise. At the time of writing it's only 1903, but that's probably good enough, you should even be able to update to 1909 if you so desire. Then follow the next steps:
  1. Startup the VM and login as an administrator, then run an admin PowerShell console.
  2. Download the Default AppLocker Policy file from GitHub and save it as policy.xml.
  3. Run the PowerShell command "Set-AppLockerPolicy -XmlPolicy policy.xml".
  4. Run the command "sc.exe config appidsvc start= auto".
  5. Reboot the VM. 
This will install a simple default policy then enables the Application Identity Service. The policy is as follows:
  • EXE Rules
    • Allow Everyone group access to run any executable under %WINDIR% and %PROGRAMFILES%.
    • Allow Administrators group to run any executable from anywhere.
  • DLL Rules
    • Allow Everyone group access to load any DLL under %WINDIR% and %PROGRAMFILES%.
    • Allow Administrators group to load a DLL from anywhere.
  • APPX Rules (Packages Applications, think store applications)
    • Allow Everyone to load any signed, packaged application .
Of course these rules are terrible and no one should actually use them, I've just presented them for the purposes of this blog post series.

Where is the policy configuration stored? There's some data in the registry, but the core of the policy configuration is stored the directory %WINDIR%\SYSTEM32\APPLOCKER, separated by type. For example the executable configuration is in EXE.APPLOCKER, the other names should be self explanatory. When the files in this directory are modified a call is made to the driver to reload the policy configuration. If we take a look at one of these files in a hex editor you'll find they're nothing like the XML policy we put in (as shown below), we'll come back to what these files actually contain in part 3 of this blog series.

Hex dump of the Exe.Applocker file which shows only binary data, no XML.

Once you reboot the VM the service will be running and AL will now be enforced. If you login with the administrator again and copy an executable to their Desktop folder, a location not allowed by policy, and run the executable you'll find, it works... You might think this makes sense generally, the user is an administrator which should be allowed to execute everything from anywhere, however the default administrator is a UAC split token admin, so the default "user" wouldn't have the Administrators group and so shouldn't be allowed to run code from anywhere? We'll get back to why this works in part 3.

To check AL is working create a new user (say using the New-LocalUser PowerShell command) and do not assign them to the local administrators group. Login as the new user and try copying and running the executable on the desktop again. You should be greeted with a suitable error dialog.

AppLocker error showing executable has been blocked from running.

It should be noted that even if you just enable the APPID driver AL won't be enforced, the service needs to be running for everything to be correctly enabled. You might assume you can just disable the service as an administrator and turn off AL trivially? Well about that...

C:\> sc.exe config appidsvc start= demand
[SC] ChangeServiceConfig FAILED 5:

Access is denied.

Seems you can't reconfigure the service back to demand start (its initial start mode) once you've auto started it. The answer to why you're given access denied is simple:

C:\> sc.exe qprotection appidsvc
[SC] QueryServiceConfig2 SUCCESS
SERVICE appidsvc PROTECTION LEVEL: WINDOWS LIGHT.

On Windows 10 (I've not checked 8.1) the AppID service runs as PPL. This means the Service Control Manager (SCM) prevents "normal" administrators from tampering with the service, such as disabling it or stopping it. I really don't see why Microsoft did this, there's SO many different ways to compromise AppLocker's function as an administrator it's not funny, disabling the service should presumably be the least of your worries. Oh well, of course in this case if you really must disable the service at run time you can use the Task Scheduler trick I showed in September to run some commands as TrustedInstaller, which happens to be a backdoor into the SCM. Try running the following PowerShell script as an administrator:

That's all for now, in part 2 we'll dig into how the Executable enforcement works under the hood.