weblogic漏洞分析之CVE-2016-0638

一、环境搭建:

这里使用前一篇文章的环境,然后打上补丁

  1. 下载补丁p20780171_1036_Generic和p22248372_1036012_Generic
  2. 解压补丁后,复制补丁到Docker中:
docker cp ./p22248372_1036012_Generic/ d1b6be39e32e:/home/
docker cp ./p20780171_1036_Generic d1b6be39e32e:/home/


  1. 安装补丁:
docker exec -it d1b6be39e32e /bin/bash
cd /root/Oracle/Middleware/utils/bsu
mkdir cache_dir
cp /home/p20780171_1036_Generic/* ./cache_dir/
cp /home/p22248372_1036012_Generic/* ./cache_dir/
vi bsu.sh 编辑MEM_ARGS参数为1024
./bsu.sh -install -patch_download_dir=/root/Oracle/Middleware/utils/bsu/cache_dir/ -patchlist=EJUW -prod_dir=/root/Oracle/Middleware/wlserver_10.3 -verbose
./bsu.sh -install -patch_download_dir=/root/Oracle/Middleware/utils/bsu/cache_dir/ -patchlist=ZLNA -prod_dir=/root/Oracle/Middleware/wlserver_10.3 -verbose


  1. 重启服务:
docker restart d1b6be39e32e


这时候,使用原先的poc已经是失效了,说明补丁成功打上

二、补丁分析

配置好weblogic的远程调试端口后,复制jar包,导入idea​

补丁位置:

weblogic.rjvm.InboundMsgAbbrev.class :: ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class


第一个位置在​​InboundMsgAbbrev​​中,新增了个判断

weblogic漏洞分析之CVE-2016-0638_docker

重点在​​ClassFilter.isBlackListed​​判断是否存在黑名单中,跟进方法

weblogic漏洞分析之CVE-2016-0638_序列化_02

方法​​isBlackListed​​中对className和pkgName都进行了黑名单判断,匹配到了则返回true。黑名单list

weblogic漏洞分析之CVE-2016-0638_oracle_03

在值为​​org.apache.commons.collections.functors.ChainedTransformer​​时候抛出了异常

weblogic漏洞分析之CVE-2016-0638_docker_04

补丁的第二个位置​​MsgAbbrevInputStream​​也是做了同样的判断

weblogic漏洞分析之CVE-2016-0638_docker_05

现在来进行一遍流程分析,先看下之前的整个调用栈

weblogic漏洞分析之CVE-2016-0638_服务器_06

POC伪造的T3协议数据包,发送到服务器,服务器反序列化处理到了​​InboundMsgAbbrev#readObject​​方法中

weblogic漏洞分析之CVE-2016-0638_反序列化_07

switch分发节点,到了case0中,调用了​​(new InboundMsgAbbrev.ServerChannelInputStream(in))​​的​​readObject​​方法来进行的反序列化

计算得出为​​InboundMsgAbbrev​​类中的一个内部类​​ServerChannelInputStream​

weblogic漏洞分析之CVE-2016-0638_docker_08

以下是​​ServerChannelInputStream​​类的继承图

weblogic漏洞分析之CVE-2016-0638_docker_09

其继承了​​ObjectInputStream​​,并重写了​​resolveClass​​方法

weblogic漏洞分析之CVE-2016-0638_反序列化_10

weblogic反序列化类时候调用的是​​ServerChannelInputStream​​的​​readObject​​方法,而在执行到​​resolveClass​​方法的时候调用的是​​ServerChannelInputStream​​重写的​​resolveClass​​方法,在方法中对其做了黑名单过滤导致无法执行后面的​​readObject​​、​​readExternal​​、​​readResolve​​方法。

三、绕过黑名单

要想绕过黑名单,则需要寻找一个不在黑名单的类。要知道,​​ObjectInputStream​​​在执行​​readObject​​​方法的时候,除了会调用反序列化类的readObject方法外,还会调用​​readExternal​​​、​​readResolve​​,详细调用过程可以看

所以​​weblogic.jms.common.StreamMessageImpl​​类就被挖掘了出来。其​​StreamMessageImpl#readExternal​​中,会调用var5的​​readObject​​方法,而var5是一个​​ObjectInputStream​​对象

weblogic漏洞分析之CVE-2016-0638_docker_11

四、漏洞分析

攻击机

这里使用weblogic_cmd构造的poc:​​https://github.com/5up3rc/weblogic_cmd​

-H "192.168.202.129" -C "touch /tmp/success" -B -os linux


这里把CC1的利用链,进行了序列化后传入了​​streamMessageImpl​​方法

weblogic漏洞分析之CVE-2016-0638_服务器_12

跟进​​streamMessageImpl​​方法,new了一个​​StreamMessageImpl​​对象,这个对象也就是构造payload的关键,然后把序列化转换后的byte数组传入了​​setDataBuffer​​方法

weblogic漏洞分析之CVE-2016-0638_oracle_13

跟进​​setDataBuffer​​,对​​this.buffer​​和​​this.length​​进行了赋值操作

weblogic漏洞分析之CVE-2016-0638_oracle_14

随后把获得的​​StreamMessageImpl​​对象又进行了一次序列化操作(序列话后的payload传入对象,再把对象进行了序列化)

weblogic漏洞分析之CVE-2016-0638_反序列化_15

​StreamMessageImpl​​对象序列化会调用​​writeExternal​​方法

weblogic漏洞分析之CVE-2016-0638_docker_16

在​​StreamMessageImpl​​对象的​​writeExternal​​方法中进行了重写,把之前的payload给write进了​​ObjectOutputStream​​对象中

weblogic漏洞分析之CVE-2016-0638_docker_17

最后返回给了byte[] payload,然后进行了T3协议构造并且发送给了服务器

weblogic漏洞分析之CVE-2016-0638_反序列化_18

服务端

在服务端接受到payload,随后进行了反序列化操作,跳转到​​InboundMsgAbbrev#resolveClass​​方法中,进行黑名单判断

weblogic漏洞分析之CVE-2016-0638_docker_19

黑名单中并没有​​StreamMessageImpl​​这个类,返回false,走入else中

weblogic漏洞分析之CVE-2016-0638_服务器_20

接下来就会到​​StreamMessageImpl#readExternal​​方法

switch中由前面构造payload时候写入,case为1

weblogic漏洞分析之CVE-2016-0638_服务器_21

此时的var4就是CC1的承载了payload的​​AnnotationInvocationHandler​​序列化后的​​BufferInputStream​

weblogic漏洞分析之CVE-2016-0638_序列化_22

最后调用了var5.readObject就触发了​​AnnotationInvocationHandler#readObject​​方法了

weblogic漏洞分析之CVE-2016-0638_反序列化_23

总结

在这里用了​​StreamMessageImpl​​类来绕过黑名单检查,把​​AnnotationInvocationHandler​​序列化后通过重写的​​writeExternal​​方法给write进了​​ObjectOutputStream​​对象中,调用​​StreamMessageImpl​​类的​​readExternal​​传入了参数并执行了参数的readObject方法从而触发了调用链