Saturday, 19 August 2017

The Art of Becoming TrustedInstaller

If you’ve spent any time administering a Windows system post Vista you’ll have encountered the TrustedInstaller (TI) group which most system files and registry keys are ACL’ed to. If, for example you look at the security for a file in System32 you’ll notice both that only TI can delete and modify files (not even the Administrators group is allowed) and the Owner is also TI so you can’t directly change the security either.

File Security Descriptor Dialog showing Trusted Installer as Owner

However, if you look in the Local Users and Groups application you won’t find a TI user or group. This blog post is about what the TI group really is, and more importantly, with the aid of PowerShell and the NtObjectManager module, how you can be TI to do whatever you wanted to do.

Where is TrustedInstaller?

If TI isn’t a user or group then what is it? Perhaps looking at the ACL more closely will give us some insight. You can use the Get-Acl cmdlet to read the security descriptor from a file and we can list the TI ACE.

File ACL in PowerShell Showing Full TrustedInstaller name.

We can see in the IdentityReference member that we’ve got the TI group, and it’s prefixed with the domain “NT SERVICE”. Therefore, this is a Windows Service SID. This is a feature added in Vista to allow each running service to have groups which they can use for access checks, without the overhead of adding individual real groups to the local system.

The SID itself is generated on the fly as the SHA1 hash of the uppercase version of the service name. For example the following code will calculate the actual SID:

$name = "TrustedInstaller"
# Calculate service SID
$bytes = [Text.Encoding]::Unicode.GetBytes($name.ToUpper())
$sha1 = [System.Security.Cryptography.SHA1]::Create()
$hash = $sha1.ComputeHash($bytes)
$rids = New-Object UInt32[] 5
[Buffer]::BlockCopy($hash, 0, $rids, 0, $hash.Length)
[string]::Format("S-1-5-80-{0}-{1}-{2}-{3}-{4}", `
       $rids[0], $rids[1], $rids[2], $rids[3], $rids[4])

Of course you don’t need to do this, NTDLL has a RtlCreateServiceSid function, and LSASS will convert a service name to a SID and vice versa. Anyway, back to the point. What this means is that there’s a service called TrustedInstaller which must be running when system resources are modified. And that’s exactly what we find if we query with the SC utility.


If we start the TI service and look at the Access Token we’ll see that it has the TI group enabled.

Access Token Showing TrustedInstaller SID

Enough of the background, assuming we’re an administrator how can we harness the power of TrustedInstaller?

Becoming TrustedInstaller

If you search on the Web for how to delete resources owned by TI you’ll tend to find articles that advocate manually taking ownership of the file or key, then change the DACL to add the administrators group. This is because even the usually compliant IFileOperation UAC COM object won’t do this automatically, as you’ll end up with the following dialog.

File Access Denied Dialog due to TrustedInstaller Owner

Changing the permissions on a system file isn’t exactly a great idea. If you do it wrong you could easily open up parts of your system to EoP issues, especially with directories. Explorer can make it easy to accidentally replace the security settings on all subfolders and files with little way of getting back the original values. Of course, the reason for TI is to stop you doing all this in the first place, but some people seem to really want it do it for some reason.

You might assume you could just add your current user to the TI group and that’s that? Unfortunately the LSASS APIs such as NetLocalGroupAddMembers takes a group name not a SID, and passing “NT SERVICE\TrustedInstaller” doesn’t work, as it’s not a real group, but created synthetically. There might be a magic incantation to do it, or at least a low-level RPC call, but I didn’t think it was worth the effort.

Therefore, the quick and dirty way would be to change the configuration of the TI service to run a different binary. Oddly, even though TI makes things on the system harder to mess with, it doesn’t protect its own service configuration from modification by a normal administrator. So you can issue a command such as the following to delete an arbitrary file as TI:

sc config TrustedInstaller binPath= "cmd.exe /C del path\to\file"

Start the TI service and *bang* the file is gone. Another reason this works is TI is not a Protected Process Light (PPL), which is odd because the TI group is given special permission to stop and delete PPL services. I pointed this out to MSRC (and Alex Ionescu did so in 2013, and clearly I didn’t bother to read it) but they didn’t do anything to fix it as no matter what they pretend PPL isn’t a security boundary, well until it a security boundary.

This still feels like a hack, you’d have to restore the TI service to its original state otherwise things like Windows Update will get unhappy really quickly. As the TI service has a token with the correct groups, what about just starting the service then “borrowing” the Token from it to create a new process or for impersonation?

As an admin we’ve got SeDebugPrivilege so can just open the TI process and open it’s token. Then we can do whatever we like with it. Simple really, let’s give that a try.

PowerShell window showing capturing the token from TrustedInstaller process.

Well that was easy. Pat yourself on the back, grab a cold one it’s time for some Trusted Installering…

PowerShell console showing token can't be used for a new process or impersonation

Well crap, seems we can’t create a new process or impersonate the token. That’s not much good. At the bottom of the screenshot we can see why, the token we’ve got only has TOKEN_QUERY access. We typically need at least TOKEN_DUPLICATE access to get the primary token for a new process or to create an impersonation token. Checking the security descriptor of the token using Process Hacker (we don’t even have READ_CONTROL to read it from PS) explains the reason why we’ve got such limited access.

Security descriptor for TrustedInstaller access token.

We can see that the Administrators group only has TOKEN_QUERY access which at least matches up with the access we were granted on the token object. You might wonder why SeDebugPrivilege didn’t help here. The Debug privilege only bypass the security checks on Process and Thread objects, it doesn’t do anything on Tokens so we get no help. Are we stuck now, at least without the more destructive techniques such as changing the service binary?

Of course not. There are examples of how to get stuff running as TI such as this but they seem to rely on first getting code running at system by install a service (like psexec with the -s switch) and then stealing the the TI token and creating a new process. Needless to say, if I wanted to create a service I’d just modify the TrustedInstaller service to begin with :-)

So here’s two quick tricks to allow use to get around this permission limitation which doesn’t require any new or modified services or injecting shellcode. First, let’s deal with creating a new process. The parent of a new process is the caller of CreateProcess, however for UAC this would make all elevated processes children of the UAC service which would look somewhat odd. To support the principle of least surprise MS introduced in Vista the ability to specify an explicit parent process when creating a new process so that the elevated process could still be a child of the caller.

Normally in the UAC case you specify an explicit token to assign to the new process. However, if you don’t specify a token the new process will inherit from the assigned parent, the only requirement for this is the handle to the process we use as a parent must have the PROCESS_CREATE_PROCESS access right. As we’ve got SeDebugPrivilege we can get full access to the TI process including the right to create new child processes from it. As an added bonus the kernel process creation code will even assign the correct session ID for the caller so we can create interactive processes. We exploit this behavior to create an arbitrary process running as the TI service token on the current desktop using the New-Win32Process cmdlet and passing the process object in the -ParentProcess parameter.

PowerShell window showing successful process creation from parent process

It’d by kind of useful to impersonate the token as well without creating a new process. Is there a way we can achieve that? Sure, we can use the NtImpersonateThread API, which allows you capture the impersonation context from an existing thread and apply it to another. The way impersonation contexts work is the kernel will try and capture the impersonation token for the thread first. If there is no existing impersonation token then it’ll take a copy of the primary token of the process associated with thread and impersonate that instead. And the beauty of the NtImpersonateThread API, like setting the parent process, doesn’t require permission to access the token, it just required THREAD_DIRECT_IMPERSONATION access to a thread which we can get due to SeDebugPrivilege. We can therefore get an impersonation thread without a new process by the following steps:
  1. Open process with at least PROCESS_QUERY_INFORMATION access to list its threads.
  2. Open the first thread in the process with THREAD_DIRECT_IMPERSONATION access. We’ll assume that the thread isn’t impersonating some lowly user.
  3. Call NtImpersonateThread to steal an impersonation token.

PowerShell console showing successful impersonation.

Now as long as something else doesn’t set the main thread’s impersonation token (and you don’t do anything on a separate thread) then your PS console we act as if it has the TI group enabled. Handy :-)


Hopefully this has given you some information about what TrustedInstaller and a few tricks to get hold of a token for that group using an admin account which would not normally be allowed. This applies equally to a number of different system services on modern Windows, which can be pain if you want to interact with their resources for testing purposes such as using my Sandbox Tools to work out what resources they can access.

Update (20170821)
Thought I should at least repeat that of course there's many ways of getting the TI token other than these 3 techniques. For example as Vincent Yiu pointed out on Twitter if you've got easy access to a system token, say using Metasploit's getsystem command you can impersonate system and then open the TI token, it's just IMO less easy :-). If you get a system token with SeTcbPrivilege you can also call LogonUserExExW or LsaLogonUser where you can specify an set of additional groups to apply to a service token. Finally if you get a system token with SeCreateTokenPrivilege (say from LSASS.exe if it's not running PPL) you can craft an arbitrary token using the NtCreateToken system call.

Thursday, 3 August 2017

DG on Windows 10 S: Abusing InstallUtil

This is the final blog post I’m going to do on Windows 10 S. The previous parts are here, here and here. Originally, I was going to describe a way of completely removing the SI policy on Win10S without upgrading to Pro so that you can install any applications you like *ahem* while keeping secure boot etc. intact.


However, I decided that enforcing the SI policy was more about licensing than it was about security so I’ve thought better of it. If you really want to run arbitrary applications on your own computer:

  1. Don’t buy a Windows 10 S machine in the first place.
  2. Or, failing that, upgrade to Pro, at least for the Surface Laptop that’s still currently free.
  3. Or, failing that, work out how I removed the policy, it’s not that hard ;-)

Instead of disabling the entire policy, I’ll detail another DG bypass. In this case, it’s exploiting the same root cause as the previous one I disclosed, .NET loading untrusted code from a byte array through serialization, but with an interesting twist (*spoiler* it’s not using BinaryFormatter, well mostly). Therefore, I don’t think it makes that much difference to disclose. MS, or at least the .NET team (hi Barry), are unlikely to fix the fundamental incompatibility between DG and .NET any time soon.

Is That You, NetDataContractSerializer?

It turns out that BinaryFormatter and .NET remoting was just too dangerous to let live and MS finally removed it from .NET. Just kidding, MS did no such thing. While MS might try and put scary, if somewhat small, warnings when you try and search for documentation on .NET remoting and BinaryFormatter, both technologies are still there in the .NET framework and no warnings are produced when using them. In fact BinaryFormatter is so awesome, it’s coming back in .NET Core 2.0 which is a bit of a shame IMHO.

What did happen in version 3.0 of the .NET Framework was the introduction of Windows Communication Foundation (WCF), a new object communication stack for accessing remote services. Learning well from the past, MS chose to use XML Web Services (well perhaps didn’t learn that well from the past) and instead of BinaryFormatter, they implemented a new serialization mechanism, Data Contracts. The canonical implementation of the WCF Data Contracts is the DataContractSerializer (DCS) class. In order to use the DCS class for serialization you are supposed to annotate your classes and properties with the DataContractAttribute and DataMemberAttribute. Explicitly annotated, Data Contracts are not that interesting, however, clearly someone decided that it’d be great if there was a way of serializing existing serializable classes. Therefore, DCS also supports serializing arbitrary classes as long as they have the SerializableAttribute annotation, for example if you have the following C# class:

namespace DCSerializer {
 public class Contract {
     public int Value;

And run it through the DCS WriteObject method you’ll get the following XML content:


In theory, there’s enough information to deserialize this XML file without any special knowledge, the namespace (DCSerializer) and the class name (Contract) and reflected in the default XML namespace and root element name respectively. However, what’s missing here is a reference to what assembly the Contract type exists in. This ambiguity is resolved by requiring that all known types (outside of some specific system types) must be specified during construction or through a resolver. This isn’t a problem in a simple, well defined web service. But it does make DCS less useful as a general, exploitable serializer.

While DCS is awesome in its own way, the requirement for specifying all types is a weakness, from a lazy developer point of view. It would be nice if you could get some of the flexibility of the more general serializers such as BinaryFormatter. This is where the similar but different NetDataContractSerializer (NDCS) class comes into the picture. Both DCS and NDCS (and the related DataContractJsonSerializer) derive from the XmlObjectSerializer class. This allows NDCS to be used for WCF services instead of DCS if you so desire. Serializing the previous class through NDCS generates the following:

 z:Assembly="DCSerializer, Version="

The output from NDCS includes assembly information. Therefore NDCS works in a similar way to BinaryFormatter in that it doesn’t need any prior knowledge of the types being deserialized. This makes NDCS the equivalent of BinaryFormatter but in XML format, though to be fair .NET already had something similar with the SoapFormatter. This is a long winded way of saying, if you can find an application which will load an untrusted NDCS XML file, you can exploit it with the exact same set of serialization gadgets as you can with BinaryFormatter from my previous post. The question therefore is, does such an application exist? Let’s see just one example.

The Ways of InstallUtil

InstallUtil is a .NET utility which is pre-installed with the .NET Framework. The utility has been available since at least v1.1 (I don’t have anything with v1.0 to check). Its purpose is to allow you to run installation code from an assembly so that you can configure system state and install your code. To use it normally, you first define a class which derives from the Installer class, annotate your class with the RunInstallerAttribute and then implement one of the main callback methods such as Install.

For example, the following class is sufficient to be executed by InstallUtil:

public class TestInstaller : Installer {
   public override void Install(IDictionary stateSaver) {
       Console.WriteLine("Hello from the Installer");

If you compile the class into an assembly, you can then run the installer using the following command line and it will execute the Install method in your assembly:

InstallUtil path\to\installer.dll

The interesting thing about InstallUtil is it’s a known Application Whitelisting bypass (specifically against something like AppLocker). The executable is Microsoft signed, located in a system directory and will execute code from an arbitrary assembly file passed on the command line. However, what it isn’t is a DG bypass. InstallUtil loads the assembly from a file, the file needs to be allowed to load in the SI policy, which means for Win10S we can only load existing assemblies signed by Microsoft. We might be able to find an assembly with an installer we could abuse, but I didn’t look very hard. Still, that doesn’t mean we can’t abuse InstallUtil in other ways.

If you run the simpler installer through InstallUtil, you might notice a file which gets created next to the installer assembly file which has an InstallState extension. This file begs for closer inspection. Opening it in a text editor you’ll encounter content which looks awfully familiar:


This looks a lot like the output from a NDCS serialization. To confirm we can just go looking at the code in a decompiler, the assembly doesn’t seem to be available in the reference source. InstallUtil is actually just a thin wrapper around the ManagedInstallerClass class which is implemented in the System.Configuration.Installer assembly. Poking around a bit, we find that AssemblyInstaller is using NDCS in a number of places. We’re not too interested in places where the NDCS is used to write out objects, rather, we’re more interested in places where it’s reading. For example in the Uninstall method, there’s the following code:

public override void Uninstall(IDictionary savedState) {
 string installStatePath = GetInstallStatePath(Path);
 if (File.Exists(installStatePath)) {
   FileStream fileStream = new FileStream(installStatePath);
   XmlReader xmlReader = XmlReader.Create(fileStream);
   var ser = new NetDataContractSerializer();
   IDictionary savedState = ser.ReadObject(xmlReader);
   // Run uninstaller...

From this snippet of code, we can see that the untrusted install state file it loaded verbatim with a insecure NDCS class instance. If we can can convince InstallUtil to load a crafted install state file which contains a deserialization chain to load an assembly from a byte array, we can bypass DG. While we can’t load untrusted assemblies, the utility doesn’t need a specific assembly so we can just instruct it to uninstall a system assembly such as mscorlib. Don’t worry, it won’t actually do anything as mscorlib doesn’t contain any installers. Also, looking at the documentation there’s a InstallStateDir parameter we can pass to specify where the utility will look for our install state. If we copy the serialized file to c:\dummy\mscorlib.InstallState then we can get the DG bypass by running the following command:

InstallUtil /u /InstallStateDir=c:\dummy /AssemblyName mscorlib

I’ve updated my DG bypass Github repository to include this bypass as well. Run the CreateInstallState utility passing the path to the assembly to load (again it will instantiate the first public type it finds) and the output filename such as mscorlib.InstallState. Execute the previous InstallUtil command and you should get your assembly executed. Note that InstallUtil will try and delete the InstallState file after use, if you don’t want that to happen you can just set the Read-Only flag on the file and the delete will fail.

The main advantage to this DG bypass over the previous one I disclosed in AddInProcess is that it’s easy to use for persistence. Just add a scheduled task which runs InstallUtil or a LNK file in the startup folder with the appropriate command line and the code DG bypass will run when you login.
As a final note, you might wonder how InstallUtil serialized the install state prior to v4 of the Framework, specifically as NDCS was only introduced in v3.0? Dropping the v2 System.Configuration.Installer assembly into the decompiler we find it uses, *drum roll* SoapFormatter. So it’s just as vulnerable to this attack in v2, assuming you’ve got v2 compatible gadgets (most v2 installs are really v3.5 so that’s typically a yes as the gadgets I presented in the previous post were introduced in v3.0).

While this bypass exists currently in Win10S and likely in many custom DG policies, it’s easy to just ban InstallUtil as you would with AddInProcess and this would eliminate the bypass. Again I’ll give you a link to Matt Graeber’s blog post about adding new executables to your DG policy.

Final Wrap Up

This is the end of my planned series on Win10S. Hopefully, I’ve demonstrated that regardless of the PR coming out of Microsoft that it’s not 100% secure, at least against anyone who knows you run Win10S and is willing to customize an attack to you or your organization. There will always be bypasses for DG, and the way Windows works, it’s almost impossible to completely lock it down. If it wasn’t .NET, it’d be a memory corruption vulnerability from an overlong command line parameter or something equally silly.

Does Win10S have no value what so ever? Of course not, DG is a good, if not perfect, way of limiting a system to a very a specific set of signed executables. I’d be less sceptical about Win10S if it hadn’t become so transparently a marketing ploy rather than a goal to really push the Windows platform forward. Unfortunately, I can’t see the goal of a secure Windows platform ever being reached without completely jettisoning all the reasons that Windows works for people at the moment.

I’d like to thank Matt Graeber for his knowledge of Device Guard and for doing reviews of these posts to make sure I’m not talking complete rubbish. Also shout outs to everyone looking for these sorts of issues such as Matt Nelson, Casey Smith, Alvaro Muñoz and no doubt others I’m forgetting.