Fun with AppContainers

AppContainers are the sanboxes typically used to run UWP processes (also known as metro, store, modern…). A process within an AppContainer runs with an Integrity Level of low, which effectively means it has no access to almost everything, as the default integrity level of objects (such as files) is Medium. This means code running inside an AppContainer can’t do any sigtnificant damage because of that lack of access. Furthermore, from an Object Manager perspective, named objects created by an AppContainer are stored under its own object manager directory, based on an identifier known as AppContainer SID. This means one AppContainer cannot interfere with another’s objects.

For example, if a process not in an AppContainer creates a mutex named “abc”, its full name is really “\Sessions\1\BaseNamedObjects\abc” (assuming the process runs in session 1). On the other hand, if AppContainer A creates a mutex named “abc”, its full name is something like “\Sessions\1\AppContainerNamedObjects\S-1-15-2-466767348-3739614953-2700836392-1801644223-4227750657-1087833535-2488631167\abc”, meaning it can nevr interfere with another AppContainer or any process running outside of an AppContainer.

Although AppContainers were created specifically for store apps, theye can also be used to execute “normal” applications, providing the same level of security and isolation. Let’s see how to do that.

First, we need to create the AppContainer and obtain an AppContainer SID. This SID is based on a hash of the container name. In the UWP world, this name is made up of the application package and the 13 digits of the signer’s hash. For normal applications, we can select any string; selecting the same string would yield the same SID – which means we can actually use it to “bundle” several processes into the same AppContainer.

The first step is to create an AppContainer profile (error handling ommitted):

PSID appContainerSid;
::CreateAppContainerProfile(containerName, containerName, containerName, nullptr, 0, &appContainerSid);

The containerName argument is the important one. If the function fails, it probably means the container profile exists already. In that case, we need to extract the SID from the existing profile:

::DeriveAppContainerSidFromAppContainerName(containerName, &appContainerSid);

The next step is prepare for process creation. The absolute minimum is to initialize a process attribute list with a SECURITY_CAPABILITIES structure to indicate we want the process to be created inside an AppContainer. As part of this, we can specify capabilities this AppContainer should have, such as internet access, access to the documents library and any other capabilities as defined by the Windows Runtime:

STARTUPINFOEX si = { sizeof(si) };
PROCESS_INFORMATION pi;
SIZE_T size;
SECURITY_CAPABILITIES sc = { 0 };
sc.AppContainerSid = appContainerSid;

::InitializeProcThreadAttributeList(nullptr, 1, 0, &size); 
auto buffer = std::make_unique<BYTE[]>(size); 
si.lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(buffer.get()); 
::InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size)); 
::UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES, &sc, sizeof(sc), nullptr, nullptr));

We specified zero capabilities for now. Now we’re ready to create the process:

::CreateProcess(nullptr, exePath, nullptr, nullptr, FALSE,
	EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr, 
        (LPSTARTUPINFO)&si, &pi);

We can try this with the usual first victim, Notepad. Notepad launches and everyhing seems OK. However, if we try to open almost any file by using Notepad’s File/Open menu item, we’ll see that notepad has no access to usual things, such as “my documents” or “my pictures”. This is because it’s runnign with Low Integrity Level and files are defaulted to Medium integrity level:

“AppContainer” in Process Explorer implies Low integirty level.

If we would want Notepad to have access to the user’s files, such as documents and pictures, we would have to set explict permissions on these objects allowing access to the AppContainer SID. Functions to use include SetNamedSecurityInfo (see the project on Github for the full code).

I’ve created a simple application to test these things. We can specify a container name, an executable path and click “Run” to execute in in AppContainer. We can add folders/files that would get full permissions:

appcontainer8

Let’s now try a more interesting application – Windows Media Player (yes, I know, who uses the old Media Player these days? But it’s an interesting example). Windows Media Player has the (annoying?) feature where you can only run a single instance of it at any given time. The way this works is that WMP creates a mutex with a very specific name, “Microsoft_WMP_70_CheckForOtherInstanceMutex“, if it already exists, it sends a message to its buddy (a previous instance of WMP) and then terminates. A simple trick we can do with Process Explorer is to close that handle and then launching another WMP.

Let’s try something different: let’s run WMP in an AppContainer. Then let’s run another one in a different AppContainer. Will we get two instances?

appcontainer4

Running WMP in this way popus up its helper, setup_wm.exe which asks for the initial settings for WMP. Clicking “Express Settings” closes the dialog but it then it comes up again! And again! You can’t get rid of it, unless you close the dialog and then WMP does not launch. Can you guess why is that?

If you guessed “permissions” – you are correct. Running Process Monitor when this dialog comes up and filtering for “ACCESS DENIED” shows something like this:

appcontainer5

Clearly, some keys need access so the settings can be saved. The tool allows adding these keys and setting full permissions for them:

appcontainer6

Now we can run WMP in two different containers (change the container name and re-run) and they both run just fine. That’s becuase each mutex now has a unique name prefixed with the AppContainer SID of the relevant AppContainer:

appcontainer7

The code can be found at https://github.com/zodiacon/RunAppContainer.