某反序列化漏洞分析与复现工作
0x00 概述
GENESIS64 软件的多个版本存在反序列化漏洞,影响多个组件,例如:
根据 CVE 漏洞相关描述,下载对应 GENESIS 软件版本搭建环境,进行漏洞分析与复现工作。
【一>所有资源获取<一】1、200 份很多已经买不到的绝版电子书 2、30G 安全大厂内部的视频资料 3、100 份 src 文档 4、常见安全面试题 5、ctf 大赛经典题目解析 6、全套工具包 7、应急响应笔记 8、网络安全学习路线
0x01 服务分析
安装完成后对整个系统进行熟悉,发现 Web 程序接口使用 Silverlight 进行数据交互,因此需要找到相关功能文件进行分析。经过一些时间查找,找到系统服务开启的配置文件,在配置文件中定义了访问接口信息以及调用的相关配置文件信息:
经过多方分析找到 FwxServer 类,类中定义了重要服务的启动与注册配置,跟进一下 StartAsyncServer()进行查看:
StartAsyncServer()函数里对配置项进行处理,加载配置项里的配置,在后面有一个 FwxServerBase()函数处理了很多的参数,继续跟进:
FwxServerBase()函数里只是对配置文件里的配置做了一些设置,但此处发现继承了 AsyncServer,再次跟进 AsyncServer:
AsyncServer()函数最后完成配置相关参数并进行启动。到这里就完成整个服务的创建与启动,当然这里只看了一个启动项目,其他的服务注册与启动都差不多:
0x02 漏洞分析
基于前期的服务启动流程以及配置项的分析,最后定位到 Asyncserver 里处理提交请求的接口中,此接口中定义了几个接口,均为提交请求的处理,于是就用这个作为分析的突破口。
下图中定义了一个服务契约,在服务契约里面有多个处理提交请求的操作契约:
我们来对相关参数做一个简单的分析,因为这里只有 PutRequests 是处理提交请求的,所以先来看看它。这里是判断提交过来的数据里的 Session 是否失效,失效返回 false,如果 Session 未失效则进入第二层处理 Request:
在下图可以看到 Request()函数对我们提交过来的数据进行了处理:
主要的 SOAP 数据包标签头:
标签里的 cat 标签对应了下面的几种提交类型,几种类型对应了相关的处理方式:
其他的标签处理大同小异。来到 PutRequest()函数,此函数里有一个 a 函数处理 session,跟进分析一下:
a()函数里,判断了标签 Actor 和用户提交的数据 A_1,跟进 a(A_1)重载函数:
可以看到 a(A_1)重载函数里只是一个值选项判断,再次回到之前,跟进 a(A_0)重载函数:
a(A_0)重载函数处理了 Session 相关数据,也没什么可分析的,接着往下看:
接下来看到存在一个 if 判断,对标签 PointName 和 PointHandle 做了值判断,因为一般情况下都会有值,因此这个地方流程一般不会进入,进入 else 分支分析:
在 else 分支里面进行了一系列的标签值判断,下面代码对提交的数据进行了处理
PointManager.ValidationResult validationResult = this.a(session, request, out pointManagerWrapper, out pointHint);
只要 validationResult 的值不为 Invalid 和 Unknown,则不会进行处理 request 数据,否则处理完成后进行返回:
继续往下看,这里调用了 IsRequestAllowed()函数,这个函数是属于 ISecurityManager 接口的,跟进看一下处理:
在 IsRequestAllowed()函数中,也对相关标签值进行了判断,这里判断了 cat 标签值是否为 4 以及 InParams 的值是否为 SubscribeProcedureInParams;接着判断了 Session 信息是否失效,后面判断了 PointName 的值是否为“cfg:”开头的,如果是则进入 tj.a()函数里,跟进 tj.a()函数:
函数根据 cat 标签的值进行处理,如果我们提交了 cat 的值为 4 且 InParams 的值为 SubscribeProcedureInParams 的话,就会进入 case 4 分支处理,再次跟进 tj.a()重载处理函数看看:
这里首先进行了一次判断,使用的是 RepositoryIdentifier 类,跟进 RepositoryIdentifier.TryParse()函数:
这里把用户提交过来的 PointName 数据用”|”进行分割,加到 list 变量里面:
在处理完数据后,判断了数据的格式是否正常,这里主要判断了数据的长度,Guid.值,“rpt:“, “ctx:” ,“tag:”,随后处理了”tag:”标签,可以看到这里将”tag:”标签 Base64 解密后进行了反序列化的操作,跟进 Deserialize()函数:
反序列化调用了 DataContractSerializer 进行序列化操作:
分析上图代码,可知代码里面存在一个坑:代码对用户提交过来序列化的数据进行自定义处理,固不能直接生成 POC,须预先做一个处理才能被利用。进入 Deserialize()函数后,函数首先获取序列化数据的前 4 个字节,然后以前 4 个字节作为长度读取序列化数据,所以我们须在前面加上长度,否则无法反序列化成功,因为在前面的 GetType 获取中就读取错了数据。
我们可以看到在 DataContractSerializer()函数中,GetType 的参数是可以控制的,分析一下对 type 的处理过程:首先使用工具生成一个测试 poc,然后带入函数进行处理:
看到函数已经对数据进行了处理,处理完数据之后我们发现,取出的变量值并不完整
接下来带入系统进行查找类型:
最后返回的 type 结果为 null,也就是没有找到所属的类型,自然就会反序列化失败:
这里的序列化类型的清单均置于 list 清单里,System.Security.Principal.WindowsPrincipal 是在 list 里面的,但却没有找到,就是因为数据存在格式问题:
根据按照序列化处理代码对 POC 进行删减构造,即可成功获取 type:
0x03 POC 构造
根据上节的漏洞分析,我们可以构造出漏洞利用 POC,并使用 DataContractSerializer()作为反序列化的载体进行利用测试:
通过抓包可以看到请求的数据,在数据包中可以看到,标签 cat 为 4,type 为 0,但是 Inparams 还不是 SubscribeProcedureInParams,借用抓到的数据包构造 POC,删除数据包中一些不必要的数据并添加一些能够让漏洞触发的数据:
数据包构造完成后,使用工具生成 POC,此处使用 ysoserial.NET,把漏洞利用 POC 修改后添加到数据包里面即可成功利用:
0x04 总结
这个 GENESIS64 .NET 的反序列化漏洞的分析过程比较曲折,一方面没有太多的资料可供参考,加之软件程序十分庞大,系统开启服务太多,漏洞分析过程中发现的坑点也很多,导致漏洞定位难度增大,但总的来说,整个漏洞的利用过程还是很有意思,个人收获很大。
评论