Deserialization of Untrusted Data
Description
Deserialization of Untrusted Data occurs when an application deserializes data from an untrusted source without sufficient verification that the resulting data will be valid and safe. Serialization converts objects into a format for storage or transmission; deserialization reconstructs objects from this format. When attackers control serialized data, they can manipulate it to instantiate arbitrary objects, set malicious property values, or trigger dangerous operations during the deserialization process. This frequently leads to remote code execution, as many programming languages allow objects to execute code during deserialization.
Risk
Deserialization vulnerabilities are among the most severe, consistently enabling remote code execution with no authentication required. Java, PHP, Python (pickle), .NET, and Ruby applications are commonly affected. The 2017 Equifax breach leveraged Apache Struts deserialization. Recent vulnerabilities in NVIDIA NeMo Framework and LangChain LangGraph demonstrate ongoing risk in AI/ML systems. Attackers craft malicious serialized payloads (often called "gadget chains") that trigger code execution when deserialized. The impact is typically complete system compromise with the privileges of the application.
Solution
Avoid deserializing data from untrusted sources whenever possible. Use text-based formats like JSON or YAML instead of binary serialization for data interchange. Implement integrity checks (HMAC, digital signatures) on serialized data. Use allowlists to restrict which classes can be deserialized. In Java, override ObjectInputStream.resolveClass() or use libraries like SerialKiller. In Python, never use pickle with untrusted data—use JSON instead. Isolate deserialization in sandboxed environments. Monitor for deserialization-related exceptions in logs.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Remote Code Execution Attackers execute arbitrary code by crafting malicious serialized objects that trigger code during deserialization. |
| Integrity | Scope: Data Manipulation Manipulated serialized objects alter application state and data. |
| Availability | Scope: Denial of Service Malicious payloads can consume excessive resources or crash the application. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: Deserializing untrusted data
import java.io.*;
public class UserService {
public User loadUser(byte[] data) throws Exception {
// Deserializing user-controlled data - RCE possible!
ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(data));
return (User) ois.readObject(); // Executes gadget chain
}
}
// Attacker sends serialized payload with malicious gadget chain
// Example: ysoserial CommonsCollections payload
# VULNERABLE: Using pickle with untrusted data
import pickle
def load_user_session(session_data):
# NEVER use pickle with untrusted data!
return pickle.loads(session_data) # RCE possible
# Attacker payload:
# import pickle, os
# class Exploit:
# def __reduce__(self):
# return (os.system, ('rm -rf /',))
# pickle.dumps(Exploit())
// VULNERABLE: Unserializing user input
<?php
$data = $_COOKIE['user_data'];
$user = unserialize($data); // Object injection!
// Attacker can instantiate any class with controlled properties
// If a __destruct or __wakeup method has side effects, RCE possible
?>
Fixed Code
// SAFE: Use JSON instead of Java serialization
import com.fasterxml.jackson.databind.ObjectMapper;
public class UserService {
private static final ObjectMapper mapper = new ObjectMapper();
public User loadUser(String jsonData) throws Exception {
// JSON parsing - no code execution
return mapper.readValue(jsonData, User.class);
}
}
// SAFE: Restricted deserialization with allowlist
import java.io.*;
import java.util.Set;
public class SafeObjectInputStream extends ObjectInputStream {
private static final Set<String> ALLOWED_CLASSES = Set.of(
"com.example.User",
"com.example.Address",
"java.lang.String"
);
public SafeObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
if (!ALLOWED_CLASSES.contains(desc.getName())) {
throw new InvalidClassException("Unauthorized class: " + desc.getName());
}
return super.resolveClass(desc);
}
}
# SAFE: Use JSON instead of pickle
import json
def load_user_session_safe(session_data):
# JSON is safe - no code execution during parsing
data = json.loads(session_data)
return User(**data)
# SAFE: If pickle is absolutely required, use HMAC verification
import pickle
import hmac
import hashlib
SECRET_KEY = b'your-secret-key'
def secure_pickle_dumps(obj):
data = pickle.dumps(obj)
signature = hmac.new(SECRET_KEY, data, hashlib.sha256).digest()
return signature + data
def secure_pickle_loads(signed_data):
signature = signed_data[:32]
data = signed_data[32:]
expected_sig = hmac.new(SECRET_KEY, data, hashlib.sha256).digest()
if not hmac.compare_digest(signature, expected_sig):
raise ValueError("Invalid signature - data tampered")
return pickle.loads(data) # Only deserialize verified data
// SAFE: Use JSON instead of serialize
<?php
$data = $_COOKIE['user_data'];
$user_array = json_decode($data, true); // Returns array, not object
// Validate the data
if (!isset($user_array['id']) || !is_int($user_array['id'])) {
throw new Exception("Invalid user data");
}
$user = new User($user_array['id'], $user_array['name']);
// SAFE: If unserialize is required, use allowed_classes
$user = unserialize($data, ['allowed_classes' => ['User', 'Address']]);
?>
Exploited in the Wild
Apache Struts / Equifax Breach (Apache, 2017)
CVE-2017-5638 involved Apache Struts deserializing untrusted data, leading to the Equifax breach that exposed 147 million records. This demonstrated the catastrophic potential of deserialization vulnerabilities.
NVIDIA NeMo Framework (NVIDIA, 2025)
CVE-2025-23249 in NVIDIA NeMo Framework allows remote code execution through crafted serialized data, affecting AI/ML development pipelines in technology, automotive, finance, and healthcare sectors.
LangChain LangGraph (LangChain, 2025)
CVE-2025-64439 in LangGraph allows RCE when msgpack serialization fails and falls back to vulnerable JSON mode, affecting AI agent frameworks.
Tools to test/exploit
-
ysoserial — Java deserialization payload generator.
-
phpggc — PHP gadget chain generator.
-
Burp Suite Deserialization Scanner — detect deserialization issues.
CVE Examples
-
CVE-2017-5638 — Apache Struts RCE (Equifax breach).
-
CVE-2025-23249 — NVIDIA NeMo deserialization RCE.
-
CVE-2015-4852 — WebLogic Commons Collections deserialization.
References
-
MITRE. "CWE-502: Deserialization of Untrusted Data." https://cwe.mitre.org/data/definitions/502.html
-
OWASP. "Deserialization of Untrusted Data." https://owasp.org/www-community/vulnerabilities/Deserialization_of_untrusted_data