External json vulnerable due to Json.Net TypeNameHandling auto?

I work on a small website where users can upload custom “objects” defined in JSON. I recently learned about possible threats using JSON with automatic type deserialization: JSON problem . I think I understand the issue, but I have to ask to be sure. If I only deserialize the incoming JSON with the given specific type (here MyObject ) JsonConvert.DeserializeObject<MyObject>(json, settings); and there is no type inside MyObject , and no subtype of any member of MyObject is of type System.Object or dynamic , there is nothing that can go wrong, right?

TypeNameHandling of settings set to TypeNameHandling.Auto (do not question this solution, it may work with None , but I want to understand that the question with it is set to Auto .)

Edit: Additional info: I tested JSON from the previously mentioned website:

 { "obj": { "$type": "System.IO.FileInfo, System.IO.FileSystem", "fileName": "rce-test.txt", "IsReadOnly": true } } 

If MyObject has System.Object or dynamic obj entered, I can reproduce the threat. But what I want to know: I am safe with poorly prepared user-json, even if MyObject is a very complex object with a lot of (derived) auxiliary objects, but NONE of them has or has System.Object or a dynamic field (also not something like List<Object> )? For instance. I could imagine that Json.NET was doing something like creating objects because of the $type information, even if no field was found in the MyObject field.

+5
source share
1 answer

TL / DR : in the absence of any obvious members of object or dynamic you can be safe, but you cannot be safe. To reduce the risk, you should follow the recommendations of the Newtonsoft documentation :

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types must be checked with the SerializationBinder when deserializing with a value other than None.

Full answer

The attacks described in How to configure Json.NET to create a vulnerable web API , TypeNameHandling caveat in Newtonsoft Json and Alvaro Muñoz and Oleksandr Mirosh black paper depend on the use of the TypeNameHandling Json.NET setting to trick the receiver into creating an attack gadget - an instance of a type that when building, filling out or deleting effects of an attack on the host system.

Json.NET does two things that help protect against such attacks. First, it ignores unknown properties. Thus, simply adding an additional unknown property to the JSON payload whose value contains the "$type" property should not be harmful. Secondly, when deserializing a polymorphic value while resolving the "$type" property "$type" it checks to see if the allowed type is compatible with the expected type in JsonSerializerInternalReader.ResolveTypeName() :

  if (objectType != null #if HAVE_DYNAMIC && objectType != typeof(IDynamicMetaObjectProvider) #endif && !objectType.IsAssignableFrom(specifiedType)) { throw JsonSerializationException.Create(reader, "Type specified in JSON '{0}' is not compatible with '{1}'.".FormatWith(CultureInfo.InvariantCulture, specifiedType.AssemblyQualifiedName, objectType.AssemblyQualifiedName)); } 

If the expected type of polymorphic value is incompatible with the type of attack gadget, the attack will fail. If you don't have serializable elements like object , dynamic or IDynamicMetaObjectProvider , this may be true. But not sure!

Cases in which a gadget attack can be designed even without any obvious untyped elements in your data model include:

  • Deserialization of untyped collections. If you deserialize any untyped collection or dictionary, such as ArrayList , List<object> , Dictionary<string, dynamic> or HashTable , then your system is vulnerable to the attack gadget contained in the elements of the collection.

  • Deserialize any of the dozens of collections inherited from CollectionBase . This type precedes the introduction of generics into .Net and is a “semi-type” collection in which element types are checked at runtime as they are added. Since validation occurs after construction, there is a window in which an attack gadget can be created.

    An example fiddle showing just that.

  • Deserialize values ​​that have a common base type or interface with an attack gadget other than a simple object . TempFileCollection implements TempFileCollection and IDisposable . ObjectDataProvider implements INotifyPropertyChanged and ISupportInitialize . If you have any polymorphic members or values ​​declared like any of these interfaces, you are vulnerable.

  • Deserialization of types implementing ISerializable . Json.NET supports this interface by default, and it is possible that the seemingly harmless type in some external library will deserialize untyped elements inside its streaming constructor without your knowledge.

    One obvious example is Sytem.Exception (or any of its subtypes), which deserializes the untyped Dictionary "Data" inside its stream constructor , which corresponds to the untyped dictionary Exception.Data . If deserialization is an Exception (contained in a log file, for example, very common), the following JSON should produce an attack:

     { "$type": "System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "ClassName": "System.Exception", "Message": "naughty exception", "Data": { "$type": "System.Collections.ListDictionaryInternal, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "data": { "$type": "System.IO.FileInfo, System.IO.FileSystem", "fileName": "rce-test.txt", "IsReadOnly": true } }, } 

    An attack can be reduced without creating a custom mediation binding by setting DefaultContractResolver.IgnoreSerializableInterface = true . Of course, this can cause problems with serializing certain types of .Net class library.

  • The types of deserialization marked with [Serializable] may cause a similar problem if you set DefaultContractResolver.IgnoreSerializableAttribute = false . However, true used by default, so you should be fine if you don't change this setting.

  • Deserialize types with members that you think are not serialized, but will be deserialized if they are present. For instance. consider the following type:

     public MyType { public object tempData; public bool ShouldSerializeTempData() { return false; } } 

    Thanks to Json.NET conditional serialization , the tempData member tempData never be serialized, so you might think that you are in clear. But it will be deserialized if present! An attacker who decompiles your code and notifies such a member will be able to handle the payload of the attack gadget for MyType .

And this is just what I could think of my head. As you can see, checking that the polymorphic type compatible with any attack gadgets never tries to deserialize on a large object graph is practically nontrivial. Therefore, I highly recommend the extra protection of the custom SerializationBinder , which ensures that unexpected types are not deserialized.

+5
source

All Articles