Apache ActiveMQ RCE

影响版本

Apache ActiveMQ < 5.18.3

利用条件

需要访问到61616端口(默认)。

漏洞分析

这里需要的是Apache ActiveMQ < 5.18.3,我这里直接下的5.18.2

https://github.com/apache/activemq/commit/958330df26cf3d5cdb63905dc2c6882e98781d8f

image-20231029005205594

在新版本中添加了一个OpenWireUtil.validateIsThrowable(clazz);在5.18.2中的代码如下

image-20231029005512964

把5.18.3的代码下下来,看看validateIsThrowable方法,代码如下

package org.apache.activemq.openwire;

public class OpenWireUtil {

    /**
     * Verify that the provided class extends {@link Throwable} and throw an
     * {@link IllegalArgumentException} if it does not.
     *
     * @param clazz
     */
    public static void validateIsThrowable(Class<?> clazz) {
        if (!Throwable.class.isAssignableFrom(clazz)) {
            throw new IllegalArgumentException("Class " + clazz + " is not assignable to Throwable");
        }
    }
}

这里如果传进来的clazz不是继承自Throwable类,就会抛出异常。在上面是通过反射获取一个类,然后调用对应的构造方法。在5.18.3在中对这个反射获取到的类进行了过滤。 现在就需要找到一个可以命令执行的类去反射获取。这里是用的BaseDataStreamMarshaller的classloader。

找到BaseDataStreamMarshaller的子类ExceptionResponseMarshaller,在其中的tightUnmarshal方法中调用了tightUnmarsalThrowable,具体代码如下

public void tightUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn, BooleanStream bs) throws IOException {
    super.tightUnmarshal(wireFormat, o, dataIn, bs);
    ExceptionResponse info = (ExceptionResponse)o;
    info.setException((java.lang.Throwable) tightUnmarsalThrowable(wireFormat, dataIn, bs));
}

tightUnmarsalThrowable具体代码如下

image-20231029011220306

在其中说到了上文当中的createThrowable,可以反射获取一个类并且调用对应的含一个string参数的构造方法,其中ExceptionResponseMarshaller类是对ExceptionResponse进行序列化操作的类

来看tightUnmarsalThrowable的主要逻辑

image-20231029011551743

从BooleanStream读入一个Boolean的数据,分别调用tightUnmarshalString去获取clazz和message,应该是把类名和message从clazz从二进制流中读取出来,然后作为参数调用createThrowable方法,tightUnmarsalThrowable方法最后返回了一个o,根据如下代码,这个o应该是ExceptionResponse的Exception属性

public void tightUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn, BooleanStream bs) throws IOException {
    super.tightUnmarshal(wireFormat, o, dataIn, bs);

    ExceptionResponse info = (ExceptionResponse)o;
    info.setException((java.lang.Throwable) tightUnmarsalThrowable(wireFormat, dataIn, bs));

}

我们这只要构造一个ExceptionResponse包含恶意的类名和message发送给服务器,服务器接受到并且反序列化就可以调用createThrowable

Exp:

没有,本人在考研,就随便跟着代码先理论理解一下了,本文也是根据@X1r0z师傅的文章所写,为了不把东西忘干净,争取每天抽1h学习