Is breaking out of a Java binary serialized string possible?
Let's look at what a serialized String actually looks like when serialized to a byte array. I grabbed sample code from this guide.
Code:
// Java code for serialization and deserialization
// of a Java object
import java.io.*;
class Demo implements java.io.Serializable
{
public String s;
// Default constructor
public Demo(String s)
{
this.s = s;
}
}
class Test
{
public static void main(String[] args)
{
Demo object = new Demo("helloWorld");
String filename = "file.ser";
// Serialization
try
{
//Saving of object in a file
FileOutputStream file = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(file);
// Method for serialization of object
out.writeObject(object);
out.close();
file.close();
System.out.println("Object has been serialized");
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
}
}
If I look at the serialized binary file file.ser
I see: (in a text editor that property handles binary, uploading a screenshot because the SE editor won't handle the non-printable chars properly)
If we break that down, we see:
- The name of the serialized object:
Demo
- The name of the serialized member object:
java/lang/String
- The data of this object:
helloWorld
Summary:
If you are only allowing the attacker to modify the contents of the String (ie "helloWorld
", then no, I don't believe it is possible to break out. However, if you are allowing the attacker to modify the entire byte stream, then they can replace the class name (ie "java/lang/string
") with whatever gadget class they want and compromise your application.
UPDATE to address this comment:
isn't that where visibility comes in handy for security? e.g. if the 'injected' class is not in the package or private .. no chance of accessing it. am i right?
I would say no. I'm not an expert, but my understanding is that the deserialization code is part of the core JVM and therefore completely ignores access modifiers like protected
, private
. Proof of concept, consider a serializable class with a private member:
class Demo implements java.io.Serializable
{
public String pub;
private String priv;
// No-args constructor
public Demo() {
pub = "Public data";
priv = "Private data";
}
}
When you serialize this and send it over the network, you need to include the private member, and the deserializer at the other end needs to be able to re-construct it:
Let's shake the myth that the java keyword private
has anything to do with security. It's a way for the developer who wrote the class to say "Hey, you shouldn't be accessing this directly" to the developer using the class, but in no way prevents you from using java reflection to read / modify private members or classes nor does it prevent data in private members or classes from being serialized / deserialized.