写点什么

【漏洞分析】反序列化漏洞

  • 2022 年 2 月 21 日
  • 本文字数:4645 字

    阅读完需:约 15 分钟

0x01 背景

2022 年 1 月 18 日,ORACLE 官方发布了 2022 年第一季度的补丁,其中涉及到多个关于 Weblogic 的漏洞。由于 Weblogic 的补丁很贵,一般人下载不到,所以一直在等待相关的细节信息。


从 CVE 官网找到的 CVE-2022-21350 漏洞是属于 Weblogic 的未授权访问和拒绝服务漏洞,CVSS 评分也只有 6.5,和这里的反序列化似乎不是一个漏洞。从漏洞的简要描述来看这个漏洞似乎也不是 CVE-2022-21306。


本篇文章主要是针对该漏洞的详细分析和研究,以探讨安全技术为目的,对漏洞分析不感兴趣的小伙伴可直接跳到文末的结论。

0x02 利用链分析

发现反序列化漏洞的过程我们就不追究了,我们直接站在巨人的肩膀上来分析这个漏洞,顺便说明一般反序列化漏洞利用链的分析方式。


要实现反序列化利用链,首先需要知道整个利用链的栈调用过程。这里就不用文字来描述了,直接用最后 poc 编写完成之后的栈调用截图来表示,如图 2.1 所示。



从栈调用过程可以看出整个反序列化利用链的入口(Source)点是 BadAttributeValueExpException 类,出口(Sink)点是 HomeHandleImpl 类。我们按照倒叙的方式来分析整个利用链,倒叙的方式可能看起来比较别扭,但是有利于我们编写反序列化利用链的利用代码。


找到 HomeHandleImpl 类的 getEJBHome 方法,如图 2.2 所示。图中箭头位置是一个典型的 JNDI 注入函数 lookup,并且 lookup 函数的两个点 ctx 和 this.jndiName 都是可控的。如果某条反序列化的利用链最终能调用这个函数,则可能造成 JNDI 注入。关于这个位置进行 JNDI 注入利用的方式在后面的章节进行分析。



【一>所有资源获取<一】1、网络安全学习路线 2、电子书籍(白帽子)3、安全大厂内部视频 4、100 份 src 文档 5、常见安全面试题 6、ctf 大赛经典题目解析 7、全套工具包 8、应急响应笔记


到这里我们要实现反序列化利用链编写的第一步,编写 Sink 点利用方式。这里可控的参数有 this.serverURL 和 this.jndiName 两个点。控制这两个点都可以实现反序列化漏洞的 poc(但是实现 exp 的利用过程需要通过 this.jndiName,这是后话)




到这里我们就可以明确一个信息,如果某个反序列化的调用链可以调用 HomeHandleImpl 类的 getEJBHome 方法,那么就可以实现反序列化,整个反序列化利又向前迈出一步。我们继续看哪里调用了 getEJBHome 方法,从栈调用的图可以看出 BusinessHandleImpl 类的 getBusinessObject 方法调用了 getEJBHome 方法。并且这里的 this.homeHandle 字段类型是 HomeHandleImpl 类。



到这里就可以把反序列化利用链再向前一步,编写利用链代码。调用之后同样可以看到 DNSLOG 截图。




到这里我们就可以明确一个信息,如果某个反序列化的调用链可以调用 BusinessHandleImpl 类的 getBusinessObject 方法,那么就可以实现反序列化。从栈调用过程中可以看出 AttributeWrapperUtils 类的 unwrapEJBObjects 方法调用了 BusinessHandleImpl 类的 getBusinessObject 方法,这里的调用过程只与第二个参数 unwrappedObject 相关。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rVTQW65S-1645444484061)(https://upload-images.jianshu.io/upload_images/26472780-4fb04124c54dc927.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]


unwrapEJBObjects 方法又被 unwrapObject 方法调用。



所以我们只要能调用 AttributeWrapperUtils 类的 unwrapObject 方法,并且第二个参数可控,那么就可以实现整个反序列化调用链。继续把反序列化调用链向上走一步,编写反序列化链代码。



继续向上寻找哪里调用了 AttributeWrapperUtils 类的 unwrapObject 方法,通过栈调用图可以看出 FileSessionData 类的 getAttributeInternal 方法调用了 AttributeWrapperUtils 类的 unwrapObject 方法。这里有一个小坑是 FileSessionData 本身是没有相关函数的调用的,实际上是通过 FileSessionData 类的父类 SessionData 类的 getAttributeInternal 方法来进行调用的。那么我们为什么不能直接用 SessionData 类呢?因为 SessionData 类是一个 abstract 类,不能直接实例化。


在上一步的分析中我们要求 unwrapObject 方法的第二个参数必须可控,在这一步中 unwrapObject 的第二个参数来源于 this.attributes 字段,属于反序列化可控字段。



getAttributeInternal 方法又被 getAttribute 方法调用。



getAttribute 方法又被 isDebuggingSession 方法调用。



isDebuggingSession 方法又被 toString 方法调用。



到这里我们就可以通过调用 SessData 类的 toString 方法来调用 attributeWrapperUtils 类的 unwrapObject 方法。继续向上编写反序列化利用链。


这里有一个点要注意就是 attributeWrapperUtils 类的 unwrapObject 方法的第二个参数来源于 this.attributes.get(name),这里的 name 来源于方法参数,最开始的赋值只能是 wl_debug_session。



有经验的小伙伴看到 toString 函数就会想到 CC 链,在 CC5 中就是通过 BadAttributeValueExpException 类的 readObject 方法来调用任意类的 toString 方法的。我们直接使用 CC5 的后面部分的代码拿过来使用就可以组成本次反序列化利用链的最终组成部分。



上面的过程就已经完整的复现了这个利用链,但是在本地实际测试的过程中却发现怎么都不成功,不成功主要是因为条件判断会包异常。


异常原因是因为本身 registry 里面的变量都是 null,再去调用 null 的任何方法都会报空指针异常。



遇到这个问题的第一感觉是通过反射来修改 registry 字段的值,但是实际测试过程中却发现这个 registry 的赋值过程异常复杂,而且里面还有很多 final 修饰的字段,通过反射修改不成功。后面发现这个 registry 是属于 Weblogic 程序启动时候的初始化数据,包含的是 Weblogic 服务器信息,本地调试当然没有 registry 信息,但是远程的真实 Weblogic 服务器就都有这个信息。所以,直接开一个 Weblogic 的远程调试。



可能有的小伙伴就觉得奇怪了,我们分析的每一步都是基于调用栈来复现的,实际情况下我们肯定不可能先有某个漏洞的调用栈,然后再来分析。这当然不可能,挖掘一个漏洞的调用过程是一个极难的事情,这可比分析漏洞难多了。

0x03 POC 编写

其实上面关于反序列化利用链的分析过程中就已经包含了 POC 代码了,但是为了方便小伙伴们查看。我们还是把整个代码整合一下,完整的利用代码如下所示:


import weblogic.ejb.container.internal.BusinessHandleImpl;


执行上面的代码会生成一个序列化的文件 cve_2022_21350.ser。然后再通过 t3 协议把这个序列化的文件发送给目标主机就可以了。



0x04 EXP 编写

上面我们的 POC 编写都还是在关于 DNSLOG 的请求层面,俗话说的好“不能弹计算器的 POC 都不是好 EXP”。上面的也仅是 POC 层面,还没有 EXP 层面的利用,我们的目标是弹出计算器。


达到 EXP 的效果,就需要跟踪 Weblogic 里面关于 JNDI 注入的代码。常规的 JNDi 注入代码大约如下所示。



一般情况下我们需要能控制 lookup 函数中的值,当我们写成 ldap 协议的地址,就会按照 ldap 协议请求加载远程数据。但是这里我们可控的是 jndiName 的字段类型是 Name,并不是一般的 String。



跟踪关于 lookup 函数的定义示。



里面最关键的是 getURLOrDefaultInitCtx 函数,该函数规定了对 Name 类型的数据进行处理,然后传递给下一步的 NamingManager 类。



我们想要进行的是 Ldap 注入,Ldap 的地址来源于 scheme 变量,只要我们控制 scheme 变量为我们恶意的 Ldap 地址,就可以进行常规的基于 LDAP 协议的 JNDI 注入。这里最关键的就是让name.get(0) == "ldap://xxxx.xxx.xx.xxx:1389/xxx"


Name 是一个接口基于此接口的实现类如下


javax.naming.CompoundName

javax.naming.CompositeName

com.sun.jndi.dns.DnsName

com.sun.jndi.toolkit.dir.HierarchicalName

com.sun.jndi.ldap.LdapName

javax.naming.ldap.LdapName


依次对这几个类创建对象,查看对象调用 get(0)之后是否能获取到ldap://xxxx.xxx/xxx这样的数据。


1) 第一个找到的是 DnsName 类


由于英文点在 DnsName 类中是 parse 函数的分隔符,所以不能使用点。但是我们可以使用 ip 转整数的方式对点进行转换。所以 127.0.0.1 可以转化为 2130706433,这样就可以获取到完整的 ldap 路径。



2) 第二个找到的是 CompoundName 类


CompoundName 可以直接使用构造函数,获取到的就是我们想要的数据。



3) 第三个找到的是 HierarchicalName 类


HierarchicalName 类是继承自 CompoundName 类的,所以使用方法也类似。



图 4.7 使用 HierarchicalName 类获取 Name 对象调用 get(0)


从上面的三种方式中任选一种来生成 Name 对象,ldap 地址填写我们远程 vps 服务器地址。Sink 部分代码如图 4.8 所示,这里采用最简单直接的 CompoundName 类生成 name 对象。



图 4.8 通过 CompoundName 类来构造恶意 ldap 地址


其他部分的代码和 POC 里面的代码完全一样。利用过程需要首先在 V1PS 上开启注册恶意的 ldap 服务,利用 marshalsec 就可以实现了。如图 4.9 和图 4.10 所示。



图 4.9 V1PS 上的 Ldap 接受请求,加载恶意类


0x05 实际测试

本来以为上面的过程之后就完了,但是本着对漏洞分析完整性的研究,我在打了补丁的环境里面测试漏洞的实际危害。下面的所有环境均值 Weblogic12.2.1.4.0,其他环境可能略有不同。


环境一,没有打补丁的环境


见 0x04 章节,可以成功复现漏洞。


环境二,打了 2021.10 的 Weblogic 补丁


按理说这个是 2022 年的漏洞,漏洞更新也是在 2022.1 的补丁中进行更新的。但是实际上在 2021.10 补丁的环境中,仍然不能复现成功这个漏洞。


打了补丁的环境是通过 FilteringObjectInputStream 类来对 t3 协议的流量进行反序列化,跟踪此类中的 resolveClass 函数,如图 5.1 所示。



图 5.1 FilteringObjectInputStream 类的 resolveClass 函数


这个函数就是用来反序列化类,获取类对象的。里面增加了安全措施,第一个就是通过 checkLegacyBlacklistIfNeeded 函数来校验待反序列化的类是否在 Weblogic 黑名单中(大名鼎鼎的 Weblogic 黑名单机制),最开始就说过的这是一条新的反序列化利用链,当然不在现在的黑名单列表中,所以这条的判断可以通过。第二个会通过 validateReturnType 函数校验生成类的类型,如图 5.2 所示。如果反序列化的类不是 this.expectedType 或者 this. expectedTypes 中的某一个类的子类,则抛出异常。



图 5.2 通过 validateReturnType 函数校验类型


动态调试发现 this. expectedType 一定是 null,但是 this. expectedTypes 是有值的,this. expectedTypes 值的定义来源于 InboundMsgAbbrev 类,如图 5.3 所示。



图 5.3 通过 readObjectValidated 来对 expectedTypes 赋值


赋值的依据是 ABBREV_CLASSES,这是一个常量,定义如图 5.4 所示。也就是我们反序列化的类必须继承自 ABBREV_CLASSES 常量中的某个类,这就相当于是一种白名单机制,比传统的黑名单机制要狠太多了。



图 5.4 对 ABBREV_CLASSES 常量进行定义


如果我们还是使用 0x04 中的 exp 来攻击打了 2021.10 补丁的环境,就会失败,并且看到异常信息。


环境三,打了 2021.01 补丁的环境


可以成功复现漏洞利用,exp 与 0x04 相同。看了一下代码,发现 2021.01 的环境中 InboundMsgAbbrev 类的定义如图 5.5 所示。



图 5.5 2021.01 补丁环境中没有定义 ABBREV_CLASSES 常量


也就是在 2021.01 中并没有白名单的类型限制,所以我们仍然可以利用成功,如图 5.6 所示。



图 5.6 在 2021.01 补丁环境中调用成功 exp


所以,Weblogic 从 2021.0x 的某个版本开始引入了 ABBREV_CLASSES 机制,该机制会限制反序列化时候的类的名称。目前不知道有什么办法可以绕过这个机制,希望知道的小伙伴能告知。

0x06 结论

目前来看这确实是一条新的反序列化利用链,但是这条利用链要成功利用还是有诸多限制,主要需要满足的条件如下:


1) 目标必须出网


2) 目标 JDK 版本必须小于 JDK1.8.0_191


3) 目标不能采用 ABBREV_CLASSES 白名单机制(这大约是 2021 年的某次更新)


虽然利用条件有限,但是还是不能阻止这确实是一个好洞,伸手党可直接扫描下方二维码关注公众号回复“weblogic”获取漏洞研究相关工具。

用户头像

我是一名网络安全渗透师 2021.06.18 加入

关注我,后续将会带来更多精选作品,需要资料+wx:mengmengji08

评论

发布
暂无评论
【漏洞分析】反序列化漏洞