Insecure Deserialization Part 1: Understanding De|Serialization

Zyad Elsayed
17 min readJul 15, 2024

--

Table Of Content

· ⠀What is Serialization?
· ⠀What is Deserialization?
· ⠀Example of serialization format
· ⠀Key Characteristics
· ⠀Practical Uses
· ⠀Important Considerations
· ⠀Advantages of Serialization
· ⠀Serialization code format in different programming language
⠀⠀ ‣ ⠀ PHP Serialization
⠀⠀ ‣⠀⠀Java Serialization
⠀⠀ ‣⠀⠀C# Serialization
⠀⠀⠀⠀⠀ ‣⠀⠀Serialization in .NET
⠀⠀⠀⠀⠀⠀⠀⠀ ∘⠀⠀ BinaryFormatter
⠀⠀⠀⠀⠀⠀⠀⠀ ∘⠀⠀ DataContractSerializer
⠀⠀⠀⠀⠀⠀⠀⠀ ∘ ⠀⠀NetDataContractSerializer
⠀⠀⠀⠀⠀⠀⠀⠀ ∘⠀⠀XML Serialization

⠀⠀ ‣⠀⠀Python Serialization
⠀⠀ ‣⠀⠀JavaScript (JSON) Serialization
⠀⠀ ‣⠀⠀Ruby Serialization
· ⠀Marking specific field from been serialized
· ⠀Serialized data format explain
⠀∘⠀ PHP Serialization Format
⠀ ∘⠀ Java Serialization Format
⠀ ∘ ⠀JSON (JavaScript Object Notation)
⠀ ∘ ⠀XML (Extensible Markup Language)
· ⠀References

What is Serialization?

Serialization is the process of converting complex data structures, including objects and their fields “essentially a combination of code and data represented within a region of data storage” into a series stream of bytes. This flattened format saves the state of the object in a form that can be easily transmitted and restored.

Serialization allows us to store the state of programmatic objects in a sequence of bytes in a reversible manner. This means that an object (whether a single variable, a set of variables, or even a whole class) can be transported to another program or location and accurately reconstructed.

What is Deserialization?

Deserialization is the process of converting a stream of bytes, typically received over a network or read from a storage medium, back into a structured data format or object in the exact state as when it was serialized.

Example of serialization format

Example of PHP serialization

Key Characteristics

  • Storage and Transmission: Serialized data can be stored in a persistent storage medium (like a file or database) or transmitted over a network.
  • Restoration: The serialized data can be easily deserialized back to its original data structure or object.
  • Interoperability: Serialization enables data to be shared between different versions of an application or across different applications, even if they use different binary formats as some programming languages can’t transfer data directly between different versions.

Practical Uses

Serialization is widely used for:

  • Storing Data: Saving the state of an object to a file or database for later retrieval.
  • Transmitting Data: Sending objects over a network to another application or service.
  • Remote Procedure Calls (RPC): Invoking methods on a remote server by sending serialized objects as arguments.

Important Considerations

  • Data Integrity: Serialized data itself is not inherently encrypted or signed, making it susceptible to tampering if not properly secured.
  • Compatibility: Different programming languages and applications may have varying serialization formats, necessitating careful handling to ensure compatibility.

Advantages of Serialization

Serializing data simplifies several tasks in programming, particularly when dealing with complex data structures.

Serializing data makes it much simpler to:

  • Write complex data to inter-process memory, a file, or a database
  • Send complex data, for example, over a network, between different components of an application, or in an API call

Serialization code format in different programming language

Serialization formats vary across programming languages, each offering different methods and formats

Java:

  • Serialization: Binary serialization using the Serializable interface.
  • Deserialization: Uses ObjectInputStream for binary deserialization.

Python:

  • Serialization and Deserialization: Uses pickle for both binary serialization and deserialization.

JavaScript:

  • Serialization: Text-based serialization using JSON.stringify.
  • Deserialization: Uses JSON.parse for JSON deserialization.

C#:

  • Serialization: Provides multiple ways to serialize objects, including JSON serialization using System.Text.Json.
  • Deserialization: Uses JsonSerializer.Deserialize for JSON deserialization.

PHP:

  • Serialization and Deserialization: Uses serialize for string-based serialization and unserialize for string-based deserialization.

Ruby:

  • Serialization and Deserialization: Uses Marshal for both binary serialization and deserialization.

PHP Serialization

PHP provides a built-in serialize function for serializing objects to a string format.

Example 1

<?php
class Person {
public $name;
public $age;

public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
}

$person = new Person("Alice", 30);

// Serialize

$serializedPerson = serialize($person);
echo $serializedPerson . "\n";

// Deserialize

$deserializedPerson = unserialize($serializedPerson);
echo "Name: " . $deserializedPerson->name . ", Age: " . $deserializedPerson->age;

?>

Example 2

<?php
class User {
public $id = 12;
public $fname = "john";
public $lname = "doe";
public $arr = array();

public function __construct() {
echo "[*] Constructor Called\n";
$this->arr[1] = 'new entry';
}
}

$u = new User;

// Serialize
echo "[*] Serialized class:\n\n";
$u_serialized = serialize($u);
echo $u_serialized."\n\n";

// Deserialize
echo "[*] Unserialized class:\n\n";
$u = unserialize($u_serialized);
echo "First Name: " . $u->fname . "\n";
echo "Last Name: " . $u->lname . "\n";
echo "ID: " . $u->id . "\n";
echo "Array content at index 1: " . $u->arr[1] . "\n";
?>
O:4:"User":4:{s:2:"id";i:12;s:5:"fname";s:4:"john";s:5:"lname";s:3:"doe";s:3:"arr";a:1:{i:1;s:9:"new entry";}}

Note that only the data “attributes or fields of an object” is serialized, not the entire code or functions associated with the object.
By considering that, you can not define a method or new methods in the serialized data

Since you cannot define new functions within the serialized data, any attack that involves manipulating object behavior would require the exploitation of pre-existing methods in the class definition.

Magic function self called without any objects

__sleep in the serialization process

__wakeup in deserialization process

Java Serialization

Java provides a built-in mechanism for object serialization using the Serializable interface. The default format is binary. Additionally, the ObjectInputStream class is used for deserializing objects.


// app1.java

import java.io.*;
// Serializable is one package of java.io "java.io.Serializable"

class Person implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}
}

// SerializationExample.java

import java.io.*;

public class SerializationExample {
public static void main(String[] args) {
Person person = new Person("Alice", 30);

// Serialize
try (FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(person);
} catch (IOException i) {
i.printStackTrace();
}

// Deserialize
Person deserializedPerson = null;
try (FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn)) {
deserializedPerson = (Person) in.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("Serialized data saved to person.ser");
System.out.println("Name: " + deserializedPerson.name + ", Age: " + deserializedPerson.age);
}
}
// app2.java

import java.io.*;

// User class
class User implements Serializable {
private static final long serialVersionUID = 1L;
String username;
int salary;

public User(String username, int salary) {
this.username = username;
this.salary = salary;
}
}

// JavaSerialization.java

import java.io.*;
public class JavaSerialization {

public static void main(String[] args) {
// Create a new User object
User user = new User("john_doe", 50000);

// Serialize the User object
try (FileOutputStream fileOut = new FileOutputStream("user.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}

// Deserialize the User object
User deserializedUser = null;
try (FileInputStream fileIn = new FileInputStream("user.ser");
ObjectInputStream in = new ObjectInputStream(fileIn)) {
deserializedUser = (User) in.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}

// Print out the deserialized User object
if (deserializedUser != null) {
System.out.println("Username: " + deserializedUser.username + ", Salary: " + deserializedUser.salary);
}
}
}

C# Serialization

C sharp provides multiple ways to serialize and deserialize objects, each with its own use cases and associated formats, including binary, XML, and JSON.

Note that: Data serialized using one of these mechanisms must be de serialized using the same one.

Methods of Serialization in .NET

  1. BinaryFormatter
  2. DataContractSerializer
  3. NetDataContractSerializer
  4. XML Serialization

Each of these methods results in a different format of a serialized object. Each serialization type is directly connected to certain supported objects and types, with their usage being situational and tied to .NET internals.

BinaryFormatter

Commonly used for binary serialization; however, its use is now discouraged due to security concerns.

Example 1

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class Person {
public string? Name { get; set; }
public int Age { get; set; }
}

public class Program {
public static void Main() {
Person person = new Person { Name = "Alice", Age = 30 };

// Serialize
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream()) {
formatter.Serialize(stream, person);
byte[] byteArray = stream.ToArray();
Console.WriteLine("Serialized binary data: " + BitConverter.ToString(byteArray));

// Deserialize
stream.Position = 0; // Reset stream position to the beginning
Person deserializedPerson = (Person)formatter.Deserialize(stream);
Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}");
}
}
}
BinaryFormatter security concern
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class Person {
public string? Name { get; set; }
public int Age { get; set; }
}

public class Program {
public static void Main() {
Person person = new Person { Name = "Alice", Age = 30 };

// Serialize
#pragma warning disable SYSLIB0011
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream()) {
formatter.Serialize(stream, person);
#pragma warning restore SYSLIB0011

byte[] byteArray = stream.ToArray();
Console.WriteLine("Serialized binary data: " + BitConverter.ToString(byteArray));

// Deserialize
#pragma warning disable SYSLIB0011
stream.Position = 0; // Reset stream position to the beginning
Person deserializedPerson = (Person)formatter.Deserialize(stream);
#pragma warning restore SYSLIB0011

Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}");
}
}
}
After suppress the warnings

Example 2

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace BinarySerializationExample
{
class Program
{
private const string filename = "data.dat";

static void Main(string[] args)
{
Console.WriteLine();

// Delete old file, if it exists
if (File.Exists(filename))
{
Console.WriteLine("Deleting old file");
File.Delete(filename);
}

// Create string
var u = "Some String here";

// Persist to file
using (FileStream stream = File.Create(filename))
{
#pragma warning disable SYSLIB0011
BinaryFormatter formatter = new BinaryFormatter();
Console.WriteLine("Serializing string");
formatter.Serialize(stream, u);
#pragma warning restore SYSLIB0011
}

// Restore from file
using (FileStream stream = File.OpenRead(filename))
{
#pragma warning disable SYSLIB0011
BinaryFormatter formatter = new BinaryFormatter();
Console.WriteLine("Deserializing string");
var v = (string)formatter.Deserialize(stream);
Console.WriteLine($"Deserialized string: {v}");
#pragma warning restore SYSLIB0011
}

Console.WriteLine();
Console.WriteLine("Press Enter Key");
Console.Read();
} // Main
} // class
} // namespace

If you need to serialize data in a binary format securely, consider using System.IO.MemoryStream combined with System.Text.Json to convert JSON to bytes. Although this approach won't produce the same binary format as BinaryFormatter, it is more secure.

DataContractSerializer

A versatile serializer for XML and binary formats, suitable for complex types including those with inheritance and polymorphism.

using System;
using System.IO;
using System.Runtime.Serialization;

[DataContract]
public class Person {
[DataMember]
public string? Name { get; set; }
[DataMember]
public int Age { get; set; }
}

public class Program {
public static void Main() {
Person person = new Person { Name = "Alice", Age = 30 };

// Serialize
DataContractSerializer serializer = new DataContractSerializer(typeof(Person));
using (MemoryStream stream = new MemoryStream()) {
serializer.WriteObject(stream, person);
byte[] byteArray = stream.ToArray();
Console.WriteLine("Serialized binary data: " + BitConverter.ToString(byteArray));

// Deserialize
stream.Position = 0; // Reset stream position to the beginning
Person? deserializedPerson = serializer.ReadObject(stream) as Person;
if (deserializedPerson != null) {
Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}");
} else {
Console.WriteLine("Deserialization failed.");
}
}
}
}

The most common way is using the System.Text.Json namespace for JSON serialization or System.Xml.Serialization for XML tasks

using System;
using System.IO;
using System.Text.Json;

public class Person {
public string? Name { get; set; }
public int Age { get; set; }
}

public class Program {
public static void Main() {
Person person = new Person { Name = "Alice", Age = 30 };

// Serialize
string jsonString = JsonSerializer.Serialize(person);
Console.WriteLine(jsonString);

// Deserialize
Person? deserializedPerson = JsonSerializer.Deserialize<Person>(jsonString);
if (deserializedPerson != null) {
Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}");
}
}
}
using System;
using System.IO;
using System.Xml.Serialization;

public class Person {
public string? Name { get; set; }
public int Age { get; set; }
}

public class Program {
public static void Main() {
Person person = new Person { Name = "Alice", Age = 30 };

// Serialize to XML
XmlSerializer serializer = new XmlSerializer(typeof(Person));
using (StringWriter writer = new StringWriter()) {
serializer.Serialize(writer, person);
string xmlString = writer.ToString();
Console.WriteLine("Serialized XML:");
Console.WriteLine(xmlString);

// Deserialize from XML
using (StringReader reader = new StringReader(xmlString)) {
Person? deserializedPerson = (Person?)serializer.Deserialize(reader);
if (deserializedPerson != null) {
Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}");
}
}
}
}
}

NetDataContractSerializer

Similar to DataContractSerializer but includes CLR type information in the serialized XML but, It is tightly coupled with .NET types, making it less suitable for interoperability scenarios.

using System;
using System.IO;
using System.Runtime.Serialization;

[DataContract]
public class Person {
[DataMember]
public string? Name { get; set; }
[DataMember]
public int Age { get; set; }
}

public class Program {
public static void Main() {
Person person = new Person { Name = "Alice", Age = 30 };

// Serialize
DataContractSerializer serializer = new DataContractSerializer(typeof(Person));
using (MemoryStream stream = new MemoryStream()) {
serializer.WriteObject(stream, person);
byte[] byteArray = stream.ToArray();
string xmlString = System.Text.Encoding.UTF8.GetString(byteArray);
Console.WriteLine("Serialized XML with DataContractSerializer:");
Console.WriteLine(xmlString);

// Deserialize
stream.Position = 0; // Reset stream position to the beginning
Person? deserializedPerson = serializer.ReadObject(stream) as Person;
if (deserializedPerson != null) {
Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}");
} else {
Console.WriteLine("Deserialization failed.");
}
}
}
}

Python Serialization

Python uses the pickle module for object serialization, which converts objects to a binary format.

import pickle

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

person = Person("Alice", 30)

# Serialize
with open('person.pkl', 'wb') as f:
pickle.dump(person, f)

# Deserialize
with open('person.pkl', 'rb') as f:
deserialized_person = pickle.load(f)

print(f"Name: {deserialized_person.name}, Age: {deserialized_person.age}")
import pickle
import base64

...
serialized_data = request.form['serialized_data']
notes_obj = pickle.loads(base64.b64decode(serialized_data))
message = "Notes successfully unpickled."
...

elif request.method == 'POST':
if 'pickle' in request.form:
content = request.form['note_content']
notes_obj.add_note(content)
pickled_content = pickle.dumps(notes_obj)
serialized_data = base64.b64encode(pickled_content).decode('utf-8')
binary_data = ' '.join(f'{x:02x}' for x in pickled_content)
message = "Notes pickled successfully."

Serialization (Pickling) : When a user submits a note, the Notes class instance (including all notes) is serialised using pickle.dumps(). This function transforms the Python object into a binary format that Python can later turn back into an object.

Deserialization (Unpickling) : The binary data is then passed to pickle.loads(), which reconstructs the original Python object from the binary stream.

JavaScript (JSON) Serialization

JavaScript typically uses JSON (JavaScript Object Notation) for serialization. JSON is a text-based format that’s easy to read and write.

const person = {
name: "Alice",
age: 30
};

// Serialize
const jsonString = JSON.stringify(person);
console.loag(jsonString);

// Deserialize
const deserializedPerson = JSON.parse(jsonString);
console.log(`Name: ${deserializedPerson.name}, Age: ${deserializedPerson.age}`);

Ruby Serialization

Ruby uses the Marshal module for binary serialization.

require 'base64'

class Person
attr_accessor :name, :age

def initialize(name, age)
@name = name
@age = age
end
end

person = Person.new("Alice", 30)

# Serialize to binary
serialized_binary = Marshal.dump(person)
puts "Serialized (Binary):"
puts serialized_binary

# Serialize to Base64
serialized_base64 = Base64.strict_encode64(Marshal.dump(person))
puts "\nSerialized (Base64):"
puts serialized_base64

# Deserialization
deserialized_person_binary = Marshal.load(serialized_binary)
puts "\nDeserialized from Binary:"
puts "Name: #{deserialized_person_binary.name}, Age: #{deserialized_person_binary.age}"

Marking specific field from been serialized

Note that all of the original object’s attributes are stored in the serialized data stream, including any private fields.

To prevent a field from being serialized, it must be identified in the coding process while class declaration .

In Java and Kotlin you can explicitly mark it as transient to prevent it from being serialized

import java.io.Serializable;

public class User implements Serializable {
private String name;
private int age;
private transient String password; // This field will not be serialized

public User(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}

}
import java.io.Serializable

data class User(
val name: String,
val age: Int,
@Transient val password: String // This field will not be serialized
) : Serializable

In C# you can use [NonSerialized]

[Serializable]
public class User {
public string Name;
public int Age;
[NonSerialized] public string Password; // This field will not be serialized

public User(string name, int age, string password) {
Name = name;
Age = age;
Password = password;
}
}

In Python you can control it by defining __getstate__ and __setstate__ The __getstate__ method can exclude certain attributes from being serialized.

import pickle

class User:
def __init__(self, name, age, password):
self.name = name
self.age = age
self._password = password # This field will not be serialized

def __getstate__(self):
state = self.__dict__.copy()
del state['_password'] # Exclude the password field
return state

def __setstate__(self, state):
self.__dict__.update(state)

# Example usage
user = User("Alice", 30, "secret")
serialized_data = pickle.dumps(user)

In JavaScript, particularly with JSON serialization, you can control which properties get serialized by defining a custom toJSON method.

class User {
constructor(name, age, password) {
this.name = name;
this.age = age;
this._password = password; // This field will not be serialized
}

toJSON() {
return {
name: this.name,
age: this.age
// Exclude the password field
};
}
}

const user = new User("Bob", 25, "secret");
const serializedData = JSON.stringify(user);
console.log(serializedData); // Output: {"name":"Bob","age":25}

Serialized data format explain

PHP Serialization Format

PHP uses a mostly human-readable string format, with letters representing the data type and numbers representing the length of each entry.

╔═══════════╦═══════════════════════════════════════════════════════════╦═════════════════════════════════════════╦══════════════════════════════════════╗
║ Type ║ Format ║ Example ║ ║
╠═══════════╬═══════════════════════════════════════════════════════════╬═════════════════════════════════════════╬══════════════════════════════════════╣
║ String ║ s:<length>:"<value>"; ║ s:12:"Welcome to THM!"; ║ ║
║ Integer ║ i:<value>; ║ i:42; ║ ║
║ Float ║ d:<value>; ║ d:3.14; ║ ║
║ Boolean ║ b:<value>; (1 for true, 0 for false) ║ b:1; ║ ║
║ Null ║ N; ║ N; ║ ║
║ Array ║ a:<length>:{<key><value>...} ║ a:2:{i:0;s:5:"apple";i:1;s:6:"banana";} ║ ║
║ Object ║ O:<length>:"<class_name>":<length>:{<property><value>...} ║ O:8:"stdClass":1:{s:3:"foo";s:3:"bar";} ║ ║
║ Reference ║ R:<ref_id>; ║ R:2; ║ ║
║ Resource ║ r:<resource_id>; ║ r:3; ║ (Unsupported in modern PHP versions) ║
╚═══════════╩═══════════════════════════════════════════════════════════╩═════════════════════════════════════════╩══════════════════════════════════════╝

Notes:

  • Properties in serialized PHP objects affect <s> value:
  • Public properties use the simple property name.
  • Protected properties prepend \0*\0 to the property name.
  • Private properties prepend \0<class>\0<s>\0, where <class> is the fully qualified class name.

Example 1

O:4:"user":6:{s:8:"username";s:4:"zyad";s:6:"salary";i:20000;s:8:"isActive";b:1;s:7:"balance";d:1234.56;s:5:"notes";N;s:11:"preferences";a:2:{s:5:"theme";s:4:"dark";s:8:"language";s:2:"en";}}

Explanation:

  • O:4:"user":6: An object (O) of class user with 4 characters in its name and 6 properties.
  • s:8:"username";s:4:"zyad": A string property named username with a value of "zyad".
  • s:6:"salary";i:20000: An integer property named salary with a value of 20000.
  • s:8:"isActive";b:1: A boolean property named isActive with a value of true.
  • s:7:"balance";d:1234.56: A float property named balance with a value of 1234.56.
  • s:5:"notes";N: A null property named notes.
  • s:11:"preferences";a:2:{s:5:"theme";s:4:"dark";s:8:"language";s:2:"en";}: An array property named preferences with 2 elements: "theme" => "dark" and "language" => "en".

After Reconstruction

<?php
class user {
public $username;
public $salary;
public $isActive;
public $balance;
public $notes;
public $preferences;
}

$obj = new user();
$obj->username = "zyad"; // String
$obj->salary = 20000; // Integer
$obj->isActive = true; // Boolean
$obj->balance = 1234.56; // Float
$obj->notes = null; // Null
$obj->preferences = [ // Array
"theme" => "dark",
"language" => "en"
];

$serializedData = serialize($obj);
echo $serializedData;
?>

Example 2

<?php
class UserProfile {
public $name;
public $age;
public $email;
public $isAdmin;
public $accountBalance;
public $bio;
public $preferences;
public $reference;
private $password; // Transient property
protected $sessionToken; // Protected property
public $resource; // Resource type (unsupported in modern PHP versions)

// Constructor to initialize properties
public function __construct() {
$this->name = "Jane Doe"; // String
$this->age = 28; // Integer
$this->email = "jane.doe@example.com"; // String
$this->isAdmin = true; // Boolean
$this->accountBalance = 1500.75; // Float
$this->bio = null; // Null
$this->preferences = [ // Array
"theme" => "dark",
"language" => "en"
];
$this->reference = &$this->age; // Reference to another property
$this->password = "superSecretPassword"; // Transient property, not serialized
$this->sessionToken = "abc123"; // Protected property
$this->resource = tmpfile(); // Resource type
}

// Destructor to close resource
public function __destruct() {
fclose($this->resource);
}
}

// Create an instance of the UserProfile class
$obj = new UserProfile();

// Serialize the object
$serializedData = serialize($obj);
echo $serializedData;
?>
O:11:"UserProfile":11:{s:4:"name";s:12:"Zyad Elsayed";s:3:"age";i:20;s:5:"email";s:20:"c3rb3rus@example.com";s:7:"isAdmin";b:1;s:14:"accountBalance";d:1500.75;s:3:"bio";N;s:11:"preferences";a:2:{s:5:"theme";s:4:"dark";s:8:"language";s:2:"en";}s:9:"reference";R:3;s:21:"UserProfilepassword";s:19:"superSecretPassword";s:15:"*sessionToken";s:6:"abc123";s:8:"resource";i:0;}
  • O:11:"UserProfile":10: An object (O) of class UserProfile with 11 characters in its name and 10 properties.
  • s:4:"name";s:12:"Zyad Elsayed";: A string property named name with a value of "Zyad Elsayed".
  • s:3:"age";i:20: An integer property named age with a value of 20.
  • s:5:"email";s:20:"c3rb3rus@example.com": A string property named email with a value of "c3rb3rus@example.com".
  • s:7:"isAdmin";b:1: A boolean property named isAdmin with a value of true.
  • s:14:"accountBalance";d:1500.75: A float property named accountBalance with a value of 1500.75.
  • s:3:"bio";N: A null property named bio.
  • s:11:"preferences";a:2:{s:5:"theme";s:4:"dark";s:8:"language";s:2:"en";}: An array property named preferences with 2 elements: "theme" => "dark" and "language" => "en".
  • s:9:"reference";R:2: A reference property named reference that points to the second serialized element (age).
  • s:13:"sessionToken";s:6:"abc123": A protected string property named sessionToken with a value of "abc123".

The transient property password and the resource property resource are not included in the serialized output.

Java Serialization Format

Some programming languages, such as Java, use binary serialization formats. These formats are more challenging to read compared to text-based serialization, but recognizing serialized data is possible if you know the key indicators. For example:

  • Serialized Java objects always start with a consistent sequence of bytes: ac ed in hexadecimal.
  • When encoded in Base64, this sequence appears as rO0.

In Java, any class that implements the java.io.Serializable interface can be serialized and deserialized. If you have access to the source code, look for any use of the readObject() method. This method is critical as it reads and deserialize data from an InputStream.

import java.io.*;

public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public static void main(String[] args) {
// Serialize
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
Person person = new Person("Alice", 30);
oos.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}

// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person person = (Person) ois.readObject();
System.out.println("Name: " + person.name + ", Age: " + person.age);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}

Hexadecimal Representation

The serialized form of this Person object in hexadecimal:

ac ed 00 05 73 72 00 0d 50 65 72 73 6f 6e 03 1b 10 1d 39 d9 19 56 49 02 00 02 4c 00 04 6e 61 6d 65 74 00 12 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 49 00 03 61 67 65 78 70 74 00 05 41 6c 69 63 65 78 70 1e

Base64 Representation

The same serialized object in Base64 encoding:

rO0ABXNyAA1QZXJzb24DGxAdOdkZVkECAAIATAAOYW1ldAASTGphdmEvbGFuZy9TdHJpbmc7SQADYWdleHB0AAFBbGljZXhwHg==

JSON (JavaScript Object Notation)

  • Language: JavaScript (and widely used across many languages)
{
"title": "My Note",
"content": "Welcome to JSON!"
}

XML (Extensible Markup Language)

Language: Used across many languages, often in web services and configuration files

<note>
<title>My Note</title>
<content>Welcome to XML!</content>
</note>

Summary of most used serialization format

╔═════════════════╦══════════════════════════════════════════════╦════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ Language/Format ║ Example Format ║ Example Output ║
╠═════════════════╬══════════════════════════════════════════════╬════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ PHP ║ Key-value pairs in serialized string format ║ a:2:{s:5:"title";s:12:"My Note";s:7:"content";s:15:"Welcome to PHP!";} ║
║ JSON ║ Key-value pairs in curly braces ║ {"title": "My Note", "content": "Welcome to JSON!"} ║
║ XML ║ Tag-based hierarchical structure ║ <note><title>My Note</title><content>Welcome to XML!</content></note> ║
║ YAML ║ Indentation-based key-value pairs ║ title: "My Note"\\ncontent: "Welcome to YAML!" ║
║ Protobuf ║ Field definitions with data types ║ message Note { string title = 1; string content = 2; } ║
║ BSON ║ Binary representation of JSON-like documents ║ {"title": "My Note", "content": "Welcome to BSON!"} ║
║ MessagePack ║ Efficient binary serialization format ║ {"title": "My Note", "content": "Welcome to MessagePack!"} ║
║ Thrift ║ Field definitions with data types ║ struct Note { 1: string title, 2: string content } ║
║ Avro ║ JSON schema for data serialization ║ {"type": "record", "name": "Note", "fields": [{"name": "title", "type": "string"}, {"name": "content", "type": "string"}]} ║
╚═════════════════╩══════════════════════════════════════════════╩════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

References

https://portswigger.net/web-security/deserialization

https://book.hacktricks.xyz/pentesting-web/deserialization

https://johnermac.github.io/notes/ewptx/zattackingserialization/

https://owasp.org/www-project-top-ten/2017/A8_2017-Insecure_Deserialization

https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html

https://www.youtube.com/playlist?list=PLcCG2wDOBXAW_XRxjrym5isS8opH8cr9x

--

--

No responses yet