Hacking Book | Free Online Hacking Learning


java deserialization vulnerability analysis

Posted by truschel at 2020-04-05


1. 背景 2. 认识java序列化与反序列化 3. 理解漏洞的产生 4. POC构造 5. 实际漏洞环境测试 6. 总结


At present, in view of this "2015 most underestimated" vulnerability, various affected Java application manufacturers have released the repaired versions one after another, and the Apache commons collections project has also carried out certain security processing for the class libraries with vulnerabilities. However, a large number of websites on the network are still affected by this vulnerability.

Understanding Java serialization and deserialization


Serialization is the process of converting the state information of an object into a byte sequence (i.e. in the form of storage or transmission). Deserialization is the reverse process, which can be restored from a byte stream to an object. Note: byte sequence refers to the storage order of each word section when multi byte data is stored in computer memory or transmitted over the network.


1) permanently save the byte sequence of the object to the hard disk, usually in a file; 2) transfer the byte sequence of the object on the network.

Application scenario:

1) generally speaking, after the server is started, it will not be shut down again, but if it has to be restarted, and the user session is still in the corresponding operation, then it is necessary to use serialization to save the session information and put it on the hard disk. After the server is restarted, it will be reloaded. In this way, the user information will not be lost and can be saved permanently.

2) in many applications, some objects need to be serialized to leave the memory space and reside in the physical hard disk, so as to reduce the memory pressure or facilitate long-term storage.

For example, the most common is the session object in the web server. When 100000 users access it concurrently, 100000 session objects may appear, and the memory may not be enough. Therefore, the web container will serialize some sessions to the hard disk first, wait for them to be used, and then restore the objects saved in the hard disk to the memory.

Example: Taobao has a regular rush to buy activity every year. Many users will log in ahead of time and wait. They will not operate for a long time and keep them in memory. When they reach a specified time and hundreds of thousands of users access them concurrently, there may be hundreds of thousands of sessions. The memory may not be enough. At this time, we need to activate and passivate the object, let it leave the memory when it is idle, save the information to the hard disk, and reload it into the memory when it is needed.

API implementation in Java:

Location: java.io.objectoutputstream java.io.objectinputstream

Serialization: objectoutputstream class -- writeobject()

Note: this method serializes the obj object specified by the parameter and writes the byte sequence to a target output stream

注:该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中 注:该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中

The standard Java convention is to give the file a. Ser extension

按Java的标准约定是给文件一个.ser扩展名 按Java的标准约定是给文件一个.ser扩展名

Deserialization: objectinputstream class -- readobject()

Note: this method reads byte sequences from a source input stream, deserializes them into an object, and returns them.

Simple test code:

import java.io.*; /* import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.FileOutputStream; import java.io.FileInputStream; */ public class Java_Test{ public static void main(String args[]) throws Exception { String obj = "ls "; // 将序列化对象写入文件object.txt中 FileOutputStream fos = new FileOutputStream("aa.ser"); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(obj); os.close(); // 从文件object.txt中读取数据 FileInputStream fis = new FileInputStream("aa.ser"); ObjectInputStream ois = new ObjectInputStream(fis); // 通过反序列化恢复对象obj String obj2 = (String)ois.readObject(); System.out.println(obj2); ois.close(); } }

We can see that we first create a file through the input stream, then call the ObjectOutputStream class's writeObject method to write the serialized data to the file; then call the ObjectInputStream class's readObject method to de serialize the data and print the data content.

把序列化的数据写入该文件;然后调 反序列化数据并打印数据内容。 把序列化的数据写入该文件;然后调 反序列化数据并打印数据内容。

Objects of classes that implement the serializable and externalizable interfaces can only be serialized.

Code instance

我们创建一个Person接口,然后写两个方法:     序列化方法: 创建一个Person实例,调用函数为其三个成员变量赋值,通过writeObject方法把该对象序列化,写入Person.txt文件中     反序列化方法:调用readObject方法,返回一个经过饭序列化处理的对象   在测试主类里面,我们先序列化Person实例对象,然后又反序列化该对象,最后调用函数获取各个成员变量的值。

How did the leak come about?

Now that we know the process of serialization and deserialization, what if the data to be deserialized is specially constructed by us!

If Java application deserializes the user's input, that is, untrusted data, the attacker can generate unexpected objects by constructing malicious input, which may lead to arbitrary code execution.

Vulnerability analysis

From Apache commons collections

Due to the need of Java serialization / deserialization, some common libraries are often used in the development process.

Org.apache.commons.collections provides a class package to extend and add the standard Java collection framework, that is to say, these extensions also belong to the basic concept of collection, but have different functions. Collection in Java can be understood as a set of objects, and the objects in collection are called collection objects. Concrete collections are set, list, queue and so on. They are set types. In another way of understanding, collection is an abstraction of set, list, queue.


As an important component of Apache open source project, commons collections is widely used in the development of various Java applications. It is precisely because of the implementation of these classes and method calls in a large number of web applications that the vulnerability of deserialization is widespread and serious.  

There is a special interface in Apache commons collections, in which a class that implements the interface can call any function by calling Java's reflection mechanism, which is called invokertransformer.

JAVA反射机制 在运行状态中:       对于任意一个类,都能够判断一个对象所属的类;       对于任意一个类,都能够知道这个类的所有属性和方法;       对于任意一个对象,都能够调用它的任意一个方法和属性; 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

There are many concepts involved here. Don't worry. Let's analyze them in detail.

POC structure

After understanding the previous sequence and deserialization, we are ready to move. So how to exploit this vulnerability?

A little thought:

构造一个对象 —— 反序列化 —— 提交数据

OK? The key question we are facing now is: what kind of objects meet the conditions? How do I execute commands? How can it execute commands when deserialized?

First of all, we can know that in order to call external commands in Java, we can use this function Runtime.getRuntime ().Exec (). However, we need to find an object first to store and execute our commands in specific cases.

(1) Map class -- transformedmap

The map class is a data structure that stores key value pairs. In Apache commons collections, transformedmap is implemented. When an element is added / deleted / modified (i.e. key or value: the data storage form in the collection is an index corresponding to a value, just like the relationship between ID card and person), the transform method is called to automatically modify the transformation. The specific transformation logic is defined by the transformer class. In other words, when the data in the transformedmap class changes, some special transformations can be performed automatically, such as changing the data back when it is modified, or performing some operations that we have set in advance when the data changes.

As for what kind of operation or transformation will be performed, this is set in advance by us. This is called transform. We'll learn about transform later.

We can get an instance of transformedmap through the transformedmap. Decorate () method

TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。 第一个参数为待转化的Map对象 第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空) 第三个参数为Map对象内的value要经过的转化方法

(2) Transformer interface

Defines a functor interface implemented by classes that transform one object into another. 作用:接口于Transformer的类都具备把一个对象转化为另一个对象的功能

Source code for transform

We can see that the class receives an object, gets the name of the object, and then invokes a invoke reflection method. In addition, multiple transformers can be linked to form chained transformer. When triggered, chainedtransformer can call a series of transformations in sequence.

Here are some classes that implement the transformer interface. The arrow is what we will use.

ConstantTransformer 把一个对象转化为常量,并返回。 InvokerTransformer 通过反射,返回一个对象 ChainedTransformer ChainedTransformer为链式的Transformer,会挨个执行我们定义Transformer

Any function can be called simply by passing in the method name, parameter type, and parameter.

Here, we can see that we first obtain the runtime class with constanttransformer(), then call getruntime function by reflection, and then call exec() function of getruntime to execute the command ''. The calling relationships in turn are: runtime -- getruntime -- exec()

Therefore, we need to construct the chainedtransformer chain in advance. It will call the runtime, getruntime and exec functions in the order we set, and then execute the command. At the formal beginning, we first construct a transformemap instance, and then try to modify its data so that it automatically calls the tansform () method to perform a specific transformation (that is, what we set before)


1)构造一个Map和一个能够执行代码的ChainedTransformer,   2)生成一个TransformedMap实例   3)利用MapEntry的setValue()函数对TransformedMap中的键值进行修改   4)触发我们构造的之前构造的链式Transforme(即ChainedTransformer)进行自动转换

Knowledge supplement

Map是java中的接口,Map.Entry是Map的一个内部接口。 Map提供了一些常用方法,如keySet()、entrySet()等方法。 keySet()方法返回值是Map中key值的集合; entrySet()的返回值也是返回一个Set集合,此集合的类型为Map.Entry。 Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为EntryK,V。它表示Map中的一个实体(一个key-value对)。 接口中有getKey(),getValue方法,可以用来对集合中的元素进行修改

We can do that


The current construction also depends on a certain item in the map to call setvalue(). How can the execution be triggered directly when the readobject() method is called?

A step closer

We know that if a class's method is overridden, the modified method will be called first when the function is called. Therefore, if a serializable class rewrites the readObject () method and modifies the key value of the map type variable in readObject (), and the map variable is controllable, then we can achieve the attack target.

So, we began to look for it, and finally, we found it~

Annotationinvocationhandler class

这个类有一个成员变量memberValues是Map类型 更棒的是,AnnotationInvocationHandler的readObject()函数中对memberValues的每一项调用了setValue()函数对value值进行一些变换。

This class completely meets our requirements, so our ideas are very clear

1)首先构造一个Map和一个能够执行代码的ChainedTransformer,   2)生成一个TransformedMap实例   3)实例化AnnotationInvocationHandler,并对其进行序列化,   4)当触发readObject()反序列化的时候,就能实现命令执行。

POC execution process is transformedmap-annotationinvocationhandler. Readobject() - setvalue() - vulnerability triggered successfully

Let's review all the technical details used

(1)java方法重写:如果一个类的方法被重写,那么调用该方法时优先调用该方法 (2)JAVA反射机制:在运行状态中              对于任意一个类,都能够判断一个对象所属的类;              对于任意一个类,都能够知道这个类的所有属性和方法;              对于任意一个对象,都能够调用它的任意一个方法和属性; (3)认识关键类与函数 TransformedMap : 利用其value修改时触发transform()的特性 ChainedTransformer: 会挨个执行我们定义的Transformer Transformer: 存放我们要执行的命令 AnnotationInvocationHandler:对memberValues的每一项调用了setValue()函数

Concrete realization

Vulnerability mining

Related tools

Ysoserial is a tool to generate serialized payload data with our ideas. The payload for Apache commons collection 3 is also constructed based on transformedmap and invokertransformer. However, when triggering, instead of the annotationinvocationhandler described above, the relevant code in java.lang.reflect.proxy is used to implement the trigger. No further in-depth analysis will be done here. Interested readers can refer to ysoserial's source code.

TransformedMap InvokerTransformer AnnotationInvocationHandler java.lang.reflect.Proxy TransformedMap InvokerTransformer AnnotationInvocationHandler java.lang.reflect.Proxy

Related tool links


Actual vulnerability environment test

JBOSS JBoss是一个管理和运行EJB项目的容器和服务器 Enterprise JavaBean (EJB)规范定义了开发和部署基于事务性、分布式对象应用程序的服务器端软件组件的体系结构。 企业组织可以构建它们自己的组件,或从第三方供应商购买组件。 这些服务器端组件称作 Enterprise Bean,它们是 Enterprise JavaBean 容器中驻留的分布式对象,为分布在网络中的客户机提供远程服务。

Actual test version

Add: CentOS opens the firewall by default, so port 80 cannot be accessed normally). Enter the command: iptables - I input - P TCP -- dport 80 - J accept

If the user accesses a URL, it will actually return a primitive Java object, which obviously has a vulnerability. But because jmxinvoker servlet runs on the same port as the main web application, it is seldom blocked by the firewall. This vulnerability can be frequently exploited through the Internet.

Common test commands

When we submit the payload data, we can grab the data package for analysis, which looks like this (the picture is not in our own environment test)



开发者:   为了确保序列化的安全性,可以对于一些敏感信息加密;   确保对象的成员变量符合正确的约束条件;   确保需要优化序列化的性能。 漏洞挖掘:   (1)通过代码审计/行为分析等手段发现漏洞所在靶点   (2)进行POC分析构造时可以利用逆推法

Bug fix

Java反序列化漏洞的快速排查和修复方案 目前打包有apache commons collections库并且应用比较广泛的主要组件有Jenkins WebLogic Jboss WebSphere OpenNMS。 其中Jenkins由于功能需要大都直接暴露给公网。 首先确认产品中是否包含上述5种组件 使用grep命令或者其他相关搜索命令检测上述组件安装目录是否包含库Apache Commons Collections。搜索下列jar。 commons-collections.jar *.commons-collections.jar apache.commons.collections.jar *.commons-collections.*.jar 如果包含请参考下述解决方案进行修复。

Strictly speaking, Java has relatively few security problems. Most of the problems are caused by reflection. Finally, the runtime.exec (string CMD) function is used to execute external commands.

If the JVM can be forbidden to execute external commands, the harm of unknown vulnerabilities will be greatly reduced and the security of the JVM will be greatly improved. For example:

As shown above, as long as you simply add a program to your Java code, you can disable the execution of external programs.

Forbidding the JVM to execute external commands is a simple and effective way to improve the security of the JVM. You can consider strengthening the detection of runtime.exec related code during code security scanning.

Repair for other web applications

Related CVE




Related learning materials