ActiveMQ CNVD-2023-69477
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引入依赖
Provider
Consumer
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


Patch
https://github.com/apache/activemq/compare/activemq-5.18.3...activemq-6.0.0
新版本BaseDataStreamMarshaller#createThrowable增加了一处判断

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

Last updated
Was this helpful?