// CyberDefenders  ·  writeup

OpenWire

CyberDefenders WiresharkZuiNetwork Miner

Scenario

During your shift as a tier-2 SOC analyst, you receive an escalation from a tier-1 analyst regarding a public-facing server. This server has been flagged for making outbound connections to multiple suspicious IPs. In response, you initiate the standard incident response protocol, which includes isolating the server from the network to prevent potential lateral movement or data exfiltration and obtaining a packet capture from the NSM utility for analysis. Your task is to analyze the pcap and assess for signs of malicious activity.

Executive Summary

An attacker exploited an unauthenticated deserialization flaw in an internet-facing Apache ActiveMQ broker’s OpenWire transport, forcing the JVM to instantiate a Spring application context from a remote XML bean definition. That bean definition invoked a Java process-builder class directly, fetching and executing a reverse shell binary staged on a second attacker-controlled server. The exploit chain required no authentication and no client interaction at all — a single crafted OpenWire command against the broker’s default listener was enough to hand the attacker arbitrary code execution on the host.

Artifacts
Initial Access
Q1 — By identifying the C2 IP, we can block traffic to and from this IP, helping to contain the breach and prevent further data exfiltration or command execution. Can you provide the IP of the C2 server that communicated with our server?

Exploitation of this class of vulnerability requires the attacker to reach the broker directly over the network, so the exploit traffic itself is on the wire somewhere in this capture. Filter for the OpenWire protocol rather than guessing which stream carries the payload — it’s a custom TCP protocol, not HTTP, and it stands out cleanly once isolated.

# tool: wireshark
openwire

One IP sends an OpenWire Exception Response command (opcode 0x1F) that triggers the broker to instantiate org.springframework.context.support.ClassPathXmlApplicationContext and load a Spring bean definition from a remote XML file at /invoice.xml. That opcode, from that source IP, is the exploit delivery — and the C2 in question.

Tip: Displaying the openwire filter first, before touching Statistics or Conversations, cuts straight to the exploit frame instead of wading through the rest of the capture’s noise.

Click flag to reveal 146.190.21.92
Q2 — Initial entry points are critical to trace back the attack vector. What is the port number of the service the adversary exploited?

The same Exception Response frame identified in Q1 already carries this answer — the TCP header on that frame shows the destination port the exploit traffic landed on.

Click flag to reveal 61616
Q3 — Following up on the previous question, what is the name of the service found to be vulnerable?

A port number alone doesn’t confirm the software behind it — follow the TCP stream for the broker conversation and pull the WireInfo response. OpenWire’s handshake includes a provider name field that names the software directly, no external lookup needed.

Click flag to reveal Apache ActiveMQ
Q7 — To better understand the specific security flaw exploited, can you identify the CVE identifier associated with this vulnerability?

With the vulnerable service confirmed as ActiveMQ and the exploit mechanism traced to a ClassPathXmlApplicationContext deserialization primitive over OpenWire, this maps to a specific, well-documented advisory rather than something that needs to be derived from the capture itself. CVE-2023-46604

Click flag to reveal CVE-2023-46604
Q8 — To address the vulnerability, the vendor added a validation step, preventing exploitation. In what Java class and method was this validation step added? (Format: Class:Method)

The CVE identifier from Q7 is the entry point into the vendor’s fix — the patch adds a validation step that checks whether the class the OpenWire command asks to instantiate is actually of a Throwable type before the marshaller allows it, closing off the arbitrary-class-instantiation path entirely. rapid7

Click to reveal answer BaseDataStreamMarshaller.createThrowable
Execution
Q6 — What Java class was invoked by the XML file to run the exploit?

The actual code execution doesn’t happen inside the OpenWire stream itself — it’s driven by the Spring bean definition fetched from /invoice.xml in Q1. Export the HTTP response for that object (Wireshark → File → Export Objects → HTTP) and read the bean configuration directly rather than assuming a generic Spring gadget chain.

The XML instantiates java.lang.ProcessBuilder directly, passing it a bash invocation that fetches, chmods, and executes a reverse shell binary in one line. The deserialization flaw isn’t just loading a Spring context — it’s using that context to hand raw process-creation primitives straight to the attacker.

Tip: ProcessBuilder invoked from inside a Spring bean definition is the generic RCE primitive behind most ActiveMQ OpenWire exploit chains — the same pattern recurs regardless of which second-stage payload gets dropped.

Click flag to reveal java.lang.ProcessBuilder
Command and Control
Q4 — The attacker's infrastructure often involves multiple components. What is the IP of the second C2 server?

The C2 IP from Q1 only accounts for exploit delivery — check Statistics → Endpoints (or NetworkMiner’s host list) to get every IP present in the capture rather than assuming a single-server operation. After excluding the public server’s own address and the confirmed exploit-delivery IP from Q1, two unknown IPs remain. Comparing traffic to and from each shows the vulnerable server reaching out to one of them to pull down a file — that’s the second C2, hosting payload infrastructure rather than delivering the exploit itself.

Click flag to reveal 128.199.52.72
Q5 — Attackers usually leave traces on the disk. What is the name of the reverse shell executable dropped on the server?

The ProcessBuilder command identified in Q6 already names the file being pulled from the second C2 in Q4 — confirm the transfer itself by exporting the HTTP object for that request rather than trusting the command-line string alone; the filename in the request URI and the file staged to /tmp/ should match exactly.

Click flag to reveal docker

Attack Summary

PhaseAction
Initial AccessAttacker sent a crafted OpenWire Exception Response command to the internet-facing ActiveMQ broker on its default port
Initial AccessCommand forced the broker to instantiate a Spring ClassPathXmlApplicationContext and fetch a bean definition from a remote XML file
ExecutionBean definition invoked java.lang.ProcessBuilder directly, running a bash command via the deserialized class
Command and ControlProcessBuilder command fetched a reverse shell binary from a second, separate attacker-controlled server
ExecutionReverse shell binary was chmod’d executable and run on the compromised host

IOCs

TypeValue
IPhxxp[://]146[.]190[.]21[.]92
IP128[.]199[.]52[.]72
URLhxxp[://]146[.]190[.]21[.]92:8000/invoice.xml
Filedocker (ELF reverse shell)
File Path/tmp/docker
CVECVE-2023-46604
Port61616 (default ActiveMQ OpenWire)

MITRE ATT&CK

TechniqueIDDescription
Exploit Public-Facing ApplicationT1190Attacker sent a crafted OpenWire command exploiting CVE-2023-46604 against the internet-facing ActiveMQ broker
Command and Scripting Interpreter: Unix ShellT1059.004Deserialized ProcessBuilder object executed a bash command to fetch and run the second-stage payload
Ingress Tool TransferT1105Reverse shell binary pulled from a second attacker-controlled server over HTTP

Defender Takeaways

An internet-facing message broker should never be reachable on its native transport port from the open internet. ActiveMQ’s OpenWire listener has no business being exposed beyond a trusted network segment or VPN boundary. Restricting inbound access to port 61616 to known application servers would have stopped this exploit chain before the first packet landed.

Unauthenticated deserialization in a broker’s wire protocol is a class of vulnerability that traditional signature-based network detection misses entirely. The exploit traffic here is a single OpenWire command, not a recognizable exploit kit or malicious file signature. Detection has to live at the protocol-anomaly level — alerting on OpenWire Exception Response commands referencing unexpected classes like ClassPathXmlApplicationContext is a far more durable control than waiting for AV to flag the eventual payload.

Outbound HTTP requests for XML bean definitions from a production broker are themselves an anomaly worth alerting on. A message broker fetching and parsing arbitrary remote XML isn’t normal behavior for that service. Egress filtering on application servers, paired with alerting on outbound requests to previously unseen external hosts, would have flagged the /invoice.xml fetch independent of any exploit-specific signature.

Patch management on internet-facing middleware needs to run on a faster cycle than internal application servers. This vulnerability was fixed by adding a single type-validation check in BaseDataStreamMarshaller.createThrowable — a narrow, well-scoped patch that eliminates the entire exploit primitive. Any exposed ActiveMQ instance not on 5.15.16, 5.16.7, 5.17.6, or 5.18.3 or later remains exploitable by this exact chain.

Conclusion

The attacker never needed credentials, a foothold, or a phishing lure — a single malformed OpenWire packet against an exposed broker was the entire initial access vector, and the deserialization flaw did the rest. The lesson isn’t in the payload; it’s that internet-facing middleware speaking its native wire protocol to the open internet is, by itself, the vulnerability.

I successfully completed OpenWire Blue Team Lab at @CyberDefenders! https://cyberdefenders.org/blueteam-ctf-challenges/achievements/inksec/openwire/

#CyberDefenders #CyberSecurity #BlueYard #BlueTeam #InfoSec #SOC #SOCAnalyst #DFIR #CCD #CyberDefender