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.
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.
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
openwirefilter first, before touching Statistics or Conversations, cuts straight to the exploit frame instead of wading through the rest of the capture’s noise.
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.

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.

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
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
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:
ProcessBuilderinvoked 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.
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.

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.

| Phase | Action |
|---|---|
| Initial Access | Attacker sent a crafted OpenWire Exception Response command to the internet-facing ActiveMQ broker on its default port |
| Initial Access | Command forced the broker to instantiate a Spring ClassPathXmlApplicationContext and fetch a bean definition from a remote XML file |
| Execution | Bean definition invoked java.lang.ProcessBuilder directly, running a bash command via the deserialized class |
| Command and Control | ProcessBuilder command fetched a reverse shell binary from a second, separate attacker-controlled server |
| Execution | Reverse shell binary was chmod’d executable and run on the compromised host |
| Type | Value |
|---|---|
| IP | hxxp[://]146[.]190[.]21[.]92 |
| IP | 128[.]199[.]52[.]72 |
| URL | hxxp[://]146[.]190[.]21[.]92:8000/invoice.xml |
| File | docker (ELF reverse shell) |
| File Path | /tmp/docker |
| CVE | CVE-2023-46604 |
| Port | 61616 (default ActiveMQ OpenWire) |
| Technique | ID | Description |
|---|---|---|
| Exploit Public-Facing Application | T1190 | Attacker sent a crafted OpenWire command exploiting CVE-2023-46604 against the internet-facing ActiveMQ broker |
| Command and Scripting Interpreter: Unix Shell | T1059.004 | Deserialized ProcessBuilder object executed a bash command to fetch and run the second-stage payload |
| Ingress Tool Transfer | T1105 | Reverse shell binary pulled from a second attacker-controlled server over HTTP |
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.
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