Security: Deserialization



Securing Deserialization

  • When a RavenDB client uses the Newtonsoft library to deserialize a JSON string to a .NET object, the object may include a reference to a gadget (a code segment) and the deserialization process may execute this gadget.
  • Some gadgets attempt to exploit the deserialization process and initiate an RCE (Remote Code Execution) attack that may, for example, inject the system with malicious code. RCE attacks may sabotage the system, gain control over it, steal information, and so on.
  • To prevent such exploitation, RavenDB's default deserializer blocks deserialization for suspicious namespaces and known .NET RCE gadgets:
    System.Configuration.Install.AssemblyInstaller
    System.Activities.Presentation.WorkflowDesigner
    System.Windows.ResourceDictionary
    System.Windows.Data.ObjectDataProvider
    System.Windows.Forms.BindingSource
    Microsoft.Exchange.Management.SystemManager.WinForms.ExchangeSettingsProvider
    System.Data.DataViewManager, System.Xml.XmlDocument/XmlDataDocument
    System.Management.Automation.PSObject

  • Users can easily modify the list of namespaces and object types for which deserialization is forbidden or allowed.

Invoking a Gadget

  • Directly-loaded gadgets Cannot be blocked using the default binder.
    When a gadget is loaded directly its loading and execution during deserialization is permitted regardless of the content of the default deserializer list.

    E.g., the following segment will be executed,

    // The object will be allowed to be deserialized
    // regardless of the default binder list.  
    session.Load<object>("Gadget");
  • Indirectly-loaded gadgets Can be blocked using the default binder.
    When a gadget is loaded indirectly its loading and execution during deserialization can be blocked using the default deserializer list.

    E.g., in the following sample, taken from here, a gadget is loaded indirectly: its name is included as a value and will only take its place and be used to execute the gadget during deserialization.
    Including this type in the default deserialization list will prevent the gadget's deserialization and execution.

    string userdata = @"{
        '$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, 
                Culture=neutral, PublicKeyToken=31bf3856ad364e35',
        'MethodName':'Start',
        'MethodParameters':{
                    '$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, 
                            Culture=neutral, PublicKeyToken=b77a5c561934e089',
            '$values':['cmd', '/c calc.exe']
        },
        'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, 
                                    Culture=neutral, PublicKeyToken=b77a5c561934e089'}
    }";

DefaultRavenSerializationBinder

Use the DefaultRavenSerializationBinder convention and its methods to block the deserialization of suspicious namespaces and object types or allow the deserialization of trusted object types.

Define a DefaultRavenSerializationBinder instance, use the dedicated methods to forbid or allow the deserialization of entities, and register the defined instance as a serialization convention as shown below.

Be sure to update the default deserializer list before the initialization of the document that you want the list to apply to.


RegisterForbiddenNamespace

Use RegisterForbiddenNamespace to prevent the deserialization of objects loaded from a given namespace.

  • public void RegisterForbiddenNamespace(string @namespace)
    Parameter Type Description
    @namespace string The name of a namespace from which deserialization won't be allowed.

    Exception

    Attempting to deserialize a forbidden namespace will throw an InvalidOperationException exception with the following details:
    "Cannot resolve type" + type.FullName + "because the namespace is on a blacklist due to security reasons. Please customize json deserializer in the conventions and override SerializationBinder with your own logic if you want to allow this type."


RegisterForbiddenType

Use RegisterForbiddenType to prevent the deserialization of a given object type.

  • public void RegisterForbiddenType(Type type)
    Parameter Type Description
    type Type An object type whose deserialization won't be allowed.

    Exception

    Attempting to deserialize a forbidden object type will throw an InvalidOperationException exception with the following details:
    "Cannot resolve type" + type.FullName + "because the type is on a blacklist due to security reasons. Please customize json deserializer in the conventions and override SerializationBinder with your own logic if you want to allow this type."


RegisterSafeType

Use RegisterSafeType to allow the deserialization of a given object type.

  • public void RegisterSafeType(Type type)
    Parameter Type Description
    type Type An object type whose deserialization will be allowed.

Example

// Create a default serialization binder
var binder = new DefaultRavenSerializationBinder();
// Register a forbidden namespace
binder.RegisterForbiddenNamespace("SuspiciousNamespace");
// Register a forbidden object type
binder.RegisterForbiddenType(suspiciousObject.GetType());
// Register a trusted object type
binder.RegisterSafeType(trustedObject.GetType());

var store = new DocumentStore()
{
    Conventions =
    {
        Serialization = new NewtonsoftJsonSerializationConventions
        {
            // Customize store deserialization using the defined binder
            CustomizeJsonDeserializer = deserializer => deserializer.SerializationBinder = binder
        }
    }
};