.NET高级代码审计(第八课)SoapFormatter反序列化漏洞
SoapFormatter格式化器和下节课介绍的BinaryFormatter格式化器都是.NET内部实现的序列化功能的类,SoapFormatter直接派生自System.Object,位于命名空间System.Runtime.Serialization.Formatters.Soap,并实现IRemotingFormatter、IFormatter接口,用于将对象图持久化为一个SOAP流,SOAP是基于XML的简易协议,让应用程序在HTTP上进行信息交换用的。但在某些场景下处理了不安全的SOAP流会造成反序列化漏洞从而实现远程RCE攻击,本文笔者从原理和代码审计的视角做了相关介绍和复现。
0x01 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">360classname>
name id="ref-4">Ivan1eename>
age>18age>
a1:TestClass>
SOAP-ENV:Body>
SOAP-ENV:Envelope>
0x02 SoapFormatter反序列化
2.1、反序列化用法
SoapFormatter类反序列化过程是将SOAP消息流转换为对象,通过创建一个新对象的方式调用Deserialize多个重载方法实现的,查看定义得知实现了IRemotingFormatter、IFormatter接口,
查看IRemotingFormatter接口定义得知也是继承了IFormatter
笔者通过创建新对象的方式调用Deserialize方法实现的具体实现代码可参考以下
反序列化后得到TestClass类的成员Name的值。
2.2、攻击向量—ActivitySurrogateSelector
在SoapFormatter类的定义中除了构造函数外,还有一个SurrogateSelector属性, SurrogateSelector便是代理选择器,序列化代理的好处在于一旦格式化器要对现有类型的实例进行反序列化,就调用由代理对象自定义的方法。查看得知实现了ISurrogateSelector接口,定义如下
因为序列化代理类型必须实现System.Runtime.Serialization.ISerializationSurrogate接口,ISerializationSurrogate在Framework Class Library里的定义如下:
图中的GetObjectData方法在对象序列化时进行调用,目的将值添加到SerializationInfo集合里,而SetObjectData方法用于反序列化,调用这个方法的时候需要传递一个SerializationInfo对象引用,换句话说就是使用SoapFormatter类的Serialize方法的时候会调用GetObjectData方法,使用Deserialize会调用SetObjectData方法。SoapFormatter类还有一个非常重要的属性SurrogateSelector,定义如下
在序列化对象的时候如果属性SurrogateSelector属性的值非NULL便会以这个对象的类型为参数调用其GetSurrogate方法,如果此方法返回一个有效的对象ISerializationSurrogate,这个对象对找到的类型进行反序列化,这里就是一个关键的地方,我们要做的就是实现重写ISerializationSurrogate调用自定义代码,如下Demo