13 Jun 2017 | Peter Stöckli
How to configure Json.NET to create a vulnerable web API
tl;dr No, of course, you don’t want to create a vulnerable JSON API.
So when using Json.NET: Don’t use another TypeNameHandling setting than the default:
In May 2017 Moritz Bechler published his MarshalSec paper where he gives an in-depth look at remote code execution (RCE) through various Java Serialization/Marshaller libraries like Jackson and XStream. In the conclusion of the detailed paper, he mentions that this kind of exploitation is not limited to Java but might also be possible in the .NET world through the Json.NET library. Newtonsoft’s Json.NET is one of the most popular .NET Libraries and allows to deserialize JSON into .NET classes (C#, VB.NET).
So we had a look at Newtonsoft.Json and indeed found a way to create a web application that allows remote code execution via a JSON based REST API. For the rest of this post we will show you how to create such a simple vulnerable application and explain how the exploitation works. It is important to note that these kind of vulnerabilities in web applications are most of the time not vulnerabilities in the serializer libraries but configuration mistakes. The idea is of course to raise awareness with developers to prevent such flaws in real .NET web applications.
The sample application
The following hypothetical ASP.NET Core sample application was tested with .NET Core 1.1. For other .NET framework versions slightly different JSONs might be necessary.
The key in making our application vulnerable for “Deserialization of untrusted data” is to enable type name handling in SerializerSettings of Json.NET. This tells Json.NET to write type information in the field “$type” of the resulting JSON and look at that field when deserializing.
In our sample application we set this SerializerSettings globally in the ConfigureServices method in Startup.cs:
Following TypeNameHandlings are vulnerable against this attack:
In fact the only kind that is not vulnerable is the default:
The official Json.NET TypeNameHandling documentation explicitly warns about this:
TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.
But as the MarshalSec paper points out: not all developers read the documentation of the libraries they’re using.
The REST web service
To offer a remote attack possibility in our web application we created a small REST API that allows POSTing a JSON object.
As you may have noticed we accept a body value from the type
Info, which is our own small dummy class:
To “use” our newly created vulnerability we simply POST a type-enhanced JSON to our web service:
Et voilà: we executed code on the server!
Wait… what? But how?
Here’s how it works
When sending a custom JSON to a REST service that is handled by a deserializer that has support for custom type name handling in combination with the
dynamic keyword the attacker can specify the type he’d like to have deserialized on the server.
So let’s have a look at the JSON we sent:
specifies the class
FileInfo from the namespace System.IO in the assembly System.IO.FileSystem.
The deserializer will instantiate a
FileInfo object by calling the public constructor
public FileInfo(String fileName) with the given fileName “rce-test.txt” (a sample file we created at the root of our insecure web app).
Json.NET prefers parameterless default constructors over one constructor with parameters, but since the default constructor of
private it uses the one with one parameter.
Afterwards it will set “IsReadOnly” to true. However, this does not simply set the “IsReadOnly” flag via reflection to true. What happens instead is that the deserializer calls the setter for IsReadOnly and the code of the setter is executed.
What happens when you call the IsReadOnly setter on a
FileInfo instance is that the file is actually set to read-only.
We see that indeed the read-only flag has been set on the rce-test.txt file on the server:
A small side effect of this vulnerable service implementation is that we also can check if a file exists on the server. If the file sent in the “fileName” field does not exist an exception is thrown when the setter for IsReadOnly is called and the server returns NotFound(404) to the caller.
To perform even more sinister work an attacker could search the .NET framework codebase or third party libraries for classes that execute code in the constructor and/or setters. The
FileInfo class here is just used as a very simple example.
When providing Json.NET based REST services always leave the default TypeNameHandling at
When other TypeNameHandling settings are used an attacker might be able to provide a type he wants the serializer to deserialize and as a result unwanted code could be executed on the server.
The described behavior is of course not unique to Json.NET but is also implemented by other libraries that support Serialization e.g. when using
Update (28 Jul 2017)
They also presented new gadgets, which allow more sinister attacks than the one published in this blog post (the gadgets might not work with all JSON/.NET framework combinations):
System.Configuration.Install.AssemblyInstaller: "Execute payload on local assembly load"
System.Activities.Presentation.WorkflowDesigner: "Arbitrary XAML load"
System.Windows.ResourceDictionary: "Arbitrary XAML load"
System.Windows.Data.ObjectDataProvider: "Arbitrary Method Invocation"
In addition to their findings they had a look at .NET open source projects which made use of any of those different JSON libraries with type support and found several vulnerabilities:
- Kaliko CMS RCE in admin interface (used FastJSON, which has insecure type name handling by default)
- Nancy RCE (RCE via CSRF cookie)
- Breeze RCE (used Json.NET with TypeNameHandling.Objects)
- DNN (aka DotNetNuke) RCE (RCE via user-provided cookie)