Внешний 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
это гарантирует, что никакие неожиданные типы не будут десериализованы.