.net反序列化萌新入门--SoapFormatter

.net反序列化萌新入门--SoapFormatter

简介

SoapFormatter位于System.Runtime.Serialization.Formatters.Soap.dll,以 SOAP 格式序列化和反序列化对象或连接对象的整个图,并实现了IRemotingFormatter、IFormatter接口。SOAP即Simple Object Access Protocol,简单对象访问协议,基于XML协议。SOAP是开放的协议,可以跨平台的其他程序也可以使用SoapFormatter序列化的文件,SoapFormatter与BinaryFormatter的区别是:SoapFormatter不能序列化泛型类型。与BinaryFormatter一样在序列化时不需要向序列化器指定序列化对象的类型。而XmlSerializer需要向XML序列化器指定序列化对象的类型。SoapFormatter的缺点是生成的文件较大,现在已很少使用。


01 SoapFormatter的序列化和反序列化

编写以下测试用例,[Serializable]标识表示此类可被序列化。

.net反序列化萌新入门--SoapFormatter


序列化并输出序列化后的xml和反序列化结果。

.net反序列化萌新入门--SoapFormatter


执行结果如下

.net反序列化萌新入门--SoapFormatter


02 ActivitySurrogateSelector

在SoapFormatter类的定义有一个SurrogateSelector 属性,SurrogateSelector即代理选择器,作用是帮助格式化程序选择要将序列化或反序列化进程委托给的序列化代理项。ISurrogateSelector定义如下。

.net反序列化萌新入门--SoapFormatter


ISurrogateSelector中ISerializationSurrogate是必须实现的接口,其中GetObjectData方法在对象序列化时进行调用,而SetObjectData方法用于反序列化。关于ISurrogateSelector和ISerializationSurrogate的详细介绍可以参考微软官网:https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.surrogateselector?view=net-6.0。我们这里只需要关注一点:代理选择器的判定在[Serializable]特性之前,可以实现将原本不能被序列化的类用来序列化和反序列化。

.net反序列化萌新入门--SoapFormatter


看到GetObjectData方法,其实已经可以接上《.net反序列化萌新入门--Json.Net》中的WindowsIdentity攻击链了,但这里我们要讲一个不同的攻击链ActivitySurrogateSelector。前面介绍了代理选择器,但在实际环境中我们无法指定代理选择器,也就无法正确的反序列化,这就用到了ActivitiySurrogateSelector,添加以下选择代理器,代码来自ysoserial的ActivitySurrogateSelectorGenerator.cs。

.net反序列化萌新入门--SoapFormatter


此时将测试用例中的[Serializable]标识删除,依然可以成功序列化,并且以下代码中soapFormatter2没有指定代理选择器也可以反序列化成功。代码中关闭DisableActivitySurrogateSelectorTypeCheck类型检查是为了绕过高版本框架对ActivitySurrogateSelector类滥用的限制。

.net反序列化萌新入门--SoapFormatter


使用dnspy分析序列化成功的原因,首先是soapFormatter.SurrogateSelector = new MySurrogateSelector()中代理选择器被赋值给this.m_surrogates。

.net反序列化萌新入门--SoapFormatter


在序列化时代理选择器传入ObjectWriter。

.net反序列化萌新入门--SoapFormatter


在ObjectWriter中,代理选择器用于WriteObjectInfo的序列化。

.net反序列化萌新入门--SoapFormatter


跟进WriteObjectInfo.Serialize(),对代理选择器进行判定后,代码最终走到了this.serializationSurrogate.GetObjectData()中。

.net反序列化萌新入门--SoapFormatter


这就来到了ActivitySurrogateSelector内实现的GetObjectData,在GetObjectData调用SetType将类型设置为子类ObjectSerializedRef。而ObjectSerializedRef是可以被序列化的。

.net反序列化萌新入门--SoapFormatter


总的来说,ActivitySurrogateSelector代理选择器中ObjectSurrogate.GetObjectData()将原本不可被序列化的对象存储到ObjectSerializedRef这个可以被序列化的类实例中,由此实现序列化原本不可序列化的类。如果我们可以加载自己的程序集,那么在new实例的时候触发构造函数就会执行恶意代码。作者将目标转向了LINQ类,如果我们替换了LINQ中的委托,通过替换委托来加载程序集并创建实例,那么触发LINQ之后就会执行恶意代码,构造攻击链:byte[] -> Assembly.Load -> Assembly -> Assembly.GetType -> Type[] -> Activator.CreateInstance。此攻击链中this.assemblyBytes = File.ReadAllBytes(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "e.dll")),使用Assembly.Load将恶意类加载到byte[]数组中,利用Select()方法拿到IEnumerable,通过创建委托配合SelectMany()拿到Assembly.GetTypes(),最后Activator.CreateInstance创建实例就完成了整个LINQ的链。

.net反序列化萌新入门--SoapFormatter


以上是作者一开始的思路,但在不同的.net框架中可能无法兼容,作者使用了System.Linq.Enumerable+WhereSelectEnumerableIterator`2修复这个问题用来兼容不同版本。

.net反序列化萌新入门--SoapFormatter


最终执行链为:Assembly.Load(byte[]).GetTypes().GetEnumerator().{MoveNext(),get_Current()} -> Activator.CreateInstance()

.net反序列化萌新入门--SoapFormatter


构造了LINQ链还有一个问题,因为LINQ的延迟执行特点,只有当我们枚举结果集合里的元素时,才会加载程序集并创建类型实例。作者又找到一种方法,使得在反序列化时执行ToString() 函数,然后找到一条链从ToString() 到 IEnumerable。

IEnumerable -> PagedDataSource -> ICollection

ICollection -> AggregateDictionary -> IDictionary

IDictionary -> DesignerVerb -> ToString


代码实现如下,使用PagedDataSource类将IEnumerable 类型转换为 ICollection类型,其中的dataSource字段为IEnumerable 类型。然后将 ICollection 类型转换为 IDictionary 类型,DesignerVerb 类型的ToString() 函数会枚举 IDictionary,需要使用反射插入IDictionary。

.net反序列化萌新入门--SoapFormatter


最后使用Hashtable启动整条链,在对Hashtable 类进行反序列化的时候,它将会重建密钥集。反射修改buckets字段的key值,将key是string的替换为verb,由此两个key相同,hash相同会异常,从而调用Environment.GetResourceString(),在GetResourceString()中string.Format会触发ToString()。

.net反序列化萌新入门--SoapFormatter


到这里整条链其实已经完成,可以执行命令但是会触发异常造成报错,所以作者又包装了一层System.Windows.Forms.AxHost.State,

.net反序列化萌新入门--SoapFormatter


可以看到PropertyBagBinary在反序列化时增加了try-catch处理,从而解决了报错的问题。

.net反序列化萌新入门--SoapFormatter


03 总结

本文介绍了SoapFormatter反序列化相关知识和ActivitySurrogateSelector攻击链,ActivitySurrogateSelectorFromFile链与ActivitySurrogateSelector基本相同,只是可以执行自己编写的程序集。从审计角度来看,如果能控制SoapFormatter.Deserialize()传入的XML数据就可以轻松实现反序列化。


参考

https://xz.aliyun.com/t/9595

https://zhuanlan.zhihu.com/p/333316520

https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.formatters.soap.soapformatter?redirectedfrom=MSDN&view=netframework-4.8.1

https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.surrogateselector?view=net-6.0


原文始发于微信公众号(M01N Team):.net反序列化萌新入门--SoapFormatter

版权声明:admin 发表于 2023年1月11日 下午6:01。
转载请注明:.net反序列化萌新入门--SoapFormatter | CTF导航

相关文章

暂无评论

暂无评论...