Java deserialization after Log4Shell: CVE-2023-46604
Log4Shell (CVE-2021-44228) made “Java deserialization” a household phrase for a few terrifying weeks in December 2021, but it wasn’t a one-off. Less than two years later, CVE-2023-46604, an unauthenticated remote code execution bug in Apache ActiveMQ’s OpenWire protocol, gave attackers the same outcome — arbitrary command execution with zero credentials — and it wasn’t a lookup-and-log JNDI trick this time, it was textbook insecure deserialization in a message broker that sits on the network path of a huge number of Java shops. CISA added it to the Known Exploited Vulnerabilities catalog within days, and ransomware crews had it weaponized before most defenders had patched (Rapid7). If you assumed Log4Shell was the last time a Java serialization bug would take down a fleet of production systems overnight, this one is worth sitting with.
Root cause
ActiveMQ’s OpenWire is the binary wire protocol brokers and clients use to
talk to each other, and by default it’s listening on port 61616 with no
authentication required to establish a connection. Inside that protocol,
one command type — ExceptionResponse — carries a class name and a message
string so a broker can tell a client “here’s the exception that happened.”
The marshalling code that unpacks it, BaseDataStreamMarshaller.createThrowable,
takes that attacker-supplied class name off the wire and instantiates it
directly, passing the attacker-supplied message string into the constructor,
without first checking that the class is actually a Throwable (Rapid7).
That’s the whole bug. Anyone who can open a TCP connection to the OpenWire
port can send a crafted EXCEPTION_RESPONSE packet naming any class on the
server’s classpath — including Spring’s ClassPathXmlApplicationContext,
which happily fetches and executes an XML bean definition from an
attacker-controlled URL. Point that at a remote XML file that defines a
ProcessBuilder-backed bean and the broker runs your shell command. It’s
the same family as the Commons Collections gadget chains that made Java
deserialization famous a decade ago — take a class that legitimately does
something dangerous when instantiated or invoked, and abuse a deserializer
that doesn’t discriminate about what it’s allowed to construct. The fix
Apache shipped adds exactly the type check that should have been there from
the start: reject any “exception” class name that isn’t actually a
Throwable before instantiating it (Rapid7).
Blast radius
CVE-2023-46604 scores CVSS 9.8, and researchers rated exploitation complexity as trivial — no auth, no user interaction, one crafted packet (Huntress). The result is arbitrary command execution as whatever OS user runs the broker process, and in the wild that turned into exactly what you’d expect once a reliable pre-auth RCE with a public PoC exists: a race between opportunistic and targeted actors. Rapid7 tracked exploitation attributed to HelloKitty ransomware starting within days of disclosure (Rapid7), Trend Micro and Sekoia both documented the Kinsing cryptomining botnet using it to drop miners and rootkits on Linux hosts (Trend Micro, Sekoia), and SOC Prime and others tracked TellYouThePass ransomware riding the same bug (SOCRadar). Message brokers tend to sit deep in the architecture — application servers, integration layers, IoT backends all talk to them — so a broker compromise isn’t an edge-of-network incident, it’s a foothold with a direct line to whatever internal services trust that broker’s traffic.
Remediation
Apache patched the marshalling logic and shipped fixed releases across every supported branch: 5.15.16, 5.16.7, 5.17.6, and 5.18.3, with 6.0.0 also carrying the fix (Apache ActiveMQ). If you’re still running an unpatched broker, upgrading is non-negotiable — this isn’t a “add authentication in front of it” situation, because the flaw is in how the broker parses its own wire protocol before any application-level auth applies. Restricting network access to port 61616 to only trusted broker and client hosts is a reasonable stopgap while you schedule the upgrade, but given how quickly this was weaponized, treat any internet-facing or broadly-reachable OpenWire port as already assumed-compromised and hunt for follow-on cryptominer or webshell activity, not just confirm the patch applied.
The bigger lesson
What’s actually changed since Log4Shell is the plumbing around detection:
WAF and IDS vendors now ship signatures for OpenWire-style deserialization
payloads within hours of a PoC landing, CISA’s KEV catalog gives defenders a
authoritative “this is being exploited right now” signal instead of relying
on vendor advisories alone, and software composition analysis tools flag
vulnerable broker and library versions in CI before they ever ship. On the
JDK side, JEP 415’s context-specific deserialization filters (Java 17+) let
you scope an allowlist to a specific ObjectInputStream instead of one
JVM-wide filter that’s either too loose or breaks half your app (Baeldung) — a real improvement over JEP 290’s blunt instrument.
What hasn’t changed is the underlying pattern: a component trusts a class
name or a byte stream from the network more than it should, and something
reachable on the classpath turns that trust into code execution. A 2024
NDSS study found over 3,600 GitHub Java projects still carrying known
deserialization vulnerabilities (Java Code Geeks) — the detection tooling
is faster, but it’s still catching the same class of bug, not preventing
it from being written. If your Java services still do implicit
ObjectInputStream deserialization or unauthenticated binary protocol
parsing anywhere on the network path, Log4Shell and ActiveMQ are both
telling you the same thing: the fix belongs in the code that trusts the
bytes, not in the WAF rule that flags them after the fact.
Join the discussion
Comments for this post live on social — reply to the thread.
Related posts
Cloud roundup: macOS Screen Sharing bug now under attack
A patched macOS Screen Sharing flaw is being exploited to plant crypto miners, a Windows Defender bypass has no fix yet, and EC2 gets built-in app health checks.
Cloud roundup: S3 finally names the policy that denied you
AWS S3 access-denied errors now name the exact policy ARN, Client VPN gets a scriptable CLI, and OpenAI ships authorized offensive-security models on Bedrock.
The Capital One breach: an SSRF bug into 100M records
A misconfigured WAF and an SSRF bug let an attacker reach the AWS metadata service and steal role credentials — exposing 100M+ Capital One records.