The Jailbreak removed that restriction making the tablet operating system instantly more usable. Still being a curious person I thought I would have a play myself, just to see if I could get unsigned code running on the Desktop without requiring the full Jailbreak or any external tools such as a debugger. This blog post is a case study of one of the issues I found in January which is fixed in Windows 8.1. In the end Microsoft did not consider it serious enough to release a bulletin for Windows 8 so I received permission to talk about it.
Step 1: Identifying an attack vector
The first thing I did was to determine a suitable attack vector. Not only does Windows RT come with the Desktop environment Microsoft even ported across a lot of, in my opinion, unnecessary functionality. One of those I was surprised to find was Powershell, a very useful scripting environment running on the .NET framework. It supports manipulating .NET objects, even ones not specifically for Powershell use. This seemed as good a place as any to start looking.
I began by running some basic Powershell script to create an arbitrary runtime type using the ‘new-object’ Powershell command. It didn’t work, instead Powershell returned the following error:
I investigated a few other techniques, such as calling static methods and using the ‘add-type’ Powershell command. It seemed that Powershell might not be as useful as I first thought. However ending it there would just be lazy, I wanted to know why it didn’t work.
Step 2: Hunting down security checks
Running the same script while ‘Jailbroken’ was successful, this at least confirmed it wasn’t a specially compiled version of Powershell. Instead there must be code to change behaviour at runtime depending on whether it is running in locked down mode or not. It was time to break out Reflector and load the core Powershell library, System.Management.Automation to have a look.
First I wanted to identify the code which generated the error. This wasn’t too difficult. While you couldn’t search for the error string directly (Powershell like most Windows binaries is localised, the actual text you see isn’t directly in the code) you could search for the ErrorId. This led me to the method, EnsureAllowedInLanguageMode, which determined whether accessing a type was allowed or not. This only applied if the current global language mode was set to Constrained.
What other values could the language mode take? The PSLanguageMode enumeration used is actually documented on MSDN, well sort of.
On http://msdn.microsoft.com/en-us/library/system.management.automation.pslanguagemode(v=vs.85).aspx you can see the mode values, Constrained is apparently TBD, which is a common euphemism for it will Never-Be-Documented. There are some other values the language mode could take such as Restricted. You could reasonably assume that there must be something that uses this mode and it could be exploitable. It would also seem reasonable to assume that Restricted is “less” restricted than Constrained as type access is only enforced for the latter.
Step 3: Exploiting language mode changes
After a bit of investigation in Reflector I found a number of places where the global language mode was captured, set to Restricted, and then reverted back to the original setting. One of the more interesting was for a barely known feature called DATA sections. This is a construct in Powershell to run a series of commands in a restricted environment. It is used for things like text localisation. The language mode is really more about restricting what you can do to make it easy for the developer rather than any significant security boundary.
If you try and do something more interesting than generating data it will fail. You can’t call arbitrary Powershell commands, you cannot use variables or call functions so how could this ever be useful? Well turns out the restrictions on what Powershell commands you can run within a data section are user specified. If you read the help file (get-help about_DATA) you find there is a special “Supported Command” parameter, which allows you permit any Powershell command to execute.
Trying it with my original script yielded success, I knew I was on to something:
Step 4: Getting unsigned code execution
The final step is to get some arbitrary unsigned .NET code to run. Even though we have elevated our privileges from Constrained to Restricted we still have a problem. Restricted doesn’t allow us to do much. Still I can create any object I like in the runtime, and using the special ‘-property’ syntax of the ‘new-object’ command I can even invoke arbitrary methods on that object. So I identified that the ‘RunspaceInvoke’ class http://msdn.microsoft.com/en-us/library/system.management.automation.runspaceinvoke(v=vs.85).aspx would run fully privileged Powershell by calling its Invoke method. This seemed perfect, but it would be nice to not have to write all my code in Powershell. Ideally I would like to run my own C# code but you might assume that the Windows RT signature checks would get in the way.
In the .NET world, DLLs and EXEs are just vessels containing the Intermediate Language code. You can load .NET code directly from disk but that will trigger the signature verification checks. However you can also load code from a byte array in memory, this does not need to be signed. So first I wrote the following simple C# code and compiled it into a DLL which is marked as running on any architecture.
I could now complete the exploit. I put together the following somewhat unwieldy Powershell script to load the DLL into memory, create the type and finally run your own fully privileged C# code.
In conclusion that is how you could run unsigned code on Windows RT without a Jailbreak. I am confident it is not the only way you could discover. This specific bug has been fixed in 8.1 by preventing DATA sections specifying supported commands in Constrained mode. But having almost the entire Desktop environment present is going to result in other techniques to get code execution no matter what Microsoft try to do because it has a massive attack surface. No pun intended.