Java Deserialization

Lecture

Java Serialization Format

Java uses binary serialization formats. This is more difficult to read, but you can still identify serialized data if you know how to recognize a few tell-tale signs. For example, serialized Java objects always begin with the same bytes, which are encoded as ac ed in hexadecimal and rO0 in Base64.

For Java objects to be serializable, their classes must implement the java.io.Serializable interface. These classes also implement special methods, writeObject() and readObject(), to handle the serialization and deserialization, respectively, of objects of that class. Here is an example:

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.io.IOException;

class User implements Serializable{
  public String username;
}

public class SerializeTest{
  public static void main(String args[]) throws Exception{
    // javac SerializeTest.java && java SerializeTest
    User newUser = new User();
    newUser.username = "ret2basic";
    
    FileOutputStream fos = new FileOutputStream("object.ser");
    ObjectOutputStream os = new ObjectOutputStream(fos);
    os.writeObject(newUser);
    os.close();
    
    FileInputStream is = new FileInputStream("object.ser");
    ObjectInputStream ois = new ObjectInputStream(is);
    User storedUser = (User)ois.readObject();
    System.out.println(storedUser.username);
    ois.close();
    }
}

ysoserial

ysoserial lets you choose one of the provided gadget chains for a library that you think the target application is using, then pass in a command that you want to execute. It then creates an appropriate serialized object based on the selected chain. This still involves a certain amount of trial and error, but it is considerably less labor-intensive than constructing your own gadget chains manually. Example usage:

java -jar path/to/ysoserial.jar CommonsCollections4 '<command>'

Note that not all of the gadget chains in ysoserial enable you to run arbitrary code. Instead, they may be useful for other purposes. For example, you can use the following ones to help you quickly detect insecure deserialization on virtually any server:

  • The URLDNS chain triggers a DNS lookup for a supplied URL. Most importantly, it does not rely on the target application using a specific vulnerable library and works in any known Java version. This makes it the most universal gadget chain for detection purposes. If you spot a serialized object in the traffic, you can try using this gadget chain to generate an object that triggers a DNS interaction with the Burp Collaborator server. If it does, you can be sure that deserialization occurred on your target.

  • JRMPClient is another universal chain that you can use for initial detection. It causes the server to try establishing a TCP connection to the supplied IP address. Note that you need to provide a raw IP address rather than a hostname. This chain may be useful in environments where all outbound traffic is firewalled, including DNS lookups. You can try generating payloads with two different IP addresses: a local one and a firewalled, external one. If the application responds immediately for a payload with a local address, but hangs for a payload with an external address, causing a delay in the response, this indicates that the gadget chain worked because the server tried to connect to the firewalled address. In this case, the subtle time difference in responses can help you to detect whether deserialization occurs on the server, even in blind cases.

Reference

Last updated