4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / Scanner_CVE-2025-31324.py PY
#!/usr/bin/env python3
"""
RedRays Scanner for Vulnerability CVE-2025-31324
-----------------------------------------------
A professional security tool to detect and mitigate critical SAP CVE-2025-31324 vulnerability.

Features:
- Detects vulnerability in Visual Composer component (SAP Security Note 3594142)
- Scans for known malicious webshells
- Follows security best practices

CVSS Score: 10.0 (Critical)
"""

import argparse
import logging
import sys
from typing import List, Tuple
from dataclasses import dataclass

import requests
from urllib3.exceptions import InsecureRequestWarning

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger("redray-scanner")

# Suppress only the specific InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# Constants
KNOWN_WEBSHELLS = ["cache.jsp", "helper.jsp", "nzwcnktc.jsp"]
BANNER = r"""
 ____           _ ____                   
|  _ \ ___  __| |  _ \ __ _ _   _ ___   
| |_) / _ \/ _` | |_) / _` | | | / __|  
|  _ <  __/ (_| |  _ < (_| | |_| \__ \  
|_| \_\___|\__,_|_| \_\__,_|\__, |___/  
                           |___/        
CVE-2025-31324 Scanner v1.1.0
"""


@dataclass
class ScanTarget:
    """Data class representing a scan target."""
    hostname: str
    port: int
    use_ssl: bool

    @property
    def base_url(self) -> str:
        """Get the base URL for the target."""
        protocol = "https" if self.use_ssl else "http"
        return f"{protocol}://{self.hostname}:{self.port}"


def check_vulnerability(target: ScanTarget) -> bool:
    """
    Check if the SAP system is vulnerable to CVE-2025-31324.
    
    Args:
        target: ScanTarget object with connection details
        
    Returns:
        bool: True if vulnerable, False otherwise
    """
    url = f"{target.base_url}/developmentserver/metadatauploader"
    vulnerable = False

    try:
        response = requests.head(url, timeout=10, verify=False)
        
        if response.status_code == 200 and 'Set-Cookie' not in response.headers:
            logger.critical(f"SAP System at {url} appears to be vulnerable to CVE-2025-31324")
            vulnerable = True
        elif response.status_code == 404:
            logger.info(f"Visual Composer at {url} appears to not be installed or unavailable")
        else:
            logger.info(f"SAP system at {url} does not appear to be vulnerable to CVE-2025-31324")
    except requests.exceptions.RequestException as e:
        logger.error(f"Connection error at {url}: {e}")
    
    return vulnerable


def detect_webshells(target: ScanTarget) -> List[str]:
    """
    Test for known webshells in the SAP system.
    
    Args:
        target: ScanTarget object with connection details
        
    Returns:
        List[str]: List of detected webshell URLs
    """
    detected_webshells = []
    
    for webshell_filename in KNOWN_WEBSHELLS:
        url = f"{target.base_url}/irj/{webshell_filename}"
        
        try:
            response = requests.get(url, timeout=10, verify=False)
            if response.status_code == 200:
                logger.critical(f"Known webshell found at: {url}")
                detected_webshells.append(url)
        except requests.exceptions.RequestException as e:
            logger.error(f"Error connecting to {url} for webshell testing: {e}")
    
    if not detected_webshells:
        logger.info("No known webshells found")
    
    return detected_webshells


def scan_system(target: ScanTarget) -> Tuple[bool, List[str]]:
    """
    Complete scan of the specified SAP system.
    
    Args:
        target: ScanTarget object with connection details
        
    Returns:
        Tuple[bool, List[str]]: (is_vulnerable, detected_webshells)
    """
    logger.info(f"Scanning SAP system at {target.base_url}")
    
    is_vulnerable = check_vulnerability(target)
    detected_webshells = detect_webshells(target)
    
    return is_vulnerable, detected_webshells


def generate_report(target: ScanTarget, is_vulnerable: bool, detected_webshells: List[str]) -> str:
    """
    Generate a text report of scan findings.
    
    Args:
        target: ScanTarget object with connection details
        is_vulnerable: Whether the system is vulnerable
        detected_webshells: List of detected webshell URLs
        
    Returns:
        str: Formatted report
    """
    report = [
        "=" * 60,
        f"SECURITY SCAN REPORT: {target.hostname}:{target.port}",
        "=" * 60,
        "",
        "VULNERABILITY ASSESSMENT:",
        f"  CVE-2025-31324: {'VULNERABLE' if is_vulnerable else 'NOT VULNERABLE'}",
        "",
        "WEBSHELL DETECTION:",
    ]
    
    if detected_webshells:
        for webshell in detected_webshells:
            report.append(f"  - {webshell}")
    else:
        report.append("  No known webshells detected")
    
    report.extend([
        "",
        "RECOMMENDATIONS:",
        "  1. Apply SAP Security Note 3594142 immediately if vulnerable",
        "  2. Investigate any detected webshells",
        "  3. Consider a full system integrity check",
        "",
        "=" * 60
    ])
    
    return "\n".join(report)


def main():
    """Main function to parse arguments and run vulnerability checks."""
    print(BANNER)
    
    parser = argparse.ArgumentParser(
        description=(
            "RedRays Scanner for Vulnerability CVE-2025-31324 (SAP Security "
            "Note 3594142) - CVSS 10 (Critical)\n\n"
            "This tool checks for the presence of the vulnerability and known "
            "webshells in the SAP system."
        ),
        epilog=(
            "DISCLAIMER: This tool is provided by RedRays as a contribution to "
            "the security and incident response community to aid in response to "
            "active exploitation of CVE-2025-31324. This tool is offered as-is "
            "with no warranty or liability."
        ),
        formatter_class=argparse.RawDescriptionHelpFormatter
    )
    
    parser.add_argument(
        "hostname",
        help="Hostname or IP address of the SAP system"
    )
    parser.add_argument(
        "port",
        type=int,
        help="Port number of the SAP system (e.g., 50000)"
    )
    parser.add_argument(
        "--ssl",
        action="store_true",
        help="Use SSL/TLS for the connection"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Enable verbose output"
    )
    parser.add_argument(
        "--output", "-o",
        help="Save report to specified file"
    )

    args = parser.parse_args()
    
    # Configure logging level
    if args.verbose:
        logger.setLevel(logging.DEBUG)
    
    # Create target object
    target = ScanTarget(
        hostname=args.hostname,
        port=args.port,
        use_ssl=args.ssl
    )
    
    # Run scan
    is_vulnerable, detected_webshells = scan_system(target)
    
    # Generate and display report
    report = generate_report(target, is_vulnerable, detected_webshells)
    print("\n" + report)
    
    # Save report if requested
    if args.output:
        try:
            with open(args.output, 'w') as f:
                f.write(report)
            logger.info(f"Report saved to {args.output}")
        except IOError as e:
            logger.error(f"Failed to save report: {e}")
    
    # Set exit code based on findings
    if is_vulnerable or detected_webshells:
        sys.exit(1)
    else:
        sys.exit(0)


if __name__ == "__main__":
    main()