一、前言
NetDataContractSerializer和DataContractSerializer一样用于序列化和反序列化Windows Communication Foundation (WCF) 消息中发送的数据。两者之间存在一个重要区别:NetDataContractSerializer 包含了CLR,通过CLR类型添加额外信息并保存引用来支持类型精确,而DataContractSerializer 则不包含。 因此,只有在序列化和反序列化端使用相同的 CLR 类型时,才能使用 NetDataContractSerializer。若要序列化对象使用 WriteObject或者Serialize方法, 若要反序列化 XML流使用 ReadObject或者Deserialize方法。在某些场景下读取了恶意的XML流就会造成反序列化漏洞,从而实现远程RCE攻击,本文笔者从原理和代码审计的视角做了相关介绍和复现。
前文回顾:
.NET高级代码审计之XmlSerializer反序列化漏洞
.NET高级代码审计(第二课) Json.Net反序列化漏洞
.NET高级代码审计(第四课)JavaScriptSerializer反序列化漏洞
.NET高级代码审计(第五课).NET Remoting反序列化漏洞
二、SoapFormatter序列化
SoapFormatter类实现的IFormatter接口中定义了核心的Serialize方法可以非常方便的实现.NET对象与SOAP流之间的转换,可以将数据保存为XML文件,官方提供了两个构造方法。
下面还是用老案例来说明问题,首先定义TestClass对象
定义了三个成员,并实现了一个静态方法ClassMethod启动进程。 序列化通过创建对象实例分别给成员赋值
常规下使用Serialize得到序列化后的SOAP流,通过使用XML命名空间来持久化原始程序集,例如下图TestClass类的开始元素使用生成的xmlns进行限定,关注a1 命名空间
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:TestClass id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/WpfApp1/WpfApp1%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<classname id="ref-3">360</classname>
<name id="ref-4">Ivan1ee</name>
<age>18</age>
</a1:TestClass>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
三、SoapFormatter反序列化
3.1 反序列化原理和用法
SoapFormatter类反序列化过程是将SOAP消息流转换为对象,通过创建一个新对象的方式调用Deserialize多个重载方法实现的,查看定义得知实现了IRemotingFormatter、IFormatter接口,
查看IRemotingFormatter接口定义得知也是继承了IFormatter
笔者通过创建新对象的方式调用Deserialize方法实现的具体实现代码可参考以下
反序列化后得到TestClass类的成员Name的值。
3.2 攻击向量—ActivitySurrogateSelector
在SoapFormatter类的定义中除了构造函数外,还有一个SurrogateSelector属性,SurrogateSelector便是代理选择器,序列化代理的好处在于一旦格式化器要对现有类型的实例进行反序列化,就调用由代理对象自定义的方法。查看得知实现了ISurrogateSelector接口,定义如下
因为序列化代理类型必须实现System.Runtime.Serialization.ISerializationSurrogate接口,ISerializationSurrogate在Framework ClassLibrary里的定义如下:
代码中判断类型解析器IsSerializable属性是否可用,如果可用直接基类返回,如果不可用就获取派生类System.Workflow.ComponentModel.Serialization.ActivitySurrogateSelector的类型,然后交给Activator创建实例,再回到GetObjectData方法体内,另外为了对序列化数据进行完全控制,就需要实现Serialization.ISeralizable接口,定义如下:
有关更多的介绍请参考《.NET高级代码审计第二课 Json.Net反序列化漏洞》,在实现自定义反序列类的时通过构造方法读取攻击者提供的PocClass类
下图定义了PayloadClass类实现ISerializable接口,然后在GetObjectData方法里又声明泛型List集合接收byte类型的数据
将PocClass对象添加到List集合,声明泛型使用IEnumerable集合map_type接收程序集反射得到的Type并返回IEnumerable类型,最后用Activator.CreateInstance创建实例保存到e3此时是一个枚举集合的迭代器。
上图将变量e3填充到了分页控件数据源,查看PageDataSource类定义一目了然,
除此之外System.Runtime.Remoting.Channels.AggregateDictionary返回的类型支持IDictionary,然后实例化对象DesignerVerb并随意赋值,此类主要为了配合填充MenuCommand类properties属性的值,最后为哈希表中的符合条件的buckets赋值。
接下来用集合添加数据源DataSet,DataSet和DataTable对象继承自System.ComponentModel.MarshalByValueComponent类,可序列化数据并支持远程处理ISerializable接口,这是ADO.NET对象中仅有支持远程处理的对象,并以二进制格式进行持久化。
更改属性DataSet.RemotingFormat值为SerializationFormat.Binary,更改属性DataSet.CaseSensitive为false等,再调用BinaryFormatter序列化List集合,如下图。
因为指定了RemotingFormat属性为Binary,所以引入了BinaryFormatter格式化器并指定属性SurrogateSelector代理器为自定义的MySurrogateSelector类。序列化后得到SOAP-XML,再利用SoapFormatter对象的Deserialize方法解析读取文件内容的流数据,成功弹出计算器
3.3 攻击向量—PSObject
由于笔者的Windows主机打过了CVE-2017-8565(Windows PowerShell远程代码执行漏洞)的补丁,利用不成功,所以在这里不做深入探讨,有兴趣的朋友可以自行研究。有关于补丁的详细信息参考:https://support.microsoft.com/zh-cn/help/4025872/windows-powershell-remote-code-execution-vulnerability
四、代码审计
4.1 XML载入
从代码审计的角度找到漏洞的EntryPoint,传入XML,就可以被反序列化,这种方式也是很常见的,需要关注一下,LoadXml直接载入xml数据,这个点也可以造成XXE漏洞。例如这段代码:
这种污染点漏洞攻击成本很低,攻击者只需要控制传入字符串参数source便可轻松实现反序列化漏洞攻击,弹出计算器。
4.2 File读取
这段是摘自某个应用的代码片段,在审计的时候只需要关注DeserializeSOAP方法中传入的path变量是否可控。
五、总结
实际开发中SoapFormatter 类从.NET Framework 2.0开始,这个类已经渐渐过时了,开发者选择它的概率也越来越少,官方注明用BinaryFormatter来替代它,下篇笔者接着来介绍BinaryFormatter反序列化漏洞。最后.NET反序列化系列课程笔者会同步到 https://github.com/Ivan1ee/ 、https://ivan1ee.gitbook.io/ ,后续笔者将陆续推出高质量的.NET反序列化漏洞文章,欢迎大伙持续关注,交流。
*本文作者:Ivan1ee@360云影实验室,转载请注明来自FreeBuf.COM
来源:freebuf.com 2019-04-15 11:26:55 by: 云影实验室
请登录后发表评论
注册