欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

程序员文章站 2023-02-24 14:54:24
0X00 前言 在.NET 框架中的 XmlSerializer 类是一种很棒的工具,它是将高度结构化的 XML 数据映射为 .NET 对象。XmlSerializer类在程序中通过单个 API 调用来执行 XML 文档和对象之间的转换。转换的映射规则在 .NET 类中通过元数据属性来表示,如果程序 ......

0x00 前言

在.net 框架中的 xmlserializer 类是一种很棒的工具,它是将高度结构化的 xml 数据映射为 .net 对象。xmlserializer类在程序中通过单个 api 调用来执行 xml 文档和对象之间的转换。转换的映射规则在 .net 类中通过元数据属性来表示,如果程序开发人员使用type类的静态方法获取外界数据,并调用deserialize反序列化xml数据就会触发反序列化漏洞攻击(例如dotnetnuke 任意代码执行漏洞 cve-2017-9822),本文笔者从原理和代码审计的视角做了相关脑图介绍和复现。

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

0x01 xmlserializer序列化

.net 框架中 system.xml.serialization 命名空间下的xmlserializer类可以将 xml 文档绑定到 .net 类的实例,有一点需要注意它只能把对象的公共属性和公共字段转换为xml元素或属性,并且由两个方法组成:serialize() 用于从对象实例生成 xml;deserialize() 用于将 xml 文档分析成对象图,被序列化的数据可以是数据、字段、数组、以及xmlelement和xmlattribute对象格式的内嵌xml。具体看下面demo

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

xmlelement指定属性要序列化为元素,xmlattribute指定属性要序列化为特性,xmlroot特性指定类要序列化为根元素;通过特性类型的属性、影响要生成的名称、名称空间和类型。再创建一个testclass类的实例填充其属性序列化为文件,xmlserializer.serialize方法重载可以接受stream、textwrite、xmlwrite类,最终生成的xml文件列出了testclass元素、classname特性和其它存储为元素的属性:

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

0x02 xmlserialize反序列化

反序列过程:将xml文件转换为对象是通过创建一个新对象的方式调用xmlserializer.deserialize方法实现的,在序列化最关键的一环当属new xmlserializer构造方法里所传的参数,这个参数来自system.type类,通过这个类可以访问关于任意数据类型的信息,指向任何给定类型的type引用有以下三种方式。

2.1、typeof

实例化xmlserializer传入的typeof(testclass) 表示获取testclass类的type,typeof是c#中的运算符,所传的参数只能是类型的名称,而不能是实例化的对象,如下demo

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

通过typeof获取到type之后就能得到该类中所有的methods、members等信息。下图运行debug时,弹出消息对话框显示当前成员name的值。

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

2.2、object.type

在.net里所有的类最终都派生自system.object,在object类中定义了许多公有和受保护的成员方法,这些方法可用于自己定义的所有其他类中,gettype方法就是其中的一个,该方法返回从system.type派生的类的一个实例,因为可以提供对象成员所属类的信息,包括基本类型、方法、属性等,上述案例中实例化testclass,再获取当前实例的type,如下demo

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

2.3、type.gettype

第三种方法是type类的静态方法gettype,这个方法允许外界传入字符串,这是重大利好,只需要传入全限定名就可以调用该类中的方法、属性等

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

type.gettype传入的参数也是反序列化产生的漏洞污染点,接下来就是要去寻找可以被用来攻击使用的类。

 

0x03 打造攻击链

首先放上攻击链打造成功后的完整demo,这段demo可以复用在任意地方(这里不涉及.net core、mvc),如下图

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

只要xmlserializer存在反序列化漏洞就可用下面demo中的内容,涉及到三个主要的技术点,以下分别来介绍原理。

 

3.1、objectdataprovider

objectdataprovider类,它位于system.windows.data命名空间下,可以调用任意被引用类中的方法,提供成员objectinstance用类似实例化类、成员methodname

调用指定类型的方法的名称、成员methodparameters表示传递给方法的参数,参考下图

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

再给testclass类定义一个classmethod方法,代码实现调用system.diagnostics.process.start启动新的进程弹出计算器。如果用xmlserializer直接序列化会抛出异常,因为在序列化过程中objectinstance这个成员类型未知,不过可以使用expandedwrapper扩展类在系统内部预先加载相关实体的查询来避免异常错误,改写demo

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

生成data.xml内容如下:

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

攻击链第一步就算完成,但美中不足的是因笔者在测试环境下新建的testclass类存在漏洞,但在生产情况下是非常复杂的,需要寻求web程序中存在脆弱的攻击点,为了使攻击成本降低肯定得调用系统类去达到命令执行,所以需要引入下面的知识。

 

3.2、resourcedictionary

resourcedictionary,也称为资源字典通常出现在wpf或uwp应用程序中用来在多个程序集间共享静态资源。既然是wpf程序,必然设计到前端ui设计语言xaml。 xaml全称extensible application markup language (可扩展应用程序标记语言) 基于xml的,且xaml是以一个树形结构作为整体,如果对xml了解的话,就能很快的掌握,例如看下面demo

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

  • 第一个标签resourcedictionary,xmlns:runtime表示读取system.diagnostics命令空间的名称起个别名为runtime

  • 第二个标签objectdataprovider指定了三个属性,x:key便于条件检索,意义不大但必须得定义;objecttype 用来获取或设置要创建其实例的对象的类型,并使用了xaml扩展;x:type相当于c#中typeof运算符功能,这里传递的值是system.diagnostics.process;methodname用来获取或设置要调用的方法的名称,传递的值为system.diagnostics.process.start方法用来启动一个进程。

  • 第三个标签objectdataprovider.methodparameters内嵌了两个方法参数标签,通过system:string分别指定了启动文件和启动时所带参数供start方法使用。

介绍完攻击链中resourcedictionary后,攻击的payload主体已经完成,接下来通过xamlreader这个系统类所提供的xml解析器来实现攻击。

 

3.3、xamlreader

xamlreader位于system.windows.markup空间下,顾名思义就是用来读取xaml文件,它是默认的xaml读取器,通过load读取stream流中的xaml数据,并返回作为根对象,而另外一个parse方法读取指定字符串中的xaml输入,也同样返回作为根对象,自然parse方法是我们关心和寻求的。

 

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

只需使用objectdataprovider的objectinstance方法实例化xamlreader,再指定methodname为parse,并且给methodparameters传递序列化之后的资源字典数据,这样就可以完成xmlserializer反序列化攻击链的打造。

 

0x04 代码审计视角

从代码审计的角度其实很容易找到漏洞的污染点,通过前面几个小节的知识能发现序列化需要满足一个关键条件type.gettype,程序必须通过type类的静态方法gettype,例如以下demo

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

首先创建xmldocument对象载入xml,变量typename通过xpath获取到item节点的type属性的值,并传给了type.gettype,紧接着读取item节点内的所有xml数据,最终交给deserialize方法反序列化,这是一个近乎完美的利用点。再来看笔者在github上收集到的xmlserializer反序列化类:xmlserializeutil.cs

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

此处值参数类型为type,代码本身没有问题,问题在于程序开发者可能会先定义一个字符串变量来接受传递的type值,通过type.gettype(string)返回 type对象再传递进deserializexml,在代码审计的过程中也需要关注此处type的来源。

 

0x05 案例复盘

最后再通过下面案例来复盘整个过程,全程展示在vs里调试里通过反序列化漏洞弹出计算器。

  1.输入http://localhost:5651/default?node=root&value=type加载了远程的(192.168.231.135)1.xml文件

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

2.    通过xmlhelper.getvalue得到root节点下的所有xml数据

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

 

3. 这步最关键,得到root节点的type属性,并提供给gettype方法,xmlserializer对象实例化成功

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

 

4.    xmlserializer.deserialize(xmlreader)成功调出计算器

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

 

最后附上动图

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞

 

0x06 总结

由于xmlserializer是系统默认的反序列类,所以在实际开发中使用率还是比较高的,攻击者发现污染点可控的时候,可以从两个维度去寻找利用的点,第一从web应用程序中寻求可以执行命令或者写webshell的类和方法;第二就是本文中所说的利用objectdataprovider、resourcedictionary、xamlreader组成的攻击链去执行命令或者反弹shell ,最后.net反序列化系列课程笔者会同步到 https://github.com/ivan1ee/、https://ivan1ee.gitbook.io/,后续笔者将陆续推出高质量的.net反序列化漏洞文章,大致课程大纲如下图

.NET高级代码审计(第一课)XmlSerializer反序列化漏洞