Внешний json уязвим из-за Json.Net TypeNameHandling auto?

Я управляю небольшим веб-сайтом, где пользователи могут загружать пользовательские "объекты", определенные в JSON. Недавно я узнал о возможных угрозах с использованием JSON с автоматической десериализацией типов: проблема JSON. Я думаю, что понимаю проблематику, но я должен спросить, чтобы быть уверенным. Если я только десериализовать входящий JSON с заданным конкретным типом (здесь MyObject) JsonConvert.DeserializeObject<MyObject>(json, settings); и нет типа внутри MyObject и нет подтипа любого члена MyObject имеет тип System.Object или же dynamic нет ничего плохого, верно?

TypeNameHandling из settings установлен в TypeNameHandling.Auto (давайте не будем сомневаться в этом решении None, но я хочу понять вопрос с его установленным Auto.)

Изменить: Дополнительная информация: я проверил JSON с ранее упомянутого сайта:

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

Если MyObject имеет System.Object или же dynamic типизированное поле obj Я могу воспроизвести угрозу. Но то, что я хочу знать: я в безопасности с плохо подготовленным user-json, даже если MyObject - очень сложный объект с большим количеством (производных) подобъектов, но ни один из них не имеет или имеет System.Object или динамическое поле (тоже не то, что List<Object>)? Например, я могу представить, что Json.NET делает что-то вроде создания объектов из-за $type информация, даже если нет соответствующего поля в MyObject может быть найден.

1 ответ

Решение

TL / DR: в отсутствие каких-либо очевидных object или же dynamic члены, вы вполне можете быть в безопасности, но вы не гарантированно в безопасности. Для дальнейшего снижения риска вы должны следовать рекомендациям документации Newtonsoft:

TypeNameHandling следует использовать с осторожностью, когда ваше приложение десериализует JSON из внешнего источника. Входящие типы должны проверяться с помощью настраиваемого SerializationBinder при десериализации со значением, отличным от None.

Полный ответ

Атаки, описанные в разделе Как настроить Json.NET для создания уязвимого веб-API, предупреждение TypeNameHandling в статье Newtonsoft Json и Альваро Муньоса и Александра Мироша - все зависит от использования TypeNameHandling настройка Json.NET, чтобы обмануть получателя в создании гаджета атаки - экземпляра типа, который при создании, заполнении или удалении производит атаку на принимающую систему.

Json.NET делает две вещи, которые помогают защитить от таких атак. Во-первых, он игнорирует неизвестные свойства. Таким образом, просто добавляя дополнительное неизвестное свойство в полезную нагрузку JSON, значение которой содержит "$type" собственность не должна причинять вреда. Во-вторых, при десериализации полиморфного значения при разрешении "$type" свойство, оно проверяет, совместим ли разрешенный тип с ожидаемым типом в 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));
    }

Если ожидаемый тип полиморфного значения несовместим с любым типом гаджета атаки, атака завершится неудачей. При условии, что у вас нет сериализуемых членов типа object, dynamic или же IDynamicMetaObjectProvider это может быть правдой. Но не уверен!

Случаи, когда гаджет атаки может быть создан даже без каких-либо явных нетипизированных элементов в вашей модели данных, включают в себя:

  • Десериализация нетипизированных коллекций. Если вы десериализуете какой-либо нетипизированный набор или словарь, такой как ArrayList, List<object>, Dictionary<string, dynamic> или же HashTable, тогда ваша система уязвима для атак гаджетов, содержащихся в элементах коллекции.

  • Десериализация любого из десятков коллекций, унаследованных от CollectionBase, Этот тип предшествует введению обобщений в.Net и представляет собой коллекцию "полу типизированных", в которой типы элементов проверяются во время выполнения по мере их добавления. Поскольку проверка происходит после создания, есть окно, в котором может быть создан гаджет атаки.

    Образец скрипки, показывающий именно это.

  • Десериализация значений, которые имеют общий базовый тип или интерфейс с гаджетом атаки, кроме просто object, TempFileCollection инвентарь ICollection а также IDisposable, ObjectDataProvider инвентарь INotifyPropertyChanged а также ISupportInitialize, Если у вас есть какие-либо полиморфные члены или значения, которые объявлены как какие-либо из этих интерфейсов, вы уязвимы.

  • Десериализация типов, которые реализуют ISerializable, Json.NET поддерживает этот интерфейс по умолчанию, и вполне возможно, что кажущийся безвредным тип в некоторой внешней библиотеке десериализует нетипизированные элементы внутри своего потокового конструктора без вашего ведома.

    Один очевидный пример Sytem.Exception (или любой из его подтипов), который десериализует нетипизированный словарь "Data" внутри его потокового конструктора, который соответствует нетипизированному словарю Exception.Data, Если вы десериализуете Exception (содержится в файле журнала, например, который очень распространен), следующий JSON должен произвести атаку:

    {
      "$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    
        }
      },
    }
    

    Атака может быть смягчена без создания настраиваемого механизма связывания сериализации DefaultContractResolver.IgnoreSerializableInterface = true, Конечно, это может вызвать проблемы с сериализацией определенных типов библиотек классов.Net.

  • Типы десериализации, отмеченные [Serializable] может иметь аналогичную проблему, если вы установите DefaultContractResolver.IgnoreSerializableAttribute = false, Тем не менее, по умолчанию true, так что вы должны быть в порядке, если вы не измените эту настройку.

  • Десериализация типов с членами, которые, по вашему мнению, не сериализуются, но будут десериализованы, если они присутствуют. Например, рассмотрим следующий тип:

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

    Благодаря функциональности условной сериализации Json.NET, tempData член никогда не будет сериализован, так что вы можете подумать, что вы в чистом виде. Но он будет десериализован, если присутствует! Злоумышленник, который декомпилирует ваш код и заметит такого участника, сможет создать полезную нагрузку для гаджета атаки MyType,

И это как раз то, что я мог придумать с головы до головы. Как вы можете видеть, проверка того, что в большом объектном графе никогда не делается попытка десериализации полиморфного типа, совместимого с каким-либо гаджетом атаки, является по существу нетривиальной. Таким образом, я настоятельно рекомендую дополнительную защиту кастома SerializationBinder это гарантирует, что никакие неожиданные типы не будут десериализованы.

Другие вопросы по тегам