blueredix logo
info vuln-class-deserialization

Insecure Deserialization, in plain English

When an application reads structured data sent over the network and the attacker can shape that data, "reading" can quietly turn into "running attacker-supplied code". The bug class behind dozens of headline RCEs.

What this is

Many applications send structured data over the network and then “read it back” on the other end. The technical word for “read it back” is deserialize. When the data is internal (a cache, a queue, a service-to-service call), that is fine. When the data arrives from somewhere the attacker can influence (a cookie, an HTTP header, a JSON Web Token, a request body), how the application reads it back becomes a security question.

The core issue: in many languages, “deserializing” is not just “fill in fields”. The reader instantiates classes, calls constructors, and runs hooks like readObject or __wakeup. If the attacker can choose which classes get instantiated, they can pick classes whose constructors trigger side effects: opening files, executing shell commands, loading remote code. A chain of such classes is called a “gadget chain”, and chains exist for almost every popular runtime.

Where it happens

The languages and serializers most commonly involved:

  • Java. The canonical home of deserialization vulnerabilities. ObjectInputStream.readObject() plus the Apache Commons Collections library, or any of dozens of others on the classpath, has caused breaches at PayPal, Cisco, Jenkins, WebLogic, Confluence, and more.
  • Python pickle. The documentation explicitly says “do not unpickle data received from untrusted sources”. Despite that, pickle still shows up in API endpoints, caches, and message queues regularly.
  • .NET BinaryFormatter, NetDataContractSerializer, LosFormatter. Microsoft has formally deprecated all of these for security reasons. Legacy ASP.NET ViewState is the largest install base.
  • PHP unserialize(). Same gadget-chain dynamic, attacking the many PHP frameworks (Laravel, Symfony, WordPress) by chaining classes loaded by Composer’s autoloader.
  • Ruby Marshal.load and YAML.load with default options. Rails CVEs every couple of years trace back to this.
  • Node.js is mostly safe by default, since JSON does not have this problem, but a few unfortunate libraries (node-serialize, older serialize-javascript versions) reproduce the issue.

What an attacker does

Once a deserialization bug is reachable, the rest is short:

  1. Identify the runtime (often visible in error pages or response headers).
  2. Pick a published gadget chain for that runtime plus the libraries on the application’s classpath. ysoserial (Java) and marshalsec (Java) are the canonical tools; equivalents exist for .NET (ysoserial.net) and PHP (PHPGGC).
  3. Generate the payload with a one-liner that runs whatever command the attacker wants.
  4. Deliver the payload at the deserialization point: Cookie, JWT, HTTP body, RMI port, JMX endpoint.
  5. Receive a reverse shell.

Severity for deserialization-driven remote code execution typically lands at 9.8 out of 10. EPSS scores for known-public chains stay high for years, because the same exploit binary works against every vulnerable instance.

How it gets fixed

The single best fix is don’t deserialize untrusted data with formats that allow arbitrary classes.

Replace the dangerous serializers with safe alternatives:

Dangerous Safer alternative
Java ObjectInputStream JSON (Jackson configured strictly), Protobuf, Avro
Python pickle JSON, msgpack with strict types
.NET BinaryFormatter System.Text.Json, MessagePack-CSharp
PHP unserialize JSON via json_decode()
Ruby YAML.load YAML.safe_load, JSON

JSON is not magic. There are also “polymorphic deserialization” patterns in Jackson and Gson that can re-introduce the class-instantiation risk. Use the strict configuration (no enableDefaultTyping, no @JsonTypeInfo with class-name resolution).

If you genuinely cannot replace the serializer (for example a legacy protocol you don’t control), apply layered defences:

  • HMAC-sign the serialized blob with a server-side secret and reject anything that doesn’t validate. The attacker can no longer alter the bytes.
  • Constrain class instantiation. Java’s ObjectInputFilter (since JDK 9) lets you allowlist exactly which classes are accepted.
  • Patch every library on the classpath that has a known gadget chain. The usual suspects are apache-commons-collections, spring-core, snakeyaml, and ehcache.

How blueredix surfaces deserialization findings

The scanner flags CVEs in third-party software whose description mentions deserialization, and runs templates that probe for known-vulnerable endpoints (Spring Cloud Gateway, Atlassian, WebLogic) where a specific exploit is well-understood. Findings link to the relevant CVE and back to How to read a CVE.

We don’t try to deserialize attacker payloads against your application. That is penetration-testing territory.

Further reading