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: TypeNameHandling.None
.
Intro
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.
TypeNameHandling
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:
TypeNameHandling.All
TypeNameHandling.Auto
TypeNameHandling.Arrays
TypeNameHandling.Objects
In fact the only kind that is not vulnerable is the default: TypeNameHandling.None
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.
[..]
[HttpPost]
public IActionResult Post([FromBody]Info value)
{
if (value == null)
{
return NotFound();
}
return Ok();
}
[..]
As you may have noticed we accept a body value from the type Info
, which is our own small dummy class:
public class Info
{
public string Name { get; set; }
public dynamic obj { get; set; }
}
The exploitation
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:
{
"obj": {
"$type": "System.IO.FileInfo, System.IO.FileSystem",
"fileName": "rce-test.txt",
"IsReadOnly": true
}
}
The line:
"$type": "System.IO.FileInfo, System.IO.FileSystem",
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 FileInfo
is 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.
Summary
When providing Json.NET based REST services always leave the default TypeNameHandling at TypeNameHandling.None
.
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 System.Web.Script.Serialization.JavaScriptSerializer
with a type resolver (e.g. SimpleTypeResolver
).
Update (28 Jul 2017)
At Black Hat USA 2017 Alvaro Muñoz and Oleksandr Mirosh held a talk with the title “Friday the 13th: JSON Attacks”. Muñoz and Mirosh had an in-depth look at different .NET (FastJSON, Json.NET, FSPickler, Sweet.Jayson, JavascriptSerializer DataContractJsonSerializer) and Java (Jackson, Genson, JSON-IO, FlexSON, GSON) JSON libraries. The conclusions regarding Json.NET are the same as in this blog post: Basically to not use another TypeNameHandling than TypeNameHandling.None or use a SerializationBinder to white list types (as in the documentation of Json.NET).
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)
Both the white paper[pdf] and the slides[pdf] are available on the Black Hat site.