Insecure deserialisation happens when an application accepts attacker-controlled serialised data and reconstructs it into objects without sufficient trust boundaries or validation. The impact ranges from simple privilege tampering to full object injection and remote code execution, depending on the language, framework, and available gadget chain. These notes cover the core model, common indicators, PHP and Python examples, and the practical mindset for testing it.
Serialisation converts an in-memory object into a storable or transferable representation. Deserialisation reverses that process and rebuilds the object from the supplied data.
That is useful for sessions, cookies, caches, queues, APIs, and persistence layers. It becomes dangerous when untrusted input controls what gets deserialised.
$noteArray = array("title" => "My THM Note", "content" => "Welcome to THM!");
$serialisedNote = serialize($noteArray);
$serialisedNote = file_get_contents('note.txt');
$noteArray = unserialize($serialisedNote);
Most input vulnerabilities influence one query, one template, or one command. Deserialisation bugs are deeper because they can alter program state and object behavior at the application logic level.
Common outcomes include:
PHP commonly uses serialize() and unserialize(). Objects, arrays, strings, integers, and booleans are all encoded into a structured string format.
class Notes {
public $Notescontent;
public function __construct($content) {
$this->Notescontent = $content;
}
}
A serialised PHP object might look like:
O:5:"Notes":1:{s:7:"content";s:14:"Welcome to THM";}
Python often uses the pickle module. Pickled data is binary, so applications frequently wrap it in Base64 before storing or transmitting it.
serialized_data = request.form['serialized_data']
notes_obj = pickle.loads(base64.b64decode(serialized_data))
Important: unsafe deserialisation is not limited to PHP. The pattern appears across Java, Python, .NET, Ruby, and other ecosystems.
In PHP, exploitation often depends on magic methods that are triggered during serialisation or deserialisation.
__sleep(): called before serialisation.__wakeup(): called after deserialisation.__serialize(): custom serialisation handler.__unserialize(): custom restoration handler.If a dangerous method is reachable after unserialize(), the attacker may be able to trigger useful side effects without needing normal application flow.
unserialize(), pickle.loads(), Java deserialisation entry points, or framework-specific equivalents.O:, arrays like a:, or framework state values like __VIEWSTATE.Applications often store serialised state in cookies. If that state is only encoded and not protected by integrity checks, a tester can decode, edit, and re-encode it.
Typical clues include:
__VIEWSTATE in older .NET patterns.A simple and common case is not RCE but privilege tampering. Suppose a PHP app stores user state in a serialised cookie:
O:5:"Notes":3:{s:4:"user";s:5:"guest";s:4:"role";s:5:"guest";s:12:"isSubscribed";b:0;}
If the backend trusts that object after deserialisation, flipping b:0 to b:1 may be enough to bypass subscription checks or unlock premium features.
That is the key testing idea: if the application stores authorization-relevant state client-side in serialised form, you should assume it is tamperable until proven otherwise.
PHP object injection is the more dangerous form of insecure deserialisation. Instead of editing a simple property, the attacker supplies a crafted object that triggers application code during or after deserialisation.
For exploitation, the application generally needs:
unserialize() call,if (isset($_GET['decode'])) {
$base64EncodedData = $_GET['decode'];
$serializedData = base64_decode($base64EncodedData);
$test = unserialize($serializedData);
}
If attacker-controlled input reaches code like this, the next question is not just “can I edit state?” but “what classes and magic methods can I abuse?”
__wakeup(), __destruct(), and related gadget paths.unserialize() and Python pickle.loads() on attacker input.