.NET高级代码审计(第一课)XmlSerializer反序列化漏洞
0X00 前言
在.NET 框架中的 XmlSerializer 类是一种很棒的工具,它是将高度结构化的 XML 数据映射为 .NET 对象。XmlSerializer类在程序中通过单个 API 调用来执行 XML 文档和对象之间的转换。转换的映射规则在 .NET 类中通过元数据属性来表示,如果程序开发人员使用Type类的静态方法获取外界数据,并调用Deserialize反序列化xml数据就会触发反序列化漏洞攻击(例如DotNetNuke 任意代码执行漏洞 CVE-2017-9822),本文笔者从原理和代码审计的视角做了相关脑图介绍和复现。
0X01 XmlSerializer序列化
.NET 框架中 System.Xml.Serialization 命名空间下的XmlSerializer类可以将 XML 文档绑定到 .NET 类的实例,有一点需要注意它只能把对象的公共属性和公共字段转换为XML元素或属性,并且由两个方法组成:Serialize() 用于从对象实例生成 XML;Deserialize() 用于将 XML 文档分析成对象图,被序列化的数据可以是数据、字段、数组、以及XmlElement和XmlAttribute对象格式的内嵌XML。具体看下面demo
XmlElement指定属性要序列化为元素,XmlAttribute指定属性要序列化为特性,XmlRoot特性指定类要序列化为根元素;通过特性类型的属性、影响要生成的名称、名称空间和类型。再创建一个TestClass类的实例填充其属性序列化为文件,XmlSerializer.Serialize方法重载可以接受Stream、TextWrite、XmlWrite类,最终生成的XML文件列出了TestClass元素、Classname特性和其它存储为元素的属性:
0x02 XmlSerialize反序列化
反序列过程:将xml文件转换为对象是通过创建一个新对象的方式调用XmlSerializer.Deserialize方法实现的,在序列化最关键的一环当属new XmlSerializer构造方法里所传的参数,这个参数来自System.Type类,通过这个类可以访问关于任意数据类型的信息,指向任何给定类型的Type引用有以下三种方式。
2.1、typeof
实例化XmlSerializer传入的typeof(TestClass) 表示获取TestClass类的Type,typeof是C#中的运算符,所传的参数只能是类型的名称,而不能是实例化的对象,如下Demo
通过typeof获取到Type之后就能得到该类中所有的Methods、Members等信息。下图运行Debug时,弹出消息对话框显示当前成员Name的值。
2.2、object.Type
在.NET里所有的类最终都派生自System.Object,在Object类中定义了许多公有和受保护的成员方法,这些方法可用于自己定义的所有其他类中,GetType方法就是其中的一个,该方法返回从System.Type派生的类的一个实例,因为可以提供对象成员所属类的信息,包括基本类型、方法、属性等,上述案例中实例化TestClass,再获取当前实例的Type,如下Demo
2.3、Type.GetType
第三种方法是Type类的静态方法GetType,这个方法允许外界传入字符串,这是重大利好,只需要传入全限定名就可以调用该类中的方法、属性等
Type.GetType传入的参数也是反序列化产生的漏洞污染点,接下来就是要去寻找可以被用来攻击使用的类。
0X03 打造攻击链
首先放上攻击链打造成功后的完整Demo,这段Demo可以复用在任意地方(这里不涉及.NET Core、MVC),如下图
只要XmlSerializer存在反序列化漏洞就可用下面Demo中的内容,涉及到三个主要的技术点,以下分别来介绍原理。
3.1、ObjectDataProvider
ObjectDataProvider类,它位于System.Windows.Data命名空间下,可以调用任意被引用类中的方法,提供成员ObjectInstance用类似实例化类、成员MethodName
调用指定类型的方法的名称、成员MethodParameters表示传递给方法的参数,参考下图
再给TestClass类定义一个ClassMethod方法,代码实现调用System.Diagnostics.Process.Start启动新的进程弹出计算器。如果用XmlSerializer直接序列化会抛出异常,因为在序列化过程中ObjectInstance这个成员类型未知,不过可以使用ExpandedWrapper扩展类在系统内部预先加载相关实体的查询来避免异常错误,改写Demo
生成data.xml内容如下:
攻击链第一步就算完成,但美中不足的是因笔者在测试环境下新建的TestClass类存在漏洞,但在生产情况下是非常复杂的,需要寻求Web程序中存在脆弱的攻击点,为了使攻击成本降低肯定得调用系统类去达到命令执行,所以需要引入下面的知识。
3.2、ResourceDictionary
ResourceDictionary,也称为资源字典通常出现在WPF或UWP应用程序中用来在多个程序集间共享静态资源。既然是WPF程序,必然设计到前端UI设计语言XAML。 XAML全称Extensible Application Markup Language (可扩展应用程序标记语言) 基于XML的,且XAML是以一个树形结构作为整体,如果对XML了解的话,就能很快的掌握,例如看下面Demo
第一个标签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方法是我们关心和寻求的。
只需使用ObjectDataProvider的ObjectInstance方法实例化XamlReader,再指定MethodName为Parse,并且给MethodParameters传递序列化之后的资源字典数据,这样就可以完成XmlSerializer反序列化攻击链的打造。
0x04 代码审计视角
从代码审计的角度其实很容易找到漏洞的污染点,通过前面几个小节的知识能发现序列化需要满足一个关键条件Type.GetType,程序必须通过Type类的静态方法GetType,例如以下demo
首先创建XmlDocument对象载入xml,变量typeName通过Xpath获取到Item节点的type属性的值,并传给了Type.GetType,紧接着读取Item节点内的所有Xml数据,最终交给Deserialize方法反序列化,这是一个近乎完美的利用点。再来看笔者在github上收集到的XmlSerializer反序列化类:XmlSerializeUtil.cs
此处值参数类型为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文件
2. 通过xmlHelper.GetValue得到root节点下的所有XML数据
3. 这步最关键,得到root节点的type属性,并提供给GetType方法,XmlSerializer对象实例化成功
4. XmlSerializer.Deserialize(xmlReader)成功调出计算器
最后附上动图
0x06 总结
由于XmlSerializer是系统默认的反序列类,所以在实际开发中使用率还是比较高的,攻击者发现污染点可控的时候,可以从两个维度去寻找利用的点,第一从Web应用程序中寻求可以执行命令或者写WebShell的类和方法;第二就是本文中所说的利用ObjectDataProvider、ResourceDictionary、XamlReader组成的攻击链去执行命令或者反弹Shell ,最后.NET反序列化系列课程笔者会同步到 https://github.com/Ivan1ee/、https://ivan1ee.gitbook.io/,后续笔者将陆续推出高质量的.NET反序列化漏洞文章,大致课程大纲如下图
.NET高级代码审计(第一课)XmlSerializer反序列化漏洞的更多相关文章
- .NET高级代码审计(第五课) .NET Remoting反序列化漏洞
0x00 前言 最近几天国外安全研究员Soroush Dalili (@irsdl)公布了.NET Remoting应用程序可能存在反序列化安全风险,当服务端使用HTTP信道中的SoapServerF ...
- .NET高级代码审计(第三课)Fastjson反序列化漏洞
0X00 前言 Java中的Fastjson曾经爆出了多个反序列化漏洞和Bypass版本,而在.Net领域也有一个Fastjson的库,作者官宣这是一个读写Json效率最高的的.Net 组件,使用内置 ...
- .NET高级代码审计(第四课) JavaScriptSerializer反序列化漏洞
0X00 前言 在.NET处理 Ajax应用的时候,通常序列化功能由JavaScriptSerializer类提供,它是.NET2.0之后内部实现的序列化功能的类,位于命名空间System.Web.S ...
- .NET高级代码审计(第二课) Json.Net反序列化漏洞
0X00 前言 Newtonsoft.Json,这是一个开源的Json.Net库,官方地址:https://www.newtonsoft.com/json ,一个读写Json效率非常高的.Net库,在 ...
- Python的一些高级特性以及反序列化漏洞
0x01 简述 文章主要记录一下python高级特性以及安全相关的问题 python作为脚本语言,其作为高级语言是由c语言开发的,关于python的编译和链接可以看向这里https://github. ...
- [代码审计]四个实例递进php反序列化漏洞理解【转载】
原作者:大方子 原文链接:https://blog.csdn.net/nzjdsds/article/details/82703639 0x01 索引 最近在总结php序列化相关的知识,看了好多前辈师 ...
- 【JavaWeb】CVE-2016-4437 Shiro反序列化漏洞分析及代码审计
Shiro反序列化漏洞分析及代码审计 漏洞简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理. Apache Shiro默认使用了CookieRe ...
- PHP反序列化漏洞代码审计—学习资料
1.什么是序列化 A.PHP网站的定义: 所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示.unserialize()函数能够重新把字符串变回php原来的值. ...
- php代码审计9审计反序列化漏洞
序列化与反序列化:序列化:把对象转换为字节序列的过程称为对象的序列化反序列化:把字节序列恢复为对象的过程称为对象的反序列化 漏洞成因:反序列化对象中存在魔术方法,而且魔术方法中的代码可以被控制,漏洞根 ...
随机推荐
- SpringBoot中使用Redis
在SpringBoot中使用Redis,思路如下: 查询时先查Redis缓存,如果缓存中存在信息,就直接从缓存中获取. 如果缓存中没有相关信息,就去数据库中查找,查完顺便将信息存放进缓存里,以便下一次 ...
- Spring依赖注入:基于xml配置
基础接口 BeanFactory.ApplicationContext. BeanFactory用于创建并管理.获取各种类的对象. ApplicationContext从BeanFactory派生而来 ...
- php页面的基本语法
概述: 1. PHP 脚本在服务器上执行,然后将纯 HTML 结果发送回浏览器. 2. PHP 脚本以 <?php 开始,以 ?> 结束,可以放到文档中的任何位置. 3. 当 PHP 解析 ...
- iOS 网络请求中的空类型字符串转换
创建一个工具类, .h: #import <Foundation/Foundation.h> @interface MySetNullWithStrTool : NSObject +( ...
- 绑定服务-----------binderService TimerTask的使用
绑定服务 服务中通过定义Binder对象的子类让这个子类成为桥梁 在onBind()中返回子类对象 这样就可以在activity中调用这个子类的方法 在Activity中通过ServiceConn ...
- geoserver 文件系统
我介绍了GeoServer的一些重要的资源以及它们的访问接口,现在来看看它们的保存形式.GeoServer的数据没有保存到数据库,而是文件系统,这让我们的学习轻松不少.默认情况下,GeoServer的 ...
- Codeforces 679B. Barnicle 模拟
B. Barnicle time limit per test: 1 second memory limit per test :256 megabytes input: standard input ...
- Debian 使用 cron 执行定时任务
在linux下有两种方法来让一个命令或者脚本执行: crontab : 执行一个任务一次或者多次. at : 只执行一次. crontab是通过读取一个crontab文件来工作,这是一个普通的文本文件 ...
- maven 打包 OutOfMemoryError
maven 打包 OutOfMemoryError [ERROR] Java heap space -> [Help 1] [ERROR] [ERROR] To see the full sta ...
- 解决Android启动显示空白界面的问题
Android程序启动时,第一个看的界面并不是我们的指定的第一个Activity界面,而是显示了一个空白的界面,带标题栏的,但是界面什么内容都没有,这个界面只显示不到1秒左右的时间就会切换到我们的第一 ...