Insecure Deserialization Part 1: Understanding De|Serialization
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
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 andunserialize
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
- BinaryFormatter
- DataContractSerializer
- NetDataContractSerializer
- 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}");
}
}
}
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}");
}
}
}
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 classuser
with 4 characters in its name and 6 properties.s:8:"username";s:4:"zyad"
: A string property namedusername
with a value of"zyad"
.s:6:"salary";i:20000
: An integer property namedsalary
with a value of20000
.s:8:"isActive";b:1
: A boolean property namedisActive
with a value oftrue
.s:7:"balance";d:1234.56
: A float property namedbalance
with a value of1234.56
.s:5:"notes";N
: A null property namednotes
.s:11:"preferences";a:2:{s:5:"theme";s:4:"dark";s:8:"language";s:2:"en";}
: An array property namedpreferences
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 classUserProfile
with 11 characters in its name and 10 properties.s:4:"name";s:12:"Zyad Elsayed";
: A string property namedname
with a value of"Zyad Elsayed"
.s:3:"age";i:20
: An integer property namedage
with a value of20
.s:5:"email";s:20:"c3rb3rus@example.com"
: A string property namedemail
with a value of"c3rb3rus@example.com"
.s:7:"isAdmin";b:1
: A boolean property namedisAdmin
with a value oftrue
.s:14:"accountBalance";d:1500.75
: A float property namedaccountBalance
with a value of1500.75
.s:3:"bio";N
: A null property namedbio
.s:11:"preferences";a:2:{s:5:"theme";s:4:"dark";s:8:"language";s:2:"en";}
: An array property namedpreferences
with 2 elements:"theme" => "dark"
and"language" => "en"
.s:9:"reference";R:2
: A reference property namedreference
that points to the second serialized element (age).s:13:"sessionToken";s:6:"abc123"
: A protected string property namedsessionToken
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