K3 CLOUD是某蝶在移动互联⽹时代基于最新技术研发的⼀款战略性ERP产品,该产品于近⽇曝 出反序列化漏洞,攻击者可构造对应的序列化数据包在⽬标部署服务器上执⾏恶意代码。⽬前已有对应的POC,以及⼀些相关的分析资料,本⽂只说明其中⽐较重要的⼏个部分。
K3Cloud采⽤ASP.NET开发,由多个Web App组成,安装后可在IIS⻅多个⽹站和虚拟⽬录
使⽤dnSpy对Web程序进⾏调试,需使⽤管理员权限运⾏,根据Poc所测试的应⽤程序附加到对应的w3wp进程中
本⽂选择MangeSite(管理后台)为调试⽬标,调试过程中可能会存在局部变量部分被优化的情况,因为程序都是Release发布的,Release的使⽤VS调试⾃带的程序⼀样代码会被优化,微软官⽅提供了对应的解决⽅案 https://learn.microsoft.com/zh-cn/dotnet/framework/debug-trace-profile/making-an-imageeasier-to-debug 需要在dll所在⽬录创建⼀个同名的ini⽂件,内容如下:
然后重启IIS即可。
本次反序列化漏洞影响只限于K3Cloud(前台) , ManageSite(后台)两个应⽤程序。漏洞路径出现在 Kingdee.BOS.ServiceFacade.ServicesStub.DevReportService.GetBusinessObjectData.common.kdsvc
,由kdsvc后缀结尾,根据web.config⾥的handler配置信息
任何由*.kdsvc结尾的请求路径均会交由KDServiceHandler进⾏处理,不懂.NET可以把它理解成JAVA中的Servlet,KDServiceHandler在程序bin⽬录下的Kingdee.BOS.ServiceFacade.KDServiceFx.dll中。
使⽤DnsPy进⾏反编译后可以看到,KDServiceHandler⼜根据开头和结尾字符再次将请求交给不同的 Handler去处理。 Kingdee.BOS.ServiceFacade.ServicesStub.DevReportService.GetBusinessObjectData.common.kdsvc
路径并不满⾜上⾯对应的结尾和开头,因此交给KDSVCHandler进⾏处理
KDSVCHandler中定义了两个⽅法,ProcessRequest和ProcessRequestInternal,根据ASP.NET处理HTTP请求的优先级会顺序执⾏ProcessRequest > ProcessRequestInternal⽅法。
ProcessRequest中使⽤ RequestExtractor.Create对请求传输的内容进⾏格式化
根据不同的Content-Type传输类型,选择不同的处理类,payload中的是Content-Type: json所以这⾥会选择JQueryRequestExtractor类进⾏处理
JQueryRequestExtractor类中再次根据POST和GET选择对应的处理⽅法,GET则选择第⼀个参数的内容,POST则是以request.InputStream输⼊流进⾏反序列化,POST⽅法处理期间还会根据UserAgent选择对应的取值⽅式,将所有的键值对存储到nameValueCollection对象中。
后续传递参数内容均会从requestExtractor对象中获取,包括了format
使⽤ExtractForm返回对应的值
这⾥可以看到定义了多个类型,当format为3时会返回Binary,并且此处已经声明“由于安全原因,⼆进制⽂件将被丢弃。
请改⽤某蝶Xml格式”。
期间会根据请求路径加载对应程序集的操作
使⽤ReflectServiceType构建⼀个ServiceType对象,这个对象⾥包含了名称,CLR类型,对应的⽅法,是否安全,返回类型,版本,和参数等信息。
加载完后,会遍历调⽤⼀些定义好的Module类。
顺序遍历调⽤其中的OnProcess,调⽤到第6个,ExecuteServiceModule时,漏洞触发点呈现
在执⾏过程中会创建⼀个serializeProxy序列化代理器,⽽序列化代理器会根据format的值创建对应的序列化类
这个在上⽂已经说明,此时的format为Binary,会返回⼀个BinaryFormatterProxy类
然后进⼊到Execute⽅法。
在Execute⽅法中,会根据之前创建的ServiceType对象进⾏⼀些判断
如请求⽅法所需要的参数和传递的参数数量必须要⼀致
以及MapToCLRType类的构造函数需要接收⼀个 Kingdee.BOS.ServiceFacade.KDServiceFx.KDServiceContext类型的对象。
随后进⼊DeserializeParameters,也就是漏洞触发点对参数进⾏反序列化。
会根据调⽤⽅法所需要的参数数量进⾏单次或者多次循环,并得到对应参数的Type类型传递给代理器的Deserialize反序列化⽅法。
这⾥⼜做了⼀层限制,当接受参数的类型为string,int,byte,float,double,long,....等等类型时,并不会进⼊ 到代理器的Deserialize⽅法 因此需要找到⼀个这些类型之外的,如Object类型,GetBusinessObjectData刚好满⾜这⼀条件。
最后进⼊到代理器的BinaryFormatterProxy.Deserialize⽅法,导致反序列化漏洞触发。
参数传递过程中必须要设置成ap0? 根据传递⽅式进⾏设置,具体看ExecuteServiceModule类中的处理
这⾥的form存储的是上⽂对传递参数进⾏反序列化后的键值对,如果其中包含parameters键,如果存在,将该参数解析为⼀个JSONArray对象。
如果不存在,则根据⽅法所需参数数量,进⾏for循环,以“ap”为开头,依次遍历数字。
当然也可以抛弃json格式的内容传递⽅式,使⽤传统的POST
paylod需要进⾏⼀次url编码。
之前某安全公众号有发布对应的临时修复⽅案
设置EnabledKDSVCBinary为False,因为在创建序列化代理器时,会取值进⾏判断是否开启⼆进制流反序列化名功能。
上⾯修复的⼿段并不完全,即使关闭了⼆进制流的⽅式传递参数内容,但是该系统提供了不只Binary⼀种⽅式传递参数,如下:
在8.0及以上版本,官⽅提供了多种⽅式,其中KingdeeXml被推荐使⽤,KingdeeXml⽐Binary更安全?
其实不然,KingdeeXml本质还是Bianry。
当format为4时,赋值为KingdeeXml,创建⼀个XmlSerializerProxy代理器
⽽XmlSerializerProxy代理中的Deserialize其中还是⽤的BinaryFormatterProxy,只是中间需要经过⼀层 NetDataContractSerializer反序列化成KingdeeXMLPack对象。
从⽽绕过EnabledKDSVCBinary的设置。