0x01
- What is serialization and deserialization?
- The key function of serialization and deserialization?
- What are the characteristics of the data after deserialization?
- What are the similarities between java deserialization vulnerability and php deserialization vulnerability?
In this chapter, we only need to figure out the first three issues. In fact, the principle of java deserialization vulnerability is very simple, but each POP chain is more complicated. I will briefly introduce the serialization of java~
0x02
In my opinion, the serialization mechanism of java is to store an object persistently or transmit an object over the network. We all know that once the jvm is closed, the object in java is also destroyed, so if you want to save it, you need to convert it to a byte sequence and write it to a file or other.
Serialization: Convert an object to a sequence of bytes
Deserialization: Convert a sequence of bytes to an object
0x03
For a class object to be serialized, two conditions must be met:
1. This class must implement the java.io.Serializable object.
2. All attributes of this class must be serializable. If there is an attribute that is not serializable, the attribute must be marked as transient. (Let’s not pay attention to this for now)
0x04
To serialize an object, first create an OutputStream object, then encapsulate it in an ObjectOutputStream object, then just call writeObject() to serialize the object and send it to the OutputStream (the object is byte-based, So use InputStream and OutputStream to inherit the hierarchy).
To deserialize an object, you need to encapsulate an InputStream in ObjectInputStream, and then call readObject().
The text is not intuitive enough, let's directly upload the code (pay attention to the comments):
import java.io.*;
public class Test {
public static void main(String[] args){
User user = new User("axin", 18, 180);
try {
// 创建一个FIleOutputStream
FileOutputStream fos = new FileOutputStream("./user.ser");
// 将这个FIleOutputStream封装到ObjectOutputStream中
ObjectOutputStream os = new ObjectOutputStream(fos);
// 调用writeObject方法,序列化对象到文件user.ser中
os.writeObject(user);
System.out.println("读取数据:");
// 创建一个FIleInutputStream
FileInputStream fis = new FileInputStream("./user.ser");
// 将FileInputStream封装到ObjectInputStream中
ObjectInputStream oi = new ObjectInputStream(fis);
// 调用readObject从user.ser中反序列化出对象,还需要进行一下类型转换,默认是Object类型
User user1 = (User)oi.readObject();
user1.info();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class User implements Serializable{
private String name;
private int age;
private float height;
public User(String name, int age, float height) {
this.name = name;
this.age = age;
this.height = height;
}
public void info(){
System.out.println("Name: "+name+", Age: "+age+", Height: "+height);
}
// private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException{
// System.out.println("[*]执行了自定义的readObject函数");
// }
}
After the program is executed, a user.ser file will be generated in the current directory, and after deserialization, the info method will be executed to print out the User information on the terminal:
You can see that it was executed as expected, and a user.ser file was successfully generated. The user class object after deserialization is stored in this file. Let's take a look at the content. Here is a small tool xxd under Linux to view the content:
In the result displayed by xxd, the middle column is the hexadecimal display of the file, and the rightmost column is the character display. The characteristic value to be noted here is the first 32 digits in hexadecimal display:
AC ED: STREAM_MAGIC, which declares that the serialization protocol is used, from which you can determine whether the saved content is serialized data. (This is a very important point in the black box mining deserialization vulnerability)
00 05: STREAM_VERSION, serialization protocol version.
0x05
The basics of serialization have been discussed above, and everyone should know how to serialize and deserialize an object. So, where are the loopholes? If you know php deserialization, then you should know php automatically triggered when deserializing an object __weakup
, __destruct
these functions, if there are some dangerous operation among these functions, then it may lead to vulnerabilities, the same, which function will be triggered automatically when java deserialize it? That's right, it is readObject(), but the readObject() function in the demo above is not a method of ObjectInputStream. Developers can't control it. How can it cause a vulnerability?
In fact, Java supports custom readObject and writeObject methods. As long as a certain class implements the readObject method according to specific requirements, it will be automatically called during deserialization. If the custom readObject method is used Some dangerous operations will lead to deserialization loopholes. Try it out: we still use the above class, but this time we customize the readObject method of the User class, that is, remove the last bit of code comment, execute it again, and view the result:
As you can see, the custom readObject is indeed executed!
Now, we write dangerous operations in readObject, such as executing system commands and playing wireshark:
Of course, no one would write this in real applications, but Li'er is just such a reason, but dangerous operations in real applications are relatively hidden, not as naked as I wrote
0x06
I think someone, like me, should not understand the various streams in java (FileOutputStream/BufferedOutputStream/DataOutputStream/ObjectOutputStream). Here is a reference to help understand the serialization code:
https://www. cnblogs.com/shitouer/archive/2012/12/19/2823641.html