.NET高级代码审计(第七课) NetDataContractSerializer反序列化漏洞
字数 1671 2025-08-18 11:38:23
.NET高级代码审计:NetDataContractSerializer反序列化漏洞
一、NetDataContractSerializer概述
NetDataContractSerializer是.NET框架中用于序列化和反序列化Windows Communication Foundation (WCF)消息中数据的类。与DataContractSerializer相比,它有以下重要区别:
- 包含CLR类型信息:NetDataContractSerializer包含了完整的CLR类型信息,通过保存类型引用支持类型精确性
- 使用限制:只有在序列化和反序列化端使用相同的CLR类型时才能使用
- 序列化方法:WriteObject或Serialize
- 反序列化方法:ReadObject或Deserialize
二、NetDataContractSerializer序列化
基本序列化示例
public class TestClass
{
public string name;
public string classname;
public int age;
public static void ClassMethod(string cmd)
{
Process.Start(cmd);
}
}
// 序列化示例
TestClass test = new TestClass();
test.name = "Ivan1ee";
test.classname = "360";
test.age = 18;
NetDataContractSerializer serializer = new NetDataContractSerializer();
using (MemoryStream ms = new MemoryStream())
{
serializer.Serialize(ms, test);
string xml = Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine(xml);
}
序列化后的XML输出示例:
<TestClass z:Id="1" z:Type="WpfApp1.TestClass" z:Assembly="WpfApp1,Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://schemas.datacontract.org/2004/07/WpfApp1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<age>18</age>
<classname z:Id="2">360</classname>
<name z:Id="3">Ivan1ee</name>
</TestClass>
三、NetDataContractSerializer反序列化
3.1 反序列化原理和用法
NetDataContractSerializer继承自XmlObjectSerializer抽象类和IFormatter接口,实现了以下关键方法:
- ReadObject:核心反序列化方法
- WriteObject:核心序列化方法
- Deserialize:内部调用ReadObject实现反序列化
反序列化示例代码:
NetDataContractSerializer serializer = new NetDataContractSerializer();
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
TestClass test = (TestClass)serializer.ReadObject(ms);
Console.WriteLine(test.name);
}
3.2 攻击向量—MulticastDelegate
利用多路广播委托(MulticastDelegate)实现RCE的核心思路:
- MulticastDelegate:继承自Delegate,调用列表中可以拥有多个元素
- GetInvocationList:获取委托链中的委托数组
- Comparison
类 :位于System.Collections.Generic命名空间,用于创建比较委托 - Comparer
类 :使用Create方法创建比较器,在反序列化时重构集合排序
攻击载荷构造步骤:
- 使用Comparison
类返回委托 - 使用Delegate或MulticastDelegate的Combine方法将委托添加到链中
- 使用Comparer
的Create方法创建比较器 - 选择SortedSet
类作为载体,利用其反序列化时重构排序的特性
四、漏洞利用与代码审计
4.1 漏洞利用点
- Deserialize方法:直接反序列化不可信的XML数据
- ReadObject方法:从不可信源读取XML流并反序列化
4.2 代码审计要点
审计时需要重点关注以下场景:
- 从不可信源读取XML数据并反序列化:
// 危险示例1
public void DeserializeData(string xml)
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
object obj = serializer.Deserialize(ms);
// 处理反序列化后的对象
}
}
// 危险示例2
public void ReadObjectData(Stream stream)
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
object obj = serializer.ReadObject(stream);
// 处理反序列化后的对象
}
- 从文件读取XML内容并反序列化:
public void LoadFromFile(string path)
{
string xml = File.ReadAllText(path);
NetDataContractSerializer serializer = new NetDataContractSerializer();
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
object obj = serializer.Deserialize(ms);
// 处理反序列化后的对象
}
}
五、漏洞复现与防御
5.1 漏洞复现
- 构造恶意XML载荷(完整POC见原文)
- 将载荷传递给存在漏洞的反序列化方法
- 成功执行任意命令(如弹出计算器)
5.2 防御措施
- 避免反序列化不可信数据:不要反序列化来自不可信源的XML数据
- 使用安全替代方案:
- 使用DataContractSerializer代替NetDataContractSerializer
- 使用Json.NET等更安全的序列化器
- 输入验证:对反序列化的XML数据进行严格验证
- 类型限制:使用SerializationBinder限制反序列化的类型
六、总结
- NetDataContractSerializer由于包含完整的CLR类型信息,比DataContractSerializer更容易触发反序列化漏洞
- 通过MulticastDelegate和Comparer
的组合可以实现远程代码执行 - 在代码审计时需要特别关注ReadObject和Deserialize方法的使用场景
- 在必须使用NetDataContractSerializer的场景下,应采取严格的安全措施防止反序列化攻击
附录:相关资源
- .NET反序列化系列课程
- .NET安全知识库
- 原文提供的完整攻击POC(见原文XML部分)