Saturday 22 July 2017

DG on Windows 10 S: Executing Arbitrary Code

From my previous blog post you might assume that getting non-Microsoft code to run on Windows 10 S would be difficult. Of course it’s already been noted that all you need to do is install Office and you can access the full scripting capability of VBA Macros as long as the file does not have MOTW. The fact is that the basic limitation of loading arbitrary executables and DLLs is the only thing being enforced by the Windows kernel. There’s nothing stopping existing, signed applications, such as Office from creating their own executable content which can be abused by the user or indeed an attacker.


You might therefore also assume that it’ll be trivial to get some arbitrary code running as there’s a number of different scripting engines on a default installation on Windows? Not so fast, many of the in-built script engines such as Powershell and Windows Scripting Host (WSH), which runs JScript/VBScript, are “Enlightened”. This means when they detect that UMCI is enabled, they’ll go into a locked down mode, such as PowerShell’s Constrained Language Mode. If the script being executed is also signed by the same sets of certificates as binary executable content, these enlightened hosts will unlock the full functionality of the scripting language. Typically, there’s ways to bypass these restrictions, in fact I did just that for Windows RT. You can see an entire presentation about the bypasses from BlueHat (when I was still invited to these things).


But this is somewhat academic when it comes with Win10S. The main enablers for bypasses, Scripting Engines, have their primary host executables blacklisted in the DG policy I showed last time. A few other well known offenders such as MSBuild are also blacklisted. So I guess we’ll have to go back to square one? Well not so much, there’s still a lot of executables on a default Win10S system which can be abused, we just need to find them.


DISCLAIMER: I’ve not sent the DG/UMCI bypass I’m about to describe to MSRC. The reason for not doing so is it’s not a click and run bypass. The only group which is likely to find the bypass useful are (as Matt and Casey would put it) Enlightened attackers. Attackers which know about how your systems are secured. This could be an external attacker, but it could also be your own users. DO NOT consider any application whitelisting solution to be secure against a bored member of staff.

Give my Regards to BinaryFormatter

Object serialization frameworks are a rich source of trivial arbitrary execution bugs, and .NET clearly didn’t want to be left out. Not that long ago, I found an RCE in .NET relating to the handling of WMI classes. You can read more details in the blog post, but to cut a long story short it allowed me to pass an arbitrary byte stream to the in-built BinaryFormatter class and get it to load an Assembly from memory and execute arbitrary code.


What was less obvious was that this was also a DG bypass. PowerShell allows you in Constrained Language Mode to query arbitrary WMI servers and classes, however the .NET runtime isn’t enlightened so it will happily load an Assembly from a byte array. Loading from a byte array is important as normally .NET will load Assemblies from an executable file which needs to be mapped into memory. The act of mapping the executable into memory triggers the CI module in the kernel to verify the signature, which for arbitrary code isn’t going to be permitted to load due to the configure CI policy. From a byte array, the kernel never sees the Assembly as .NET, will process it and execute arbitrary managed code from it. The DCOM bug has now been fixed, and at any rate PowerShell is blocked so we couldn’t invoke the WMI methods. However, if we could find another application which will take an array of bytes and pass it to BinaryFormatter we could reuse the deserialization exploit chain used in my previous exploit and use it to get a DG bypass in memory.


I concentrated my efforts on just the executables inside the %SystemRoot%\Microsoft.Net directories as many of them are written in .NET and so stand a reasonable chance of being exploitable. The first one to catch my eye, more than anything from a purely alphabetic point-of-view was AddInProcess.exe. This executable is known to me, in fact I’ve looked at it before from the perspective of Partial Trust sandbox escapes (maybe I’ll blog about that at some point in the future).


The process is used as part of the Add-In model which was introduced in .NET 4. The Add-In model provides a structured framework to expose functionality to 3rd parties to add additional features to an existing application. A plugin framework if you will. Actually implementing this requires you to develop contract interfaces and build pipelines and many other complicated things, but we don’t really care about all that. The thing which is interesting is the model supports Out-of-Process (OOP) Add-Ins, and this is the purpose of the AddInProcess. The executable is started in order to act as a host for these OOP Add-Ins. The Main function of the executable is pretty simple, the following is it in almost its entirety:


static int Main(string[] args) {
    if (args.Length != 2
|| !args[0].StartsWith("/guid:")
    || !args[1].StartsWith("/pid:")) {
       return 1;
   }
   
   string guid = args[0].Substring(6);
   int pid = int.Parse(args[1].Substring(5));
   AddInServer server = new AddInServer();
   var server = new BinaryServerFormatterSinkProvider {
       TypeFilterLevel = TypeFilterLevel.Full
   };
   var client = new BinaryClientFormatterSinkProvider();
   var props = new Hashtable();
   props["name"] = "ServerChannel";
   props["portName"] = guid;
   props["typeFilterLevel"] = "Full";
   var chnl = new AddInIpcChannel(props, client, server);
   ChannelServices.RegisterChannel(chnl, false);
   RemotingServices.Marshal(server, "AddInServer");
   Process.GetProcessById(pid).WaitForExit();
   return 0;
}


The interesting thing to point out here is the use of ChannelServices.RegisterChannel. This indicates that it’s using .NET remoting to perform communication. Where have we seen .NET remoting before? Oh that’s right, when I last broke .NET remoting. The main point about all this is that not only is it using .NET remoting which is basically broken, they’re using it with BinaryFormatter in Full TypeFilterLevel mode which means we can deserialize any data we like without worrying about the few security restrictions imposed such as running everything inside a PermitOnly grant for SerializationFormatter permission.


The process is creating an IPC channel, which uses Windows Named Pipes. The name of the pipe is specified using the portName property which is being passed on the command line. The process also takes a process ID which it waits for until the other process exits. Therefore we can start the AddInProcess with the following command line:


AddInProcess.exe /guid:32a91b0f-30cd-4c75-be79-ccbd6345de11 /pid:XXXX


Replace XXXX with a process ID which we know will stick around, such as Explorer. We'll find that the process creates the named pipe \\.\pipe\32a91b0f-30cd-4c75-be79-ccbd6345de11. The name of the service is configured using RemotingServices.Marshal which in this case is AddInServer. Therefore we can build the remoting URI as ipc://32a91b0f-30cd-4c75-be79-ccbd6345de11/AppInServer and we can use my ExploitRemotingService tool to verify it's exploitable (on a non-DG Windows 10 machine of course).


We need to use the --useser flag with the ExploitRemotingService tool in order to not use the old exploits which MS fixed. The useser flag sends serialized objects and gets them back from the server which allows you to do file operations such as listing directories and uploading/downloading files. This only works if the TypeFilterLevel is set to Full. This proves that the remoting channel will be vulnerable to arbitrary deserialization. We can just replace the serialized bytes from my tool with the one from my .NET DCOM exploit and we should get code arbitrary code execution in the context of the AddInProcess.

Now at this point we have an issue, if the only way to send data to this IPC server is by running a tool specially designed to communicate with a .NET remoting service then we can already run arbitrary code and don’t need a bypass. As the channel is a named pipe perhaps we can exploit it remotely? No such luck, the .NET Framework creates the named pipe with an explicit security descriptor which blocks network access.


In theory, we could change the permissions but even if we found a tool to do it needing a second machine is a pain. So what to do? Fortunately for us the .NET remoting protocol is pretty simple, at least when not used in a secure mode (which in this case it’s not). It’s a good example of a fire-and-forget protocol. No negotiation takes place at the start of the connection, the client just sends a correctly formatted set of bytes, including a header and the serialized Message to the server and if correct the server will respond. There’s no secrets which need to be worked out, we can create a binary file containing the serialized request ahead of time and just write it to the named pipe. If we massage the function which packages up the request from ExploitRemotingService and combine it the .NET serialization exploit from earlier we can generate a binary file which will exploit the .NET AddInProcess server.

If we have a file called request.bin the simplest way of writing this to the named pipe is to use CMD:

C:\> type request.bin > \\.\pipe\32a91b0f-30cd-4c75-be79-ccbd6345de11

This is great and really simple, it does sadly suffer from one tiny flaw, barely worth mentioning really… We can’t run CMD. Oh well back to the drawing board. What else can we use? While WSH is blocked, we can still run scriptlets in regsvr32. However, the scriptlet hosting environment is enlightened, which in the case of JScript/VBScript means you’re severely limited in what COM objects you can create. One of the only objects you can create is the Scripting.FileSystemObject which allows you to open arbitrary text files and read/write to them. It supports opening named pipes as a byproduct of the fact it also uses some of this functionality for handling process output. Therefore you can do something like the following to write arbitrary data to a named pipe.

var fso = new ActiveXObject("Scripting.FileSystemObject");
var pipe = "\\\\.\\pipe\\32A91B0F-30CD-4C75-BE79-CCBD6345DE11";
// Create a new ANSI text file object to the named pipe.
var file = fso.CreateTextFile(pipe, true, false);
// Write raw data to the pipe.
var data = "RAW DATA";
file.Write(data);
file.Close();

Unfortunately nothing is ever simple. The request data is arbitrary binary, so I initially tried to use a Unicode text file which makes writing binary data trivial. The class writes a Byte-Order-Mark (BOM) to the start when creating the file which screws up the request. So I tried ANSI mode, however this converts the UCS-2 characters from JScript into the current ANSI Code Page. On an English Windows system this is typically code page 1252, you can build a mapping table between a UCS-2 character and an arbitrary 8 bit character. However, if your system is set to another code page such as one of the more complex multi-byte character sets such as Shift-JIS this might be impossible. Anyway I’m sure I could make it work on more platforms with a bit more effort but it does the job, it allows me to load any arbitrary .NET code I like and execute it with the full DG Win10S policy enforced.

I’ve uploaded the code to my github here. Run the CreateAddInIpcData tool on another machine with the path to a IL-only .NET assembly and the name of the output scriptlet file. Make sure to give the scriptlet file an .sct extension. The .NET assembly must contain single public class with an empty constructor to act as the entry point during deserialization. Some C# similar to the following should do it, just compile into a class library Assembly.


public class EntryPoint {
   public EntryPoint() {
       MessageBox.Show("Hello");
   }
}

Copy the output scriptlet file to the Win10S machine. Start AddInProcess with the earlier command line (make sure the GUID is the same as previous as the endpoint URI ends up in the serialized request) and specify a PID (get it from Task Manager). Make sure the AddInProcess executable doesn’t immediately exit, which would indicate an error in your command line. Execute the scriptlet either by right clicking it in Explorer and selecting “Unregister” or manually using the following command from Explorer’s Run dialog:

regsvr32 /s /n /u /i:c:\path\to\scriptlet.sct scrobj.dll

You should now find your arbitrary .NET gets executed in the context of AddInProcess. From here you can write any code you like, well except for loading unsigned .NET assemblies from a file on disk that is.


That’s all for now. It should be clear that UMCI and .NET do not mix very well, just as it didn’t 4 years ago when I used similar tricks to break Windows RT. I’ve no idea if Microsoft have any future plans to limit things like loading .NET assemblies from memory (based on the response to issues like this I doubt it).

If you’re worried about this bypass you can block AddInProcess in your DG or Applocker policy. However until Microsoft find a solution to the Confused Deputy problem of .NET applications circumventing CI policy there will certainly be other bypasses. If you want to add this binary to your DG policy I’d recommend following the instructions on this blog post. Don't forget to also blacklist AddInProcess32 while you're at it.

Next time, we’ll go into leveraging this arbitrary code execution to run some more analysis tools and perhaps even get Powershell back, providing a good example of why you should always write your tooling in .NET. ;-)