Rappel théorique

La désérialisation non sécurisée survient lorsqu'une application reconstruit des objets PHP à partir de données fournies par l'utilisateur via unserialize(), sans aucune restriction. PHP peut alors instancier n'importe quelle classe connue de l'application et appeler automatiquement ses méthodes magiques (__wakeup(), __destruct(), __toString()...), même si rien dans ces méthodes n'était censé être déclenchable par un attaquant.

Si une classe quelconque du projet effectue, dans une de ces méthodes magiques, une action sensible à partir d'une propriété de l'objet (écrire un fichier, exécuter une commande, faire une requête...), un attaquant peut fabriquer un objet sérialisé "piégé" pour déclencher cette action - même sans jamais appeler directement cette classe lui-même. On parle de chaîne POP (Property-Oriented Programming).

Exemple de classe vulnérable

class MaliciousPayload {
    public string $cmd;

    public function __wakeup(): void {
        system($this->cmd); // exécuté automatiquement par unserialize() !
    }
}

// Coté attaquant :
$payload = serialize(new MaliciousPayload());
$payload = str_replace('""', '"rm -rf /"', $payload); // injecte sa commande
// Envoyé au serveur, qui fait : unserialize($_POST['data']);

Conséquences

Mesures de protection

Démo vulnérable

unserialize($_POST['data']) sans restriction : un objet MaliciousPayload sérialisé déclenche son __wakeup(), qui écrit un fichier marqueur (à la place d'une vraie commande système).

Accéder à la démo vulnérable

Démo sécurisée

Même formulaire, mais unserialize($data, ['allowed_classes' => false]) : __wakeup() n'est jamais appelé. Alternative recommandée : JSON.

Accéder à la démo sécurisée