当前位置: 移动技术网 > IT编程>开发语言>.net > .NET高级代码审计(第一课)XmlSerializer反序列化漏洞

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

2019年03月10日  | 移动技术网IT编程  | 我要评论

白莉灵子,烈车战队特急者,杨筱雪

0x00 前言

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

图片1.png

0x01 xmlserializer序列化

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

图片2.png

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

图片3.png

0x02 xmlserialize反序列化

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

2.1、typeof

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

图片4.png

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

图片5.png

2.2、object.type

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

图片6.png

2.3、type.gettype

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

图片7.png

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

 

0x03 打造攻击链

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

图片8.png

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

 

3.1、objectdataprovider

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

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

图片9.png

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

图片10.png

生成data.xml内容如下:

图片11.png

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

 

3.2、resourcedictionary

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

图片12.png

  • 第一个标签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方法是我们关心和寻求的。

 

图片13.png

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

 

0x04 代码审计视角

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

图片14.png

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

图片15.png

此处值参数类型为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文件

图片16.png

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

图片17.png

 

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

图片18.png

 

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

图片19.png

 

最后附上动图

 

0x06 总结

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

image.png

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网