3403 lines
159 KiB
Python
3403 lines
159 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import os
|
|
import subprocess
|
|
import argparse
|
|
import sys
|
|
import re
|
|
import socket
|
|
import ipaddress
|
|
import threading
|
|
import time
|
|
import signal
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
from pathlib import Path
|
|
import json
|
|
import csv
|
|
import logging
|
|
|
|
# Global configuration
|
|
MAX_THREADS = 10
|
|
REACHABILITY_THREADS = 100
|
|
COMMON_PORTS = "21,22,23,25,53,80,110,111,135,139,143,443,993,995,1723,3306,3389,5432,5900,8080"
|
|
TCPDUMP_DURATION = 300 # 5 minutes default
|
|
REACHABILITY_TIMEOUT = 3 # seconds for each connectivity test
|
|
REACHABILITY_PORTS = [22, 23, 25, 53, 80, 135, 139, 443, 445, 993, 995, 3389, 5985, 5986, 8080, 8443]
|
|
|
|
# Global logging variables
|
|
csv_logger = None
|
|
verbose_logger = None
|
|
|
|
class Colors:
|
|
HEADER = '\033[95m'
|
|
OKBLUE = '\033[94m'
|
|
OKCYAN = '\033[96m'
|
|
OKGREEN = '\033[92m'
|
|
WARNING = '\033[93m'
|
|
FAIL = '\033[91m'
|
|
ENDC = '\033[0m'
|
|
BOLD = '\033[1m'
|
|
|
|
def setup_logging(base_dir):
|
|
"""Setup comprehensive logging for TrashPanda operations."""
|
|
global csv_logger, verbose_logger
|
|
|
|
logs_dir = os.path.join(base_dir, "logs")
|
|
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
|
|
|
# Setup CSV command logging
|
|
csv_file = os.path.join(logs_dir, f"trashpanda_commands_{timestamp}.csv")
|
|
csv_fieldnames = ['start_time', 'end_time', 'hostname', 'command', 'exit_code', 'duration_seconds']
|
|
|
|
with open(csv_file, 'w', newline='') as f:
|
|
writer = csv.DictWriter(f, fieldnames=csv_fieldnames)
|
|
writer.writeheader()
|
|
|
|
# Setup verbose console logging
|
|
verbose_file = os.path.join(logs_dir, f"trashpanda_verbose_{timestamp}.log")
|
|
verbose_logger = logging.getLogger('trashpanda_verbose')
|
|
verbose_logger.setLevel(logging.DEBUG)
|
|
|
|
# Create file handler for verbose log
|
|
file_handler = logging.FileHandler(verbose_file)
|
|
file_handler.setLevel(logging.DEBUG)
|
|
|
|
# Create console handler that captures all output
|
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
console_handler.setLevel(logging.DEBUG)
|
|
|
|
# Create formatter
|
|
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
|
file_handler.setFormatter(formatter)
|
|
|
|
verbose_logger.addHandler(file_handler)
|
|
verbose_logger.propagate = False # Prevent duplicate console output
|
|
|
|
print(f"{Colors.OKGREEN}[+] Logging initialized:{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] CSV commands log: {csv_file}{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Verbose log: {verbose_file}{Colors.ENDC}")
|
|
|
|
return csv_file, verbose_file
|
|
|
|
def log_command(command, start_time=None, end_time=None, exit_code=None, hostname=None):
|
|
"""Log command execution to CSV file."""
|
|
global csv_logger
|
|
|
|
if not hasattr(log_command, 'csv_file'):
|
|
return # Logging not initialized
|
|
|
|
try:
|
|
duration = (end_time - start_time) if start_time and end_time else None
|
|
|
|
with open(log_command.csv_file, 'a', newline='') as f:
|
|
writer = csv.DictWriter(f, fieldnames=['start_time', 'end_time', 'hostname', 'command', 'exit_code', 'duration_seconds'])
|
|
writer.writerow({
|
|
'start_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time)) if start_time else '',
|
|
'end_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(end_time)) if end_time else '',
|
|
'hostname': hostname or socket.gethostname(),
|
|
'command': command,
|
|
'exit_code': exit_code,
|
|
'duration_seconds': f"{duration:.2f}" if duration else ''
|
|
})
|
|
except Exception as e:
|
|
print(f"{Colors.WARNING}[!] Logging error: {e}{Colors.ENDC}")
|
|
|
|
def log_verbose(message, level='INFO'):
|
|
"""Log message to verbose log file."""
|
|
global verbose_logger
|
|
|
|
if verbose_logger:
|
|
if level == 'DEBUG':
|
|
verbose_logger.debug(message)
|
|
elif level == 'WARNING':
|
|
verbose_logger.warning(message)
|
|
elif level == 'ERROR':
|
|
verbose_logger.error(message)
|
|
else:
|
|
verbose_logger.info(message)
|
|
|
|
class LoggingPrint:
|
|
"""Wrapper to capture and log all print statements."""
|
|
def __init__(self, original_stdout):
|
|
self.original_stdout = original_stdout
|
|
|
|
def write(self, message):
|
|
# Write to original stdout
|
|
self.original_stdout.write(message)
|
|
# Log to verbose log (strip ANSI colors for log file)
|
|
if message.strip():
|
|
clean_message = re.sub(r'\033\[[0-9;]*m', '', message.strip())
|
|
log_verbose(clean_message)
|
|
|
|
def flush(self):
|
|
self.original_stdout.flush()
|
|
|
|
def is_public_ip(ip_str):
|
|
"""Check if an IP address is public (not private/reserved)."""
|
|
try:
|
|
ip = ipaddress.ip_address(ip_str)
|
|
return not (ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_multicast or ip.is_reserved)
|
|
except ValueError:
|
|
return False
|
|
|
|
def filter_public_ips_from_targets(targets):
|
|
"""Filter out public IPs from target list and warn user."""
|
|
filtered_targets = []
|
|
public_ips = []
|
|
|
|
for target in targets:
|
|
# Handle CIDR ranges
|
|
if '/' in target:
|
|
try:
|
|
network = ipaddress.ip_network(target, strict=False)
|
|
if any(is_public_ip(str(ip)) for ip in list(network)[:5]): # Check first 5 IPs
|
|
public_ips.append(target)
|
|
print(f"{Colors.WARNING}[!] Skipping public network range: {target}{Colors.ENDC}")
|
|
else:
|
|
filtered_targets.append(target)
|
|
except ValueError:
|
|
filtered_targets.append(target) # Keep if not valid CIDR
|
|
# Handle IP ranges
|
|
elif '-' in target and not target.count('.') > 3:
|
|
try:
|
|
base_ip = target.split('-')[0]
|
|
if is_public_ip(base_ip):
|
|
public_ips.append(target)
|
|
print(f"{Colors.WARNING}[!] Skipping public IP range: {target}{Colors.ENDC}")
|
|
else:
|
|
filtered_targets.append(target)
|
|
except:
|
|
filtered_targets.append(target) # Keep if parsing fails
|
|
# Handle single IPs
|
|
else:
|
|
try:
|
|
# Try to parse as IP first
|
|
ip = ipaddress.ip_address(target)
|
|
if is_public_ip(str(ip)):
|
|
public_ips.append(target)
|
|
print(f"{Colors.WARNING}[!] Skipping public IP: {target}{Colors.ENDC}")
|
|
else:
|
|
filtered_targets.append(target)
|
|
except ValueError:
|
|
# Not an IP, probably hostname - keep it
|
|
filtered_targets.append(target)
|
|
|
|
if public_ips:
|
|
print(f"{Colors.WARNING}[!] Filtered out {len(public_ips)} public IP targets for safety{Colors.ENDC}")
|
|
response = input(f"{Colors.WARNING}Continue with remaining {len(filtered_targets)} targets? [y/N]: {Colors.ENDC}")
|
|
if response.lower() != 'y':
|
|
print(f"{Colors.FAIL}[!] Scan aborted by user{Colors.ENDC}")
|
|
sys.exit(0)
|
|
|
|
return filtered_targets
|
|
|
|
def print_banner():
|
|
banner = f"""
|
|
{Colors.HEADER}{Colors.BOLD}
|
|
████████╗██████╗ █████╗ ███████╗██╗ ██╗██████╗ █████╗ ███╗ ██╗██████╗ █████╗
|
|
╚══██╔══╝██╔══██╗██╔══██╗██╔════╝██║ ██║██╔══██╗██╔══██╗████╗ ██║██╔══██╗██╔══██╗
|
|
██║ ██████╔╝███████║███████╗███████║██████╔╝███████║██╔██╗ ██║██║ ██║███████║
|
|
██║ ██╔══██╗██╔══██║╚════██║██╔══██║██╔═══╝ ██╔══██║██║╚██╗██║██║ ██║██╔══██║
|
|
██║ ██║ ██║██║ ██║███████║██║ ██║██║ ██║ ██║██║ ╚████║██████╔╝██║ ██║
|
|
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═╝
|
|
|
|
🦝 TrashPanda - Network Enumeration Tool v2.4 🦝
|
|
Professional Penetration Testing Framework
|
|
{Colors.ENDC}
|
|
"""
|
|
print(banner)
|
|
|
|
def create_pentest_structure(base_name="/root/operator"):
|
|
"""Create a comprehensive penetration testing directory structure."""
|
|
|
|
# Main engagement directory
|
|
base_dir = os.path.abspath(base_name)
|
|
|
|
# Primary directories
|
|
main_dirs = {
|
|
"tools": "Downloaded/compiled tools and scripts",
|
|
"scans": "All scan results organized by type",
|
|
"logs": "Execution logs and debug output",
|
|
"loot": "Extracted credentials, hashes, and sensitive data",
|
|
"payloads": "Custom payloads and exploit code",
|
|
"targets": "Target lists and reconnaissance data",
|
|
"screenshots": "Visual evidence and GUI captures",
|
|
"reports": "Draft reports and documentation",
|
|
"notes": "Manual notes and observations",
|
|
"exploits": "Working exploits and proof-of-concepts",
|
|
"wordlists": "Custom and downloaded wordlists",
|
|
"pcaps": "Network captures and traffic analysis"
|
|
}
|
|
|
|
# Scan subdirectories
|
|
scan_subdirs = {
|
|
"nmap": "Network discovery and port scanning",
|
|
"dns": "DNS enumeration and zone transfers",
|
|
"snmp": "SNMP enumeration and community strings",
|
|
"smb": "SMB/NetBIOS enumeration and shares",
|
|
"web": "Web application scanning and enumeration",
|
|
"ssl": "SSL/TLS certificate and cipher analysis",
|
|
"vulns": "Vulnerability scanning and NSE scripts",
|
|
"ldap": "LDAP enumeration and directory services",
|
|
"ftp": "FTP enumeration and anonymous access",
|
|
"ssh": "SSH enumeration and key analysis",
|
|
"databases": "Database enumeration (MySQL, MSSQL, etc)",
|
|
"custom": "Custom and manual scans",
|
|
"reachability": "Network reachability test results"
|
|
}
|
|
|
|
# Loot subdirectories
|
|
loot_subdirs = {
|
|
"credentials": "Usernames, passwords, and authentication data",
|
|
"hashes": "Password hashes and cracking results",
|
|
"keys": "SSH keys, certificates, and crypto material",
|
|
"configs": "Configuration files and sensitive data",
|
|
"databases": "Extracted database contents",
|
|
"files": "Interesting files and documents"
|
|
}
|
|
|
|
print(f"{Colors.OKGREEN}[+] Creating penetration testing structure: {base_dir}{Colors.ENDC}")
|
|
|
|
# Create main directories
|
|
for dir_name, description in main_dirs.items():
|
|
dir_path = os.path.join(base_dir, dir_name)
|
|
Path(dir_path).mkdir(parents=True, exist_ok=True)
|
|
|
|
# Create README files for documentation
|
|
readme_path = os.path.join(dir_path, "README.md")
|
|
if not os.path.exists(readme_path):
|
|
with open(readme_path, 'w') as f:
|
|
f.write(f"# {dir_name.upper()}\n\n")
|
|
f.write(f"{description}\n\n")
|
|
f.write(f"Created by TrashPanda on {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
|
|
# Create scan subdirectories
|
|
scans_dir = os.path.join(base_dir, "scans")
|
|
for subdir, description in scan_subdirs.items():
|
|
subdir_path = os.path.join(scans_dir, subdir)
|
|
Path(subdir_path).mkdir(parents=True, exist_ok=True)
|
|
|
|
readme_path = os.path.join(subdir_path, "README.md")
|
|
if not os.path.exists(readme_path):
|
|
with open(readme_path, 'w') as f:
|
|
f.write(f"# {subdir.upper()} SCANS\n\n")
|
|
f.write(f"{description}\n\n")
|
|
|
|
# Create loot subdirectories
|
|
loot_dir = os.path.join(base_dir, "loot")
|
|
for subdir, description in loot_subdirs.items():
|
|
subdir_path = os.path.join(loot_dir, subdir)
|
|
Path(subdir_path).mkdir(parents=True, exist_ok=True)
|
|
|
|
readme_path = os.path.join(subdir_path, "README.md")
|
|
if not os.path.exists(readme_path):
|
|
with open(readme_path, 'w') as f:
|
|
f.write(f"# {subdir.upper()}\n\n")
|
|
f.write(f"{description}\n\n")
|
|
|
|
# Create engagement log
|
|
engagement_log = os.path.join(base_dir, "logs", "engagement.log")
|
|
with open(engagement_log, 'w') as f:
|
|
f.write(f"TrashPanda Engagement Log\n")
|
|
f.write(f"========================\n")
|
|
f.write(f"Started: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"Operator: operator\n")
|
|
f.write(f"Tool: TrashPanda v2.4\n\n")
|
|
|
|
# Create initial target file
|
|
target_template = os.path.join(base_dir, "targets", "targets.txt")
|
|
if not os.path.exists(target_template):
|
|
with open(target_template, 'w') as f:
|
|
f.write("# Target List\n")
|
|
f.write("# Add IPs, ranges, or hostnames (one per line)\n")
|
|
f.write("# Examples:\n")
|
|
f.write("# 192.168.1.1\n")
|
|
f.write("# 192.168.1.0/24\n")
|
|
f.write("# 192.168.1.1-50\n")
|
|
f.write("# target.domain.com\n\n")
|
|
|
|
# Create manual commands file
|
|
manual_commands = os.path.join(base_dir, "scans", "_manual_commands.txt")
|
|
with open(manual_commands, 'w') as f:
|
|
f.write("# Manual Commands for Further Enumeration\n")
|
|
f.write("# ======================================\n")
|
|
f.write(f"# Generated by TrashPanda on {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
|
|
|
print(f"{Colors.OKGREEN}[+] Penetration testing structure created successfully{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Add targets to: {target_template}{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Engagement log: {engagement_log}{Colors.ENDC}")
|
|
|
|
return base_dir
|
|
|
|
def start_tcpdump(base_dir, duration=TCPDUMP_DURATION, interface="any"):
|
|
"""Start tcpdump for network capture, but only if not already running."""
|
|
pcap_dir = os.path.join(base_dir, "pcaps")
|
|
|
|
# Check if capture files already exist
|
|
try:
|
|
import glob
|
|
existing_captures = glob.glob(os.path.join(pcap_dir, "capture_*.pcap"))
|
|
if existing_captures:
|
|
print(f"{Colors.WARNING}[!] Found {len(existing_captures)} existing capture file(s):{Colors.ENDC}")
|
|
for capture in existing_captures[-3:]: # Show last 3 files
|
|
file_size = os.path.getsize(capture) / (1024*1024) # MB
|
|
mod_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(os.path.getmtime(capture)))
|
|
print(f"{Colors.WARNING} - {os.path.basename(capture)} ({file_size:.1f}MB, {mod_time}){Colors.ENDC}")
|
|
if len(existing_captures) > 3:
|
|
print(f"{Colors.WARNING} ... and {len(existing_captures)-3} more{Colors.ENDC}")
|
|
print(f"{Colors.WARNING}[!] Skipping new capture to avoid overwriting existing data{Colors.ENDC}")
|
|
return None
|
|
except Exception as e:
|
|
print(f"{Colors.WARNING}[!] Error checking for existing captures: {e}{Colors.ENDC}")
|
|
pass # Continue with capture if check fails
|
|
|
|
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
|
pcap_file = os.path.join(pcap_dir, f"capture_{timestamp}.pcap")
|
|
|
|
print(f"{Colors.OKBLUE}[*] Starting tcpdump capture for {duration} seconds...{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Capture file: {pcap_file}{Colors.ENDC}")
|
|
|
|
# Build tcpdump command
|
|
tcpdump_cmd = [
|
|
"sudo", "tcpdump",
|
|
"-i", interface,
|
|
"-U", # Unbuffered output
|
|
"-w", pcap_file,
|
|
"-s", "65535", # Capture full packets
|
|
"not", "port", "22" # Exclude SSH traffic to reduce noise
|
|
]
|
|
|
|
try:
|
|
# Start tcpdump process
|
|
tcpdump_process = subprocess.Popen(
|
|
tcpdump_cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True
|
|
)
|
|
|
|
# Log the process
|
|
log_file = os.path.join(base_dir, "logs", "tcpdump.log")
|
|
with open(log_file, 'a') as f:
|
|
f.write(f"TCPDump started at {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"Command: {' '.join(tcpdump_cmd)}\n")
|
|
f.write(f"PID: {tcpdump_process.pid}\n")
|
|
f.write(f"Duration: {duration} seconds\n")
|
|
f.write(f"Output: {pcap_file}\n\n")
|
|
|
|
# Return process info for later termination
|
|
return {
|
|
'process': tcpdump_process,
|
|
'start_time': time.time(),
|
|
'duration': duration,
|
|
'pcap_file': pcap_file,
|
|
'log_file': log_file
|
|
}
|
|
|
|
except Exception as e:
|
|
print(f"{Colors.FAIL}[!] Failed to start tcpdump: {e}{Colors.ENDC}")
|
|
print(f"{Colors.WARNING}[!] Make sure you have sudo privileges{Colors.ENDC}")
|
|
return None
|
|
|
|
def stop_tcpdump(tcpdump_info):
|
|
"""Stop tcpdump and log results."""
|
|
if not tcpdump_info:
|
|
return
|
|
|
|
try:
|
|
process = tcpdump_info['process']
|
|
|
|
# Terminate gracefully
|
|
process.terminate()
|
|
|
|
# Wait for termination with timeout
|
|
try:
|
|
process.wait(timeout=5)
|
|
except subprocess.TimeoutExpired:
|
|
print(f"{Colors.WARNING}[!] TCPDump didn't terminate gracefully, killing...{Colors.ENDC}")
|
|
process.kill()
|
|
process.wait()
|
|
|
|
# Log completion
|
|
end_time = time.time()
|
|
actual_duration = end_time - tcpdump_info['start_time']
|
|
|
|
with open(tcpdump_info['log_file'], 'a') as f:
|
|
f.write(f"TCPDump stopped at {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"Actual duration: {actual_duration:.2f} seconds\n")
|
|
f.write(f"Exit code: {process.returncode}\n")
|
|
|
|
# Check file size
|
|
pcap_file = tcpdump_info['pcap_file']
|
|
if os.path.exists(pcap_file):
|
|
file_size = os.path.getsize(pcap_file)
|
|
print(f"{Colors.OKGREEN}[+] TCPDump capture completed{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] Capture file: {pcap_file} ({file_size:,} bytes){Colors.ENDC}")
|
|
else:
|
|
print(f"{Colors.WARNING}[!] TCPDump capture file not found{Colors.ENDC}")
|
|
|
|
except Exception as e:
|
|
print(f"{Colors.FAIL}[!] Error stopping tcpdump: {e}{Colors.ENDC}")
|
|
|
|
def run_command(command, output_file=None, debug=False, stealth=False):
|
|
"""Run a command with comprehensive logging."""
|
|
start_time = time.time()
|
|
hostname = socket.gethostname()
|
|
|
|
if debug:
|
|
print(f"{Colors.OKCYAN}[DEBUG] Running: {command}{Colors.ENDC}")
|
|
|
|
log_verbose(f"COMMAND START: {command}", 'INFO')
|
|
|
|
try:
|
|
# Increase timeout for stealth mode (slower scans)
|
|
timeout = 7200 if stealth else 3600
|
|
result = subprocess.run(command, shell=True, text=True, capture_output=True, timeout=timeout)
|
|
|
|
end_time = time.time()
|
|
duration = end_time - start_time
|
|
|
|
# Log command to CSV
|
|
log_command(command, start_time, end_time, result.returncode, hostname)
|
|
|
|
# Log to verbose log
|
|
log_verbose(f"COMMAND END: {command} (exit_code: {result.returncode}, duration: {duration:.2f}s)", 'INFO')
|
|
|
|
if result.stdout:
|
|
log_verbose(f"STDOUT: {result.stdout[:1000]}{'...' if len(result.stdout) > 1000 else ''}", 'DEBUG')
|
|
if result.stderr:
|
|
log_verbose(f"STDERR: {result.stderr[:1000]}{'...' if len(result.stderr) > 1000 else ''}", 'WARNING')
|
|
|
|
if output_file:
|
|
with open(output_file, 'w') as f:
|
|
f.write(f"Command: {command}\n")
|
|
f.write(f"Timestamp: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time))}\n")
|
|
f.write(f"Duration: {duration:.2f} seconds\n")
|
|
f.write(f"Return Code: {result.returncode}\n")
|
|
f.write(f"STDOUT:\n{result.stdout}\n")
|
|
f.write(f"STDERR:\n{result.stderr}\n")
|
|
|
|
if debug:
|
|
print(f"{Colors.OKCYAN}[DEBUG] Return code: {result.returncode}, Duration: {duration:.2f}s{Colors.ENDC}")
|
|
if result.stdout:
|
|
print(f"{Colors.OKCYAN}[DEBUG] STDOUT: {result.stdout[:500]}...{Colors.ENDC}")
|
|
|
|
return result
|
|
except subprocess.TimeoutExpired:
|
|
end_time = time.time()
|
|
error_msg = f"Command timed out: {command}"
|
|
print(f"{Colors.WARNING}[!] {error_msg}{Colors.ENDC}")
|
|
log_command(command, start_time, end_time, -1, hostname) # -1 for timeout
|
|
log_verbose(f"TIMEOUT: {error_msg}", 'ERROR')
|
|
return None
|
|
except Exception as e:
|
|
end_time = time.time()
|
|
error_msg = f"Error running command: {e}"
|
|
print(f"{Colors.FAIL}[!] {error_msg}{Colors.ENDC}")
|
|
log_command(command, start_time, end_time, -2, hostname) # -2 for error
|
|
log_verbose(f"ERROR: {error_msg}", 'ERROR')
|
|
return None
|
|
|
|
def add_manual_command(base_dir, service_name, commands):
|
|
"""Add manual commands to the manual commands file."""
|
|
manual_file = os.path.join(base_dir, "scans", "_manual_commands.txt")
|
|
|
|
with open(manual_file, 'a') as f:
|
|
f.write(f"\n[*] {service_name}\n")
|
|
f.write("=" * (len(service_name) + 4) + "\n\n")
|
|
|
|
if isinstance(commands, str):
|
|
commands = [commands]
|
|
|
|
for cmd in commands:
|
|
f.write(f" {cmd}\n")
|
|
f.write("\n")
|
|
|
|
def parse_targets(target_input):
|
|
"""Parse various target formats (IPs, ranges, CIDRs, hostnames)."""
|
|
targets = []
|
|
|
|
if os.path.isfile(target_input):
|
|
with open(target_input, 'r') as f:
|
|
lines = f.read().splitlines()
|
|
else:
|
|
lines = [target_input]
|
|
|
|
for line in lines:
|
|
line = line.strip()
|
|
if not line or line.startswith('#'):
|
|
continue
|
|
|
|
try:
|
|
# Check if it's a CIDR range
|
|
if '/' in line:
|
|
network = ipaddress.ip_network(line, strict=False)
|
|
targets.extend([str(ip) for ip in network.hosts()])
|
|
# Check if it's an IP range (e.g., 192.168.1.1-50)
|
|
elif '-' in line and not line.count('-') > 1:
|
|
ip_parts = line.split('-')
|
|
if len(ip_parts) == 2:
|
|
base_ip = ip_parts[0]
|
|
end_range = ip_parts[1]
|
|
|
|
# Handle cases like 192.168.1.1-50
|
|
if '.' in base_ip and '.' not in end_range:
|
|
base_parts = base_ip.split('.')
|
|
start_num = int(base_parts[3])
|
|
end_num = int(end_range)
|
|
for i in range(start_num, end_num + 1):
|
|
targets.append(f"{'.'.join(base_parts[:3])}.{i}")
|
|
else:
|
|
targets.append(line) # Add as-is if format not recognized
|
|
else:
|
|
# Single IP or hostname
|
|
targets.append(line)
|
|
except Exception as e:
|
|
print(f"{Colors.WARNING}[!] Error parsing target {line}: {e}{Colors.ENDC}")
|
|
targets.append(line) # Add as-is and let tools handle it
|
|
|
|
return list(set(targets)) # Remove duplicates
|
|
|
|
def classify_network_ranges(targets):
|
|
"""Intelligently classify and group IP targets into appropriate network ranges for scanning."""
|
|
rfc1918_networks = {
|
|
'class_a': set(), # 10.0.0.0/8
|
|
'class_b': set(), # 172.16.0.0/12
|
|
'class_c': set(), # 192.168.0.0/16
|
|
}
|
|
|
|
non_rfc1918_ips = []
|
|
hostnames = []
|
|
|
|
for target in targets:
|
|
try:
|
|
ip_obj = ipaddress.ip_address(target)
|
|
|
|
if ip_obj.is_private:
|
|
ip_str = str(ip_obj)
|
|
|
|
# Class A: 10.0.0.0/8
|
|
if ip_str.startswith('10.'):
|
|
octets = ip_str.split('.')
|
|
# Group by /16 networks within Class A
|
|
network_prefix = f"{octets[0]}.{octets[1]}"
|
|
rfc1918_networks['class_a'].add(f"{network_prefix}.0.0/16")
|
|
|
|
# Class B: 172.16.0.0/12 (172.16.0.0 to 172.31.255.255)
|
|
elif ip_str.startswith('172.'):
|
|
octets = ip_str.split('.')
|
|
second_octet = int(octets[1])
|
|
if 16 <= second_octet <= 31:
|
|
network_prefix = f"{octets[0]}.{octets[1]}"
|
|
rfc1918_networks['class_b'].add(f"{network_prefix}.0.0/16")
|
|
|
|
# Class C: 192.168.0.0/16
|
|
elif ip_str.startswith('192.168.'):
|
|
octets = ip_str.split('.')
|
|
network_prefix = f"{octets[0]}.{octets[1]}.{octets[2]}"
|
|
rfc1918_networks['class_c'].add(f"{network_prefix}.0/24")
|
|
else:
|
|
non_rfc1918_ips.append(target)
|
|
|
|
except ValueError:
|
|
# Not an IP address, likely a hostname
|
|
hostnames.append(target)
|
|
|
|
return rfc1918_networks, non_rfc1918_ips, hostnames
|
|
|
|
def discover_services_from_nmap(base_dir):
|
|
"""Parse nmap results to discover services for enhanced enumeration."""
|
|
services = {}
|
|
nmap_dir = os.path.join(base_dir, "scans", "nmap")
|
|
|
|
# Parse nmap gnmap files for services
|
|
for nmap_file in Path(nmap_dir).glob("*.gnmap"):
|
|
try:
|
|
with open(nmap_file, 'r') as f:
|
|
for line in f:
|
|
if "open" in line:
|
|
parts = line.split()
|
|
if len(parts) > 1:
|
|
ip = parts[1]
|
|
if ip not in services:
|
|
services[ip] = []
|
|
|
|
# Extract port info
|
|
port_info = [p for p in parts if "open" in p]
|
|
for port_data in port_info:
|
|
port_match = re.search(r'(\d+)/(tcp|udp)', port_data)
|
|
service_match = re.search(r'//(.+?)/', port_data)
|
|
|
|
if port_match:
|
|
port = port_match.group(1)
|
|
protocol = port_match.group(2)
|
|
service = service_match.group(1) if service_match else "unknown"
|
|
|
|
service_info = {
|
|
'port': port,
|
|
'protocol': protocol,
|
|
'service': service,
|
|
'ssl': 'ssl' in port_data or 'https' in port_data
|
|
}
|
|
|
|
if service_info not in services[ip]:
|
|
services[ip].append(service_info)
|
|
except Exception as e:
|
|
print(f"{Colors.WARNING}[!] Error parsing {nmap_file}: {e}{Colors.ENDC}")
|
|
|
|
return services
|
|
|
|
# Network Reachability Testing Functions
|
|
def test_icmp_connectivity(target, timeout=REACHABILITY_TIMEOUT):
|
|
"""Test ICMP connectivity using ping."""
|
|
try:
|
|
if sys.platform.startswith('win'):
|
|
result = subprocess.run(['ping', '-n', '1', '-w', str(timeout*1000), target],
|
|
capture_output=True, text=True, timeout=timeout+2)
|
|
else:
|
|
result = subprocess.run(['ping', '-c', '1', '-W', str(timeout), target],
|
|
capture_output=True, text=True, timeout=timeout+2)
|
|
return result.returncode == 0
|
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
|
|
return False
|
|
|
|
def test_dns_connectivity(dns_server, timeout=3):
|
|
"""Test DNS server connectivity with actual DNS query."""
|
|
try:
|
|
# Test UDP DNS first with a real DNS query
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
sock.settimeout(timeout)
|
|
|
|
# DNS query for google.com (more realistic than generic UDP test)
|
|
dns_query = b'\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01'
|
|
sock.sendto(dns_query, (dns_server, 53))
|
|
|
|
# Wait for response
|
|
response, addr = sock.recvfrom(1024)
|
|
sock.close()
|
|
|
|
# Check if we got a valid DNS response
|
|
if len(response) > 12: # Minimum DNS response size
|
|
return True
|
|
|
|
except (socket.error, socket.timeout):
|
|
pass
|
|
|
|
# Fallback: test TCP connectivity to port 53
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(timeout)
|
|
result = sock.connect_ex((dns_server, 53))
|
|
sock.close()
|
|
return result == 0
|
|
except (socket.error, socket.timeout):
|
|
return False
|
|
|
|
def test_tcp_connectivity(target, port, timeout=REACHABILITY_TIMEOUT):
|
|
"""Test TCP connectivity to specific port."""
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(timeout)
|
|
result = sock.connect_ex((target, port))
|
|
sock.close()
|
|
return result == 0
|
|
except (socket.error, socket.timeout):
|
|
return False
|
|
|
|
def test_udp_connectivity(target, port=53, timeout=REACHABILITY_TIMEOUT):
|
|
"""Test UDP connectivity (primarily DNS)."""
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
sock.settimeout(timeout)
|
|
# Send a simple DNS query for connectivity test
|
|
if port == 53:
|
|
# Simple DNS query packet for google.com
|
|
dns_query = b'\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01'
|
|
sock.sendto(dns_query, (target, port))
|
|
data, addr = sock.recvfrom(1024)
|
|
sock.close()
|
|
return True
|
|
else:
|
|
# For other UDP ports, just try to send a packet
|
|
sock.sendto(b'test', (target, port))
|
|
sock.close()
|
|
return True
|
|
except (socket.error, socket.timeout):
|
|
return False
|
|
|
|
def test_comprehensive_connectivity(target, debug=False):
|
|
"""Run comprehensive connectivity tests for a single target."""
|
|
results = {
|
|
'target': target,
|
|
'icmp': False,
|
|
'tcp_ports': {},
|
|
'udp_dns': False,
|
|
'reachable': False,
|
|
'response_time': 0,
|
|
'best_ports': []
|
|
}
|
|
|
|
start_time = time.time()
|
|
|
|
# Test ICMP first
|
|
if debug:
|
|
print(f"{Colors.OKCYAN}[DEBUG] Testing ICMP to {target}{Colors.ENDC}")
|
|
|
|
results['icmp'] = test_icmp_connectivity(target)
|
|
|
|
# Test common TCP ports
|
|
tcp_results = {}
|
|
for port in REACHABILITY_PORTS:
|
|
if debug:
|
|
print(f"{Colors.OKCYAN}[DEBUG] Testing TCP {target}:{port}{Colors.ENDC}")
|
|
tcp_results[port] = test_tcp_connectivity(target, port)
|
|
if tcp_results[port]:
|
|
results['best_ports'].append(port)
|
|
|
|
results['tcp_ports'] = tcp_results
|
|
|
|
# Test UDP DNS
|
|
if debug:
|
|
print(f"{Colors.OKCYAN}[DEBUG] Testing UDP DNS to {target}{Colors.ENDC}")
|
|
results['udp_dns'] = test_udp_connectivity(target, 53)
|
|
|
|
# Determine overall reachability
|
|
results['reachable'] = (results['icmp'] or
|
|
any(tcp_results.values()) or
|
|
results['udp_dns'])
|
|
|
|
results['response_time'] = round(time.time() - start_time, 2)
|
|
|
|
return results
|
|
|
|
def analyze_network_infrastructure(targets, debug=False):
|
|
"""Analyze targets and identify key network infrastructure to test."""
|
|
infrastructure = {
|
|
'subnets': {}, # Changed from 'enclaves' to 'subnets'
|
|
'individual_hosts': [],
|
|
'dns_servers': set(),
|
|
'analysis_summary': {},
|
|
'total_original_targets': 0 # Track original scope
|
|
}
|
|
|
|
print(f"{Colors.OKGREEN}[+] Analyzing Target Infrastructure{Colors.ENDC}")
|
|
|
|
# Count original targets for reduction metrics
|
|
total_original = 0
|
|
for target in targets:
|
|
try:
|
|
if '/' in target:
|
|
network = ipaddress.ip_network(target, strict=False)
|
|
total_original += network.num_addresses - 2 # Exclude network and broadcast
|
|
else:
|
|
total_original += 1
|
|
except ValueError:
|
|
total_original += 1
|
|
|
|
infrastructure['total_original_targets'] = total_original
|
|
print(f"{Colors.OKBLUE}[*] Original scope: ~{total_original:,} potential targets{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Performing intelligent analysis to avoid brute force scanning...{Colors.ENDC}")
|
|
|
|
subnet_count = 0
|
|
|
|
for target in targets:
|
|
try:
|
|
# Try to parse as IP address
|
|
ip_obj = ipaddress.ip_address(target)
|
|
|
|
# Determine which subnet/network this belongs to
|
|
if ip_obj.is_private:
|
|
octets = str(ip_obj).split('.')
|
|
|
|
# Group by /24 networks for now (can be adjusted)
|
|
if octets[0] == '10':
|
|
# For 10.x networks, group by /16
|
|
subnet_key = f"{octets[0]}.{octets[1]}.0.0/16"
|
|
elif octets[0] == '172' and 16 <= int(octets[1]) <= 31:
|
|
# For 172.16-31 networks, group by /16
|
|
subnet_key = f"{octets[0]}.{octets[1]}.0.0/16"
|
|
elif octets[0] == '192' and octets[1] == '168':
|
|
# For 192.168 networks, group by /24
|
|
subnet_key = f"{octets[0]}.{octets[1]}.{octets[2]}.0/24"
|
|
else:
|
|
# Other private ranges, group by /24
|
|
subnet_key = f"{octets[0]}.{octets[1]}.{octets[2]}.0/24"
|
|
else:
|
|
# Public IP - each gets its own "subnet"
|
|
subnet_key = f"public_{str(ip_obj)}"
|
|
|
|
# Initialize subnet if not seen before
|
|
if subnet_key not in infrastructure['subnets']:
|
|
subnet_count += 1
|
|
infrastructure['subnets'][subnet_key] = {
|
|
'network': subnet_key,
|
|
'targets': [],
|
|
'sample_targets': [],
|
|
'key_infrastructure': [],
|
|
'subnet_id': subnet_count
|
|
}
|
|
|
|
# Add key infrastructure for this subnet
|
|
try:
|
|
network = ipaddress.ip_network(subnet_key, strict=False)
|
|
if network.is_private and network.num_addresses > 2:
|
|
# Add potential gateways and key servers
|
|
base_ip = str(network.network_address).split('.')
|
|
key_ips = [
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.1", # Common gateway
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.254", # Alt gateway
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.10", # Common server IP
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.53", # DNS server
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.100", # Common server range
|
|
]
|
|
|
|
# Only add IPs that are actually in the network
|
|
for key_ip in key_ips:
|
|
try:
|
|
if ipaddress.ip_address(key_ip) in network:
|
|
infrastructure['subnets'][subnet_key]['key_infrastructure'].append(key_ip)
|
|
except ValueError:
|
|
pass
|
|
except ValueError:
|
|
pass
|
|
|
|
# Add target to subnet
|
|
infrastructure['subnets'][subnet_key]['targets'].append(str(ip_obj))
|
|
|
|
except ValueError:
|
|
# Handle CIDR ranges
|
|
if '/' in target:
|
|
try:
|
|
network = ipaddress.ip_network(target, strict=False)
|
|
subnet_key = str(network)
|
|
|
|
if subnet_key not in infrastructure['subnets']:
|
|
subnet_count += 1
|
|
infrastructure['subnets'][subnet_key] = {
|
|
'network': subnet_key,
|
|
'targets': [],
|
|
'sample_targets': [],
|
|
'key_infrastructure': [],
|
|
'subnet_id': subnet_count,
|
|
'is_full_network': True
|
|
}
|
|
|
|
# For full networks, we'll sample them intelligently (not all hosts)
|
|
all_hosts = list(network.hosts())
|
|
if len(all_hosts) > 50:
|
|
# Large network - take strategic samples
|
|
sample_hosts = all_hosts[:10] + all_hosts[-10:] + all_hosts[len(all_hosts)//2:len(all_hosts)//2+10]
|
|
infrastructure['subnets'][subnet_key]['targets'] = [str(h) for h in sample_hosts[:30]]
|
|
else:
|
|
# Small network - include all
|
|
infrastructure['subnets'][subnet_key]['targets'] = [str(h) for h in all_hosts]
|
|
|
|
except ValueError:
|
|
infrastructure['individual_hosts'].append(target)
|
|
else:
|
|
# Hostname or IP range
|
|
infrastructure['individual_hosts'].append(target)
|
|
|
|
# Generate sample targets for each subnet (for testing) - SMALL samples only
|
|
for subnet_key, subnet_data in infrastructure['subnets'].items():
|
|
targets_in_subnet = subnet_data['targets']
|
|
|
|
if len(targets_in_subnet) <= 5:
|
|
# Very small subnet - test all targets
|
|
subnet_data['sample_targets'] = targets_in_subnet.copy()
|
|
else:
|
|
# Larger subnet - SMALL intelligent sampling (max 8 targets)
|
|
sample_size = min(8, max(3, len(targets_in_subnet) // 20)) # Much smaller sample
|
|
|
|
# Always include first, last, and some middle targets
|
|
samples = []
|
|
samples.append(targets_in_subnet[0]) # First
|
|
if len(targets_in_subnet) > 1:
|
|
samples.append(targets_in_subnet[-1]) # Last
|
|
|
|
# Add evenly distributed samples
|
|
remaining = sample_size - len(samples)
|
|
if remaining > 0 and len(targets_in_subnet) > 2:
|
|
step = len(targets_in_subnet) // (remaining + 1)
|
|
for i in range(remaining):
|
|
idx = (i + 1) * step
|
|
if idx < len(targets_in_subnet):
|
|
samples.append(targets_in_subnet[idx])
|
|
|
|
subnet_data['sample_targets'] = list(set(samples))
|
|
|
|
# Add key infrastructure to samples
|
|
subnet_data['sample_targets'].extend(subnet_data['key_infrastructure'])
|
|
subnet_data['sample_targets'] = list(set(subnet_data['sample_targets']))
|
|
|
|
# Discover system DNS servers
|
|
try:
|
|
with open('/etc/resolv.conf', 'r') as f:
|
|
for line in f:
|
|
if line.startswith('nameserver'):
|
|
dns_ip = line.split()[1]
|
|
try:
|
|
ipaddress.ip_address(dns_ip)
|
|
infrastructure['dns_servers'].add(dns_ip)
|
|
except ValueError:
|
|
pass
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
# Add common external DNS if none found
|
|
if not infrastructure['dns_servers']:
|
|
infrastructure['dns_servers'].update(['8.8.8.8', '1.1.1.1'])
|
|
|
|
# Generate summary
|
|
total_samples = sum(len(e['sample_targets']) for e in infrastructure['subnets'].values())
|
|
infrastructure['analysis_summary'] = {
|
|
'total_subnets': len(infrastructure['subnets']),
|
|
'total_targets': sum(len(e['targets']) for e in infrastructure['subnets'].values()),
|
|
'total_samples': total_samples,
|
|
'individual_hosts': len(infrastructure['individual_hosts']),
|
|
'dns_servers': len(infrastructure['dns_servers']),
|
|
'reduction_ratio': total_original / max(total_samples, 1)
|
|
}
|
|
|
|
# Print detailed analysis
|
|
print(f"{Colors.OKGREEN}[+] Infrastructure Analysis Complete{Colors.ENDC}")
|
|
reduction_pct = (1 - total_samples / total_original) * 100
|
|
print(f"{Colors.OKGREEN}[+] Smart sampling: {total_samples:,} tests vs {total_original:,} original ({reduction_pct:.1f}% reduction){Colors.ENDC}")
|
|
|
|
print(f"{Colors.OKBLUE}[*] Identified {len(infrastructure['subnets'])} network subnets:{Colors.ENDC}")
|
|
|
|
for subnet_key, subnet_data in infrastructure['subnets'].items():
|
|
subnet_id = subnet_data['subnet_id']
|
|
target_count = len(subnet_data['targets'])
|
|
sample_count = len(subnet_data['sample_targets'])
|
|
|
|
print(f"{Colors.OKCYAN} [{subnet_id}] {subnet_key}: {target_count} targets → {sample_count} samples{Colors.ENDC}")
|
|
|
|
return infrastructure
|
|
|
|
def test_subnet_reachability(infrastructure, debug=False, timeout=3):
|
|
"""Test each subnet independently to determine reachability."""
|
|
results = {
|
|
'reachable_subnets': {},
|
|
'unreachable_subnets': {},
|
|
'dns_servers': {'reachable': [], 'unreachable': []},
|
|
'individual_hosts': {'reachable': [], 'unreachable': []},
|
|
'testing_summary': {}
|
|
}
|
|
|
|
print(f"\n{Colors.OKGREEN}[+] Testing Subnet Reachability{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Strategy: ANY response from subnet = entire subnet reachable{Colors.ENDC}")
|
|
|
|
# Test DNS servers first
|
|
dns_servers_list = list(infrastructure['dns_servers'])
|
|
total_dns = len(dns_servers_list)
|
|
|
|
if total_dns > 0:
|
|
print(f"{Colors.OKBLUE}[*] Step 1: Testing DNS connectivity ({total_dns} servers){Colors.ENDC}")
|
|
|
|
for dns_idx, dns_server in enumerate(dns_servers_list, 1):
|
|
print(f"{Colors.OKCYAN} [{dns_idx}/{total_dns}] Testing DNS server {dns_server}...{Colors.ENDC}", end=' ')
|
|
|
|
is_reachable = test_dns_connectivity(dns_server, timeout)
|
|
|
|
if is_reachable:
|
|
results['dns_servers']['reachable'].append(dns_server)
|
|
print(f"{Colors.OKGREEN}✓ REACHABLE{Colors.ENDC}")
|
|
else:
|
|
results['dns_servers']['unreachable'].append(dns_server)
|
|
print(f"{Colors.FAIL}✗ UNREACHABLE{Colors.ENDC}")
|
|
|
|
print(f"{Colors.OKBLUE}[*] DNS Summary: {len(results['dns_servers']['reachable'])}/{total_dns} reachable{Colors.ENDC}")
|
|
|
|
# Test each subnet - handle both 'subnets' and 'enclaves' keys for compatibility
|
|
subnet_dict = infrastructure.get('subnets', infrastructure.get('enclaves', {}))
|
|
total_subnets = len(subnet_dict)
|
|
|
|
print(f"\n{Colors.OKBLUE}[*] Step 2: Testing Network Subnet Reachability ({total_subnets} subnets){Colors.ENDC}")
|
|
|
|
subnet_counter = 0
|
|
for subnet_key, subnet_data in subnet_dict.items():
|
|
subnet_counter += 1
|
|
subnet_id = subnet_data['subnet_id']
|
|
sample_targets = subnet_data['sample_targets']
|
|
|
|
print(f"\n{Colors.OKCYAN}[{subnet_counter}/{total_subnets}] Testing subnet: {subnet_key} (ID: {subnet_id}){Colors.ENDC}")
|
|
print(f"{Colors.OKCYAN} Sample size: {len(sample_targets)} targets{Colors.ENDC}")
|
|
|
|
# Test samples in parallel
|
|
subnet_results = []
|
|
reachable_count = 0
|
|
|
|
with ThreadPoolExecutor(max_workers=min(20, len(sample_targets))) as executor:
|
|
future_to_target = {
|
|
executor.submit(test_basic_connectivity, target, timeout): target
|
|
for target in sample_targets
|
|
}
|
|
|
|
target_counter = 0
|
|
for future in as_completed(future_to_target):
|
|
target = future_to_target[future]
|
|
target_counter += 1
|
|
try:
|
|
is_reachable = future.result()
|
|
subnet_results.append((target, is_reachable))
|
|
|
|
if is_reachable:
|
|
reachable_count += 1
|
|
if debug:
|
|
print(f"{Colors.OKGREEN} [{target_counter}/{len(sample_targets)}] ✓ {target}{Colors.ENDC}")
|
|
else:
|
|
if debug:
|
|
print(f"{Colors.FAIL} [{target_counter}/{len(sample_targets)}] ✗ {target}{Colors.ENDC}")
|
|
|
|
except Exception as e:
|
|
subnet_results.append((target, False))
|
|
if debug:
|
|
print(f"{Colors.WARNING} [{target_counter}/{len(sample_targets)}] ! {target} (error: {e}){Colors.ENDC}")
|
|
|
|
# Calculate reachability
|
|
reachability_percentage = (reachable_count / len(sample_targets)) * 100 if sample_targets else 0
|
|
|
|
print(f"{Colors.OKCYAN} Results: {reachable_count}/{len(sample_targets)} samples reachable ({reachability_percentage:.1f}%){Colors.ENDC}")
|
|
|
|
# New logic: ANY response means subnet is reachable
|
|
if reachable_count > 0:
|
|
results['reachable_subnets'][subnet_key] = {
|
|
'subnet_data': subnet_data,
|
|
'sample_results': subnet_results,
|
|
'reachable_count': reachable_count,
|
|
'total_tested': len(sample_targets),
|
|
'reachability_percentage': reachability_percentage,
|
|
'confidence': 'high' if reachability_percentage >= 50 else 'medium' if reachable_count >= 3 else 'low'
|
|
}
|
|
confidence = results['reachable_subnets'][subnet_key]['confidence']
|
|
print(f"{Colors.OKGREEN} → Subnet REACHABLE (confidence: {confidence.upper()}){Colors.ENDC}")
|
|
else:
|
|
results['unreachable_subnets'][subnet_key] = {
|
|
'subnet_data': subnet_data,
|
|
'sample_results': subnet_results,
|
|
'reachable_count': reachable_count,
|
|
'total_tested': len(sample_targets),
|
|
'reachability_percentage': reachability_percentage
|
|
}
|
|
print(f"{Colors.FAIL} → Subnet UNREACHABLE (zero response - no routing){Colors.ENDC}")
|
|
|
|
# Show overall progress
|
|
remaining_subnets = total_subnets - subnet_counter
|
|
if remaining_subnets > 0:
|
|
print(f"{Colors.OKBLUE} Progress: {subnet_counter}/{total_subnets} subnets completed ({remaining_subnets} remaining){Colors.ENDC}")
|
|
|
|
# Test individual hosts
|
|
individual_hosts = infrastructure['individual_hosts']
|
|
total_individual = len(individual_hosts)
|
|
|
|
if total_individual > 0:
|
|
print(f"\n{Colors.OKBLUE}[*] Step 3: Testing Individual Hosts ({total_individual} hosts){Colors.ENDC}")
|
|
|
|
with ThreadPoolExecutor(max_workers=20) as executor:
|
|
future_to_host = {
|
|
executor.submit(test_basic_connectivity, host, timeout): host
|
|
for host in individual_hosts
|
|
}
|
|
|
|
host_counter = 0
|
|
for future in as_completed(future_to_host):
|
|
host = future_to_host[future]
|
|
host_counter += 1
|
|
try:
|
|
is_reachable = future.result()
|
|
if is_reachable:
|
|
results['individual_hosts']['reachable'].append(host)
|
|
print(f"{Colors.OKGREEN} [{host_counter}/{total_individual}] ✓ {host}{Colors.ENDC}")
|
|
else:
|
|
results['individual_hosts']['unreachable'].append(host)
|
|
print(f"{Colors.FAIL} [{host_counter}/{total_individual}] ✗ {host}{Colors.ENDC}")
|
|
except Exception as e:
|
|
results['individual_hosts']['unreachable'].append(host)
|
|
print(f"{Colors.WARNING} [{host_counter}/{total_individual}] ! {host} (error){Colors.ENDC}")
|
|
|
|
# Generate testing summary
|
|
results['testing_summary'] = {
|
|
'total_subnets_tested': total_subnets,
|
|
'reachable_subnets_count': len(results['reachable_subnets']),
|
|
'unreachable_subnets_count': len(results['unreachable_subnets']),
|
|
'subnet_reachability_percentage': len(results['reachable_subnets']) / max(total_subnets, 1) * 100,
|
|
'dns_reachability_percentage': len(results['dns_servers']['reachable']) / max(total_dns, 1) * 100 if total_dns > 0 else 0
|
|
}
|
|
|
|
return results
|
|
|
|
def test_basic_connectivity(target, timeout=3):
|
|
"""Test basic connectivity using multiple methods quickly."""
|
|
# Method 1: ICMP ping (fastest)
|
|
try:
|
|
if sys.platform.startswith('win'):
|
|
result = subprocess.run(['ping', '-n', '1', '-w', str(timeout*1000), target],
|
|
capture_output=True, text=True, timeout=timeout+1)
|
|
else:
|
|
result = subprocess.run(['ping', '-c', '1', '-W', str(timeout), target],
|
|
capture_output=True, text=True, timeout=timeout+1)
|
|
|
|
if result.returncode == 0:
|
|
return True
|
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
|
|
pass
|
|
|
|
# Method 2: ARP ping for local networks (often more reliable than ICMP)
|
|
try:
|
|
# Check if target appears to be in local network (basic check)
|
|
target_ip = ipaddress.ip_address(target)
|
|
if target_ip.is_private:
|
|
# Use nmap ARP ping for local networks
|
|
result = subprocess.run(['nmap', '-PR', '-sn', '--max-retries', '1',
|
|
'--max-rtt-timeout', f'{timeout}s', target],
|
|
capture_output=True, text=True, timeout=timeout+2)
|
|
if result.returncode == 0 and "Host is up" in result.stdout:
|
|
return True
|
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError, ValueError):
|
|
pass
|
|
|
|
# Method 3: Quick TCP tests on common ports
|
|
common_ports = [22, 80, 443, 135, 139, 445, 3389]
|
|
|
|
for port in common_ports:
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(timeout / len(common_ports))
|
|
result = sock.connect_ex((target, port))
|
|
sock.close()
|
|
|
|
if result == 0:
|
|
return True
|
|
except (socket.error, socket.timeout):
|
|
continue
|
|
|
|
return False
|
|
|
|
def test_optimized_connectivity(targets, timeout=3, debug=False):
|
|
"""Optimized connectivity testing with fastest methods first and verified host tracking."""
|
|
verified_hosts = set()
|
|
unverified_hosts = set(targets)
|
|
|
|
print(f"{Colors.OKBLUE}[*] Optimized connectivity testing: fastest methods first{Colors.ENDC}")
|
|
|
|
# Phase 1: ICMP ping sweep (fastest method)
|
|
print(f"{Colors.OKCYAN}[*] Phase 1: ICMP ping sweep (fastest)...{Colors.ENDC}")
|
|
icmp_verified = 0
|
|
for target in list(unverified_hosts):
|
|
try:
|
|
if sys.platform.startswith('win'):
|
|
result = subprocess.run(['ping', '-n', '1', '-w', str(timeout*1000), target],
|
|
capture_output=True, text=True, timeout=timeout+1)
|
|
else:
|
|
result = subprocess.run(['ping', '-c', '1', '-W', str(timeout), target],
|
|
capture_output=True, text=True, timeout=timeout+1)
|
|
|
|
if result.returncode == 0:
|
|
verified_hosts.add(target)
|
|
unverified_hosts.remove(target)
|
|
icmp_verified += 1
|
|
if debug:
|
|
print(f"{Colors.OKGREEN}[+] ICMP: {target} verified{Colors.ENDC}")
|
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
|
|
continue
|
|
|
|
print(f"{Colors.OKGREEN}[+] ICMP verified: {icmp_verified} hosts, {len(unverified_hosts)} remaining{Colors.ENDC}")
|
|
|
|
if not unverified_hosts:
|
|
return list(verified_hosts), []
|
|
|
|
# Phase 2: ARP ping for local networks (often catches hosts that don't respond to ICMP)
|
|
print(f"{Colors.OKCYAN}[*] Phase 2: ARP ping for remaining local network hosts...{Colors.ENDC}")
|
|
arp_verified = 0
|
|
for target in list(unverified_hosts):
|
|
try:
|
|
target_ip = ipaddress.ip_address(target)
|
|
if target_ip.is_private:
|
|
result = subprocess.run(['nmap', '-PR', '-sn', '--max-retries', '1',
|
|
'--max-rtt-timeout', f'{timeout}s', target],
|
|
capture_output=True, text=True, timeout=timeout+2)
|
|
if result.returncode == 0 and "Host is up" in result.stdout:
|
|
verified_hosts.add(target)
|
|
unverified_hosts.remove(target)
|
|
arp_verified += 1
|
|
if debug:
|
|
print(f"{Colors.OKGREEN}[+] ARP: {target} verified{Colors.ENDC}")
|
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError, ValueError):
|
|
continue
|
|
|
|
print(f"{Colors.OKGREEN}[+] ARP verified: {arp_verified} hosts, {len(unverified_hosts)} remaining{Colors.ENDC}")
|
|
|
|
if not unverified_hosts:
|
|
return list(verified_hosts), []
|
|
|
|
# Phase 3: TCP port checks for remaining hosts (thorough but slower)
|
|
print(f"{Colors.OKCYAN}[*] Phase 3: TCP port checks for remaining {len(unverified_hosts)} hosts...{Colors.ENDC}")
|
|
tcp_verified = 0
|
|
common_ports = [22, 80, 443, 135, 139, 445, 3389]
|
|
|
|
for target in list(unverified_hosts):
|
|
for port in common_ports:
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(timeout / len(common_ports))
|
|
result = sock.connect_ex((target, port))
|
|
sock.close()
|
|
|
|
if result == 0:
|
|
verified_hosts.add(target)
|
|
unverified_hosts.remove(target)
|
|
tcp_verified += 1
|
|
if debug:
|
|
print(f"{Colors.OKGREEN}[+] TCP: {target}:{port} verified{Colors.ENDC}")
|
|
break # Host verified, no need to test other ports
|
|
except (socket.error, socket.timeout):
|
|
continue
|
|
|
|
print(f"{Colors.OKGREEN}[+] TCP verified: {tcp_verified} hosts, {len(unverified_hosts)} remaining{Colors.ENDC}")
|
|
|
|
# Note: DNS testing is handled separately as it's already optimized but slow
|
|
# DNS testing should be done last if all other methods fail
|
|
|
|
print(f"{Colors.OKGREEN}[+] Fast connectivity testing complete: {len(verified_hosts)} verified, {len(unverified_hosts)} for DNS testing{Colors.ENDC}")
|
|
|
|
return list(verified_hosts), list(unverified_hosts)
|
|
|
|
def generate_conservative_target_list(infrastructure, subnet_results, debug=False):
|
|
"""Generate a realistic target list based on actual reachability testing."""
|
|
|
|
print(f"\n{Colors.OKGREEN}[+] Generating Realistic Target List{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Logic: ANY reachable host in subnet = entire subnet is reachable{Colors.ENDC}")
|
|
|
|
final_targets = []
|
|
decision_log = []
|
|
|
|
# Add reachable DNS servers
|
|
dns_targets = subnet_results['dns_servers']['reachable']
|
|
if dns_targets:
|
|
final_targets.extend(dns_targets)
|
|
decision_log.append(f"Added {len(dns_targets)} reachable DNS servers")
|
|
print(f"{Colors.OKCYAN}[+] DNS Servers: Added {len(dns_targets)} confirmed reachable{Colors.ENDC}")
|
|
|
|
# Process ALL subnets - if ANY sample responds, include the whole subnet
|
|
all_subnets = {**subnet_results['reachable_subnets'], **subnet_results['unreachable_subnets']}
|
|
|
|
for subnet_key, subnet_info in all_subnets.items():
|
|
subnet_data = subnet_info['subnet_data']
|
|
reachable_count = subnet_info['reachable_count']
|
|
total_tested = subnet_info['total_tested']
|
|
reachability_pct = subnet_info['reachability_percentage']
|
|
|
|
print(f"\n{Colors.OKCYAN}[{subnet_data['subnet_id']}] Processing subnet: {subnet_key}{Colors.ENDC}")
|
|
print(f"{Colors.OKCYAN} Test Results: {reachable_count}/{total_tested} samples responded ({reachability_pct:.1f}%){Colors.ENDC}")
|
|
|
|
if reachable_count > 0:
|
|
# ANY response means the network is reachable
|
|
targets_to_add = subnet_data['targets']
|
|
final_targets.extend(targets_to_add)
|
|
|
|
# Determine confidence level for user awareness
|
|
if reachability_pct >= 50:
|
|
confidence = "HIGH"
|
|
reason = "majority of samples responded"
|
|
elif reachable_count >= 3:
|
|
confidence = "MEDIUM"
|
|
reason = "multiple samples responded"
|
|
else:
|
|
confidence = "LOW"
|
|
reason = "minimal samples responded, but network is routable"
|
|
|
|
decision_log.append(f"subnet {subnet_key}: added all {len(targets_to_add)} targets (confidence: {confidence})")
|
|
print(f"{Colors.OKGREEN} → INCLUDED all {len(targets_to_add)} targets{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN} → Confidence: {confidence} ({reason}){Colors.ENDC}")
|
|
|
|
else:
|
|
# Absolutely no response - likely not routable
|
|
decision_log.append(f"subnet {subnet_key}: excluded - zero response from all {total_tested} samples")
|
|
print(f"{Colors.FAIL} → EXCLUDED (zero response from all samples - likely not routable){Colors.ENDC}")
|
|
|
|
# Add individual reachable hosts
|
|
individual_reachable = subnet_results['individual_hosts']['reachable']
|
|
if individual_reachable:
|
|
final_targets.extend(individual_reachable)
|
|
decision_log.append(f"Added {len(individual_reachable)} individual reachable hosts")
|
|
print(f"{Colors.OKCYAN}[+] Individual Hosts: Added {len(individual_reachable)} confirmed reachable{Colors.ENDC}")
|
|
|
|
# Remove duplicates while preserving order
|
|
seen = set()
|
|
unique_targets = []
|
|
for target in final_targets:
|
|
if target not in seen:
|
|
seen.add(target)
|
|
unique_targets.append(target)
|
|
|
|
print(f"\n{Colors.OKGREEN}[+] Realistic Target List Summary:{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Total unique targets: {len(unique_targets)}{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Original target count: {infrastructure['analysis_summary']['total_targets']}{Colors.ENDC}")
|
|
|
|
if infrastructure['analysis_summary']['total_targets'] > 0:
|
|
reduction_pct = (1 - len(unique_targets) / infrastructure['analysis_summary']['total_targets']) * 100
|
|
if reduction_pct > 0:
|
|
print(f"{Colors.OKBLUE}[*] Filtered out: {reduction_pct:.1f}% (unreachable networks){Colors.ENDC}")
|
|
else:
|
|
print(f"{Colors.OKBLUE}[*] No networks filtered - all appear reachable{Colors.ENDC}")
|
|
|
|
return unique_targets, decision_log
|
|
|
|
def run_dns_intelligence_gathering(targets, base_dir=None, debug=False, timeout=5):
|
|
"""Run comprehensive DNS intelligence gathering as first phase of reachability testing."""
|
|
|
|
if base_dir:
|
|
dns_dir = os.path.join(base_dir, "scans", "dns")
|
|
os.makedirs(dns_dir, exist_ok=True)
|
|
else:
|
|
dns_dir = "./dns_intelligence"
|
|
os.makedirs(dns_dir, exist_ok=True)
|
|
|
|
print(f"{Colors.OKGREEN}[+] DNS Intelligence Gathering Phase{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Strategy: Extract reachable hosts from DNS before connectivity testing{Colors.ENDC}")
|
|
|
|
start_time = time.time()
|
|
|
|
# Phase 1: Discover DNS servers
|
|
dns_servers = discover_dns_servers(targets, debug)
|
|
print(f"{Colors.OKBLUE}[*] Found {len(dns_servers)} DNS servers to query{Colors.ENDC}")
|
|
|
|
# Phase 2: Attempt zone transfers
|
|
zone_transfer_results = attempt_zone_transfers(dns_servers, debug, timeout)
|
|
|
|
# Phase 3: Reverse DNS sweeps
|
|
reverse_dns_results = perform_reverse_dns_sweeps(targets, dns_servers, debug, timeout)
|
|
|
|
# Phase 4: Forward DNS brute forcing
|
|
forward_dns_results = perform_forward_dns_enumeration(targets, dns_servers, debug, timeout)
|
|
|
|
# Phase 5: Compile intelligence
|
|
dns_intelligence = compile_dns_intelligence(
|
|
zone_transfer_results, reverse_dns_results, forward_dns_results, debug
|
|
)
|
|
|
|
total_time = time.time() - start_time
|
|
|
|
# Generate reports
|
|
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
|
generate_dns_intelligence_reports(dns_intelligence, dns_dir, timestamp, total_time)
|
|
|
|
return dns_intelligence
|
|
|
|
def discover_dns_servers(targets, debug=False):
|
|
"""Discover all potential DNS servers from targets and system config."""
|
|
dns_servers = set()
|
|
|
|
print(f"{Colors.OKBLUE}[*] Phase 1: DNS Server Discovery{Colors.ENDC}")
|
|
|
|
# Get system DNS servers
|
|
try:
|
|
with open('/etc/resolv.conf', 'r') as f:
|
|
for line in f:
|
|
if line.startswith('nameserver'):
|
|
dns_ip = line.split()[1]
|
|
try:
|
|
ipaddress.ip_address(dns_ip)
|
|
dns_servers.add(dns_ip)
|
|
if debug:
|
|
print(f"{Colors.OKCYAN} Found system DNS: {dns_ip}{Colors.ENDC}")
|
|
except ValueError:
|
|
pass
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
# Extract potential DNS servers from target networks
|
|
for target in targets:
|
|
try:
|
|
if '/' in target:
|
|
# CIDR network
|
|
network = ipaddress.ip_network(target, strict=False)
|
|
if network.is_private and network.num_addresses > 2:
|
|
# Common DNS server positions in networks
|
|
common_dns_positions = [1, 2, 10, 53, 100]
|
|
base_ip = str(network.network_address)
|
|
|
|
for pos in common_dns_positions:
|
|
try:
|
|
dns_candidate = str(list(network.hosts())[pos-1])
|
|
dns_servers.add(dns_candidate)
|
|
except (IndexError, ValueError):
|
|
pass
|
|
else:
|
|
# Individual IP - check if it could be a DNS server
|
|
try:
|
|
ip_obj = ipaddress.ip_address(target)
|
|
if ip_obj.is_private:
|
|
octets = str(ip_obj).split('.')
|
|
# Add likely DNS servers in same subnet
|
|
subnet_dns = [
|
|
f"{octets[0]}.{octets[1]}.{octets[2]}.1",
|
|
f"{octets[0]}.{octets[1]}.{octets[2]}.2",
|
|
f"{octets[0]}.{octets[1]}.{octets[2]}.10",
|
|
f"{octets[0]}.{octets[1]}.{octets[2]}.53"
|
|
]
|
|
dns_servers.update(subnet_dns)
|
|
except ValueError:
|
|
pass
|
|
except ValueError:
|
|
continue
|
|
|
|
# Add common external DNS servers
|
|
dns_servers.update(['8.8.8.8', '8.8.4.4', '1.1.1.1', '1.0.0.1'])
|
|
|
|
# Test which DNS servers actually respond
|
|
working_dns = []
|
|
print(f"{Colors.OKCYAN} Testing {len(dns_servers)} potential DNS servers...{Colors.ENDC}")
|
|
|
|
with ThreadPoolExecutor(max_workers=20) as executor:
|
|
future_to_dns = {
|
|
executor.submit(test_dns_connectivity, dns_server, 3): dns_server
|
|
for dns_server in dns_servers
|
|
}
|
|
|
|
for future in as_completed(future_to_dns):
|
|
dns_server = future_to_dns[future]
|
|
try:
|
|
if future.result():
|
|
working_dns.append(dns_server)
|
|
if debug:
|
|
print(f"{Colors.OKGREEN} ✓ {dns_server}{Colors.ENDC}")
|
|
except Exception:
|
|
pass
|
|
|
|
print(f"{Colors.OKGREEN} → {len(working_dns)} working DNS servers found{Colors.ENDC}")
|
|
return working_dns
|
|
|
|
def attempt_zone_transfers(dns_servers, debug=False, timeout=10):
|
|
"""Attempt DNS zone transfers (AXFR) from discovered domains."""
|
|
print(f"\n{Colors.OKBLUE}[*] Phase 2: DNS Zone Transfer Attempts{Colors.ENDC}")
|
|
|
|
zone_results = {
|
|
'successful_transfers': {},
|
|
'failed_transfers': [],
|
|
'discovered_domains': set(),
|
|
'discovered_hosts': set()
|
|
}
|
|
|
|
# Common internal domain patterns to try
|
|
common_domains = [
|
|
'local', 'internal', 'corp', 'company', 'domain', 'ad', 'lan',
|
|
'intranet', 'office', 'net', 'priv', 'private'
|
|
]
|
|
|
|
# Also try reverse zones for common private networks
|
|
reverse_zones = [
|
|
'10.in-addr.arpa', '168.192.in-addr.arpa',
|
|
'16.172.in-addr.arpa', '17.172.in-addr.arpa', '18.172.in-addr.arpa'
|
|
]
|
|
|
|
all_zones_to_try = common_domains + reverse_zones
|
|
|
|
print(f"{Colors.OKCYAN} Attempting zone transfers for {len(all_zones_to_try)} common zones...{Colors.ENDC}")
|
|
|
|
for dns_server in dns_servers[:5]: # Limit to first 5 DNS servers
|
|
for zone in all_zones_to_try:
|
|
try:
|
|
if debug:
|
|
print(f"{Colors.OKCYAN} Trying {zone} from {dns_server}...{Colors.ENDC}")
|
|
|
|
# Use dig for zone transfer
|
|
cmd = f"dig @{dns_server} {zone} AXFR +time={timeout}"
|
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
|
|
|
|
if result.returncode == 0 and len(result.stdout) > 100:
|
|
# Successful transfer
|
|
zone_results['successful_transfers'][f"{dns_server}_{zone}"] = result.stdout
|
|
zone_results['discovered_domains'].add(zone)
|
|
|
|
# Parse hosts from zone transfer
|
|
for line in result.stdout.split('\n'):
|
|
if '\tA\t' in line or '\tAAAA\t' in line:
|
|
parts = line.split()
|
|
if len(parts) >= 5:
|
|
hostname = parts[0].rstrip('.')
|
|
ip = parts[4]
|
|
zone_results['discovered_hosts'].add(ip)
|
|
if debug:
|
|
print(f"{Colors.OKGREEN} Found: {hostname} -> {ip}{Colors.ENDC}")
|
|
|
|
print(f"{Colors.OKGREEN} ✓ Zone transfer successful: {zone} from {dns_server}{Colors.ENDC}")
|
|
else:
|
|
zone_results['failed_transfers'].append(f"{dns_server}_{zone}")
|
|
|
|
except subprocess.TimeoutExpired:
|
|
if debug:
|
|
print(f"{Colors.WARNING} Timeout: {zone} from {dns_server}{Colors.ENDC}")
|
|
except Exception as e:
|
|
if debug:
|
|
print(f"{Colors.WARNING} Error: {zone} from {dns_server} - {e}{Colors.ENDC}")
|
|
|
|
success_count = len(zone_results['successful_transfers'])
|
|
host_count = len(zone_results['discovered_hosts'])
|
|
|
|
if success_count > 0:
|
|
print(f"{Colors.OKGREEN} → {success_count} successful zone transfers, {host_count} hosts discovered{Colors.ENDC}")
|
|
else:
|
|
print(f"{Colors.WARNING} → No successful zone transfers (transfers likely disabled){Colors.ENDC}")
|
|
|
|
return zone_results
|
|
|
|
def perform_reverse_dns_sweeps(targets, dns_servers, debug=False, timeout=3):
|
|
"""Perform SMART reverse DNS sampling - not brute force sweeps."""
|
|
print(f"\n{Colors.OKBLUE}[*] Phase 3: Smart Reverse DNS Sampling{Colors.ENDC}")
|
|
|
|
reverse_results = {
|
|
'networks_with_dns': {},
|
|
'discovered_hosts': set(),
|
|
'hostname_patterns': set(),
|
|
'networks_with_no_dns': set()
|
|
}
|
|
|
|
# Extract IP networks from targets
|
|
networks_to_sample = []
|
|
for target in targets:
|
|
try:
|
|
if '/' in target:
|
|
network = ipaddress.ip_network(target, strict=False)
|
|
networks_to_sample.append(network)
|
|
else:
|
|
ip = ipaddress.ip_address(target)
|
|
if ip.version == 4:
|
|
network = ipaddress.ip_network(f"{ip}/24", strict=False)
|
|
networks_to_sample.append(network)
|
|
except ValueError:
|
|
continue
|
|
|
|
total_networks = len(networks_to_sample)
|
|
print(f"{Colors.OKCYAN} Smart sampling {total_networks} networks (not brute forcing){Colors.ENDC}")
|
|
|
|
network_counter = 0
|
|
for network in networks_to_sample:
|
|
network_counter += 1
|
|
print(f"{Colors.OKCYAN} [{network_counter}/{total_networks}] Sampling network: {network}...{Colors.ENDC}", end=' ')
|
|
|
|
# SMART SAMPLING: Only test a few strategic IPs per network
|
|
all_hosts = list(network.hosts())
|
|
if len(all_hosts) == 0:
|
|
print(f"{Colors.WARNING}No hosts{Colors.ENDC}")
|
|
continue
|
|
|
|
# Sample strategy: Test 5-8 strategic IPs to determine if reverse DNS exists
|
|
sample_ips = []
|
|
|
|
# Always test first few IPs (common for infrastructure)
|
|
sample_ips.extend(all_hosts[:3])
|
|
|
|
# Test some IPs from the middle
|
|
if len(all_hosts) > 10:
|
|
mid_point = len(all_hosts) // 2
|
|
sample_ips.extend(all_hosts[mid_point:mid_point+2])
|
|
|
|
# Test last few IPs
|
|
if len(all_hosts) > 5:
|
|
sample_ips.extend(all_hosts[-2:])
|
|
|
|
# Remove duplicates and limit to max 8 samples
|
|
sample_ips = list(dict.fromkeys(sample_ips))[:8]
|
|
|
|
# Test samples quickly
|
|
network_has_reverse_dns = False
|
|
found_hosts = []
|
|
|
|
with ThreadPoolExecutor(max_workers=8) as executor:
|
|
future_to_ip = {
|
|
executor.submit(reverse_dns_lookup, str(ip), dns_servers[0] if dns_servers else '8.8.8.8', timeout): str(ip)
|
|
for ip in sample_ips
|
|
}
|
|
|
|
for future in as_completed(future_to_ip):
|
|
ip = future_to_ip[future]
|
|
try:
|
|
hostname = future.result()
|
|
if hostname:
|
|
network_has_reverse_dns = True
|
|
found_hosts.append((ip, hostname))
|
|
reverse_results['discovered_hosts'].add(ip)
|
|
reverse_results['hostname_patterns'].add(hostname)
|
|
|
|
if debug:
|
|
print(f"\n{Colors.OKGREEN} {ip} -> {hostname}{Colors.ENDC}")
|
|
|
|
except Exception:
|
|
pass
|
|
|
|
if network_has_reverse_dns:
|
|
reverse_results['networks_with_dns'][str(network)] = found_hosts
|
|
print(f"{Colors.OKGREEN}✓ HAS REVERSE DNS ({len(found_hosts)} found){Colors.ENDC}")
|
|
|
|
# If we found reverse DNS in samples, it's worth checking a few more strategic IPs
|
|
if len(found_hosts) >= 2:
|
|
print(f"{Colors.OKCYAN} Network appears to use reverse DNS - checking key infrastructure IPs...{Colors.ENDC}")
|
|
|
|
# Check common infrastructure IPs that might have DNS
|
|
key_infrastructure_ips = []
|
|
base_octets = str(network.network_address).split('.')
|
|
|
|
# Common server/infrastructure IPs
|
|
common_endings = [1, 2, 10, 25, 53, 100, 200, 250, 254]
|
|
for ending in common_endings:
|
|
try:
|
|
potential_ip = f"{base_octets[0]}.{base_octets[1]}.{base_octets[2]}.{ending}"
|
|
if ipaddress.ip_address(potential_ip) in network:
|
|
key_infrastructure_ips.append(potential_ip)
|
|
except ValueError:
|
|
pass
|
|
|
|
# Test infrastructure IPs (max 10)
|
|
with ThreadPoolExecutor(max_workers=10) as executor:
|
|
infra_futures = {
|
|
executor.submit(reverse_dns_lookup, ip, dns_servers[0] if dns_servers else '8.8.8.8', timeout): ip
|
|
for ip in key_infrastructure_ips[:10]
|
|
}
|
|
|
|
for future in as_completed(infra_futures):
|
|
ip = infra_futures[future]
|
|
try:
|
|
hostname = future.result()
|
|
if hostname and ip not in [h[0] for h in found_hosts]:
|
|
found_hosts.append((ip, hostname))
|
|
reverse_results['discovered_hosts'].add(ip)
|
|
reverse_results['hostname_patterns'].add(hostname)
|
|
if debug:
|
|
print(f"{Colors.OKGREEN} Infrastructure: {ip} -> {hostname}{Colors.ENDC}")
|
|
except Exception:
|
|
pass
|
|
|
|
reverse_results['networks_with_dns'][str(network)] = found_hosts
|
|
else:
|
|
reverse_results['networks_with_no_dns'].add(str(network))
|
|
print(f"{Colors.FAIL}✗ No reverse DNS{Colors.ENDC}")
|
|
|
|
# Show progress
|
|
remaining = total_networks - network_counter
|
|
if remaining > 0 and network_counter % 5 == 0: # Show progress every 5 networks
|
|
print(f"{Colors.OKBLUE} Progress: {network_counter}/{total_networks} networks completed ({remaining} remaining){Colors.ENDC}")
|
|
|
|
total_hosts = len(reverse_results['discovered_hosts'])
|
|
networks_with_dns = len(reverse_results['networks_with_dns'])
|
|
networks_without_dns = len(reverse_results['networks_with_no_dns'])
|
|
|
|
print(f"{Colors.OKGREEN} → {total_hosts} hosts with reverse DNS across {networks_with_dns} networks{Colors.ENDC}")
|
|
print(f"{Colors.OKCYAN} → {networks_without_dns} networks have no reverse DNS configured{Colors.ENDC}")
|
|
|
|
return reverse_results
|
|
|
|
def analyze_network_infrastructure(targets, debug=False):
|
|
"""Analyze targets and identify key network infrastructure to test."""
|
|
infrastructure = {
|
|
'subnets': {}, # Use 'subnets' consistently
|
|
'individual_hosts': [],
|
|
'dns_servers': set(),
|
|
'analysis_summary': {},
|
|
'total_original_targets': 0
|
|
}
|
|
|
|
print(f"{Colors.OKGREEN}[+] Analyzing Target Infrastructure{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Processing {len(targets)} targets to identify network segments...{Colors.ENDC}")
|
|
|
|
# Count original targets for reduction metrics
|
|
total_original = 0
|
|
for target in targets:
|
|
try:
|
|
if '/' in target:
|
|
network = ipaddress.ip_network(target, strict=False)
|
|
total_original += network.num_addresses - 2
|
|
else:
|
|
total_original += 1
|
|
except ValueError:
|
|
total_original += 1
|
|
|
|
infrastructure['total_original_targets'] = total_original
|
|
print(f"{Colors.OKBLUE}[*] Original scope: ~{total_original:,} potential targets{Colors.ENDC}")
|
|
|
|
subnet_count = 0
|
|
|
|
for target in targets:
|
|
try:
|
|
ip_obj = ipaddress.ip_address(target)
|
|
|
|
if ip_obj.is_private:
|
|
octets = str(ip_obj).split('.')
|
|
|
|
if octets[0] == '10':
|
|
subnet_key = f"{octets[0]}.{octets[1]}.0.0/16"
|
|
elif octets[0] == '172' and 16 <= int(octets[1]) <= 31:
|
|
subnet_key = f"{octets[0]}.{octets[1]}.0.0/16"
|
|
elif octets[0] == '192' and octets[1] == '168':
|
|
subnet_key = f"{octets[0]}.{octets[1]}.{octets[2]}.0/24"
|
|
else:
|
|
subnet_key = f"{octets[0]}.{octets[1]}.{octets[2]}.0/24"
|
|
else:
|
|
subnet_key = f"public_{str(ip_obj)}"
|
|
|
|
if subnet_key not in infrastructure['subnets']:
|
|
subnet_count += 1
|
|
infrastructure['subnets'][subnet_key] = {
|
|
'network': subnet_key,
|
|
'targets': [],
|
|
'sample_targets': [],
|
|
'key_infrastructure': [],
|
|
'subnet_id': subnet_count
|
|
}
|
|
|
|
try:
|
|
network = ipaddress.ip_network(subnet_key, strict=False)
|
|
if network.is_private and network.num_addresses > 2:
|
|
base_ip = str(network.network_address).split('.')
|
|
key_ips = [
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.1",
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.254",
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.10",
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.53",
|
|
f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.100",
|
|
]
|
|
|
|
for key_ip in key_ips:
|
|
try:
|
|
if ipaddress.ip_address(key_ip) in network:
|
|
infrastructure['subnets'][subnet_key]['key_infrastructure'].append(key_ip)
|
|
except ValueError:
|
|
pass
|
|
except ValueError:
|
|
pass
|
|
|
|
infrastructure['subnets'][subnet_key]['targets'].append(str(ip_obj))
|
|
|
|
except ValueError:
|
|
if '/' in target:
|
|
try:
|
|
network = ipaddress.ip_network(target, strict=False)
|
|
subnet_key = str(network)
|
|
|
|
if subnet_key not in infrastructure['subnets']:
|
|
subnet_count += 1
|
|
infrastructure['subnets'][subnet_key] = {
|
|
'network': subnet_key,
|
|
'targets': [],
|
|
'sample_targets': [],
|
|
'key_infrastructure': [],
|
|
'subnet_id': subnet_count,
|
|
'is_full_network': True
|
|
}
|
|
|
|
all_hosts = list(network.hosts())
|
|
if len(all_hosts) > 50:
|
|
sample_hosts = all_hosts[:10] + all_hosts[-10:] + all_hosts[len(all_hosts)//2:len(all_hosts)//2+10]
|
|
infrastructure['subnets'][subnet_key]['targets'] = [str(h) for h in sample_hosts[:30]]
|
|
else:
|
|
infrastructure['subnets'][subnet_key]['targets'] = [str(h) for h in all_hosts]
|
|
|
|
except ValueError:
|
|
infrastructure['individual_hosts'].append(target)
|
|
else:
|
|
infrastructure['individual_hosts'].append(target)
|
|
|
|
# Generate sample targets for each subnet
|
|
for subnet_key, subnet_data in infrastructure['subnets'].items():
|
|
targets_in_subnet = subnet_data['targets']
|
|
|
|
if len(targets_in_subnet) <= 5:
|
|
subnet_data['sample_targets'] = targets_in_subnet.copy()
|
|
else:
|
|
sample_size = min(8, max(3, len(targets_in_subnet) // 20))
|
|
|
|
samples = []
|
|
samples.append(targets_in_subnet[0])
|
|
if len(targets_in_subnet) > 1:
|
|
samples.append(targets_in_subnet[-1])
|
|
|
|
remaining = sample_size - len(samples)
|
|
if remaining > 0 and len(targets_in_subnet) > 2:
|
|
step = len(targets_in_subnet) // (remaining + 1)
|
|
for i in range(remaining):
|
|
idx = (i + 1) * step
|
|
if idx < len(targets_in_subnet):
|
|
samples.append(targets_in_subnet[idx])
|
|
|
|
subnet_data['sample_targets'] = list(set(samples))
|
|
|
|
subnet_data['sample_targets'].extend(subnet_data['key_infrastructure'])
|
|
subnet_data['sample_targets'] = list(set(subnet_data['sample_targets']))
|
|
|
|
# Discover system DNS servers
|
|
try:
|
|
with open('/etc/resolv.conf', 'r') as f:
|
|
for line in f:
|
|
if line.startswith('nameserver'):
|
|
dns_ip = line.split()[1]
|
|
try:
|
|
ipaddress.ip_address(dns_ip)
|
|
infrastructure['dns_servers'].add(dns_ip)
|
|
except ValueError:
|
|
pass
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
if not infrastructure['dns_servers']:
|
|
infrastructure['dns_servers'].update(['8.8.8.8', '1.1.1.1'])
|
|
|
|
# Generate summary
|
|
infrastructure['analysis_summary'] = {
|
|
'total_subnets': len(infrastructure['subnets']),
|
|
'total_targets': sum(len(e['targets']) for e in infrastructure['subnets'].values()),
|
|
'total_samples': sum(len(e['sample_targets']) for e in infrastructure['subnets'].values()),
|
|
'individual_hosts': len(infrastructure['individual_hosts']),
|
|
'dns_servers': len(infrastructure['dns_servers'])
|
|
}
|
|
|
|
print(f"{Colors.OKGREEN}[+] Infrastructure Analysis Complete{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Identified {len(infrastructure['subnets'])} network subnets:{Colors.ENDC}")
|
|
|
|
for subnet_key, subnet_data in infrastructure['subnets'].items():
|
|
subnet_id = subnet_data['subnet_id']
|
|
target_count = len(subnet_data['targets'])
|
|
sample_count = len(subnet_data['sample_targets'])
|
|
|
|
print(f"{Colors.OKCYAN} [{subnet_id}] {subnet_key}: {target_count} targets → {sample_count} samples{Colors.ENDC}")
|
|
|
|
if debug:
|
|
print(f"{Colors.OKCYAN} Sample IPs: {', '.join(subnet_data['sample_targets'][:5])}{'...' if len(subnet_data['sample_targets']) > 5 else ''}{Colors.ENDC}")
|
|
|
|
if infrastructure['individual_hosts']:
|
|
print(f"{Colors.OKCYAN} Individual hosts: {len(infrastructure['individual_hosts'])}{Colors.ENDC}")
|
|
|
|
print(f"{Colors.OKBLUE}[*] Total testing targets: {infrastructure['analysis_summary']['total_samples']} (vs {infrastructure['analysis_summary']['total_targets']} original){Colors.ENDC}")
|
|
|
|
return infrastructure
|
|
|
|
def perform_forward_dns_enumeration(targets, dns_servers, debug=False, timeout=3):
|
|
"""Perform forward DNS enumeration using common hostname patterns."""
|
|
print(f"\n{Colors.OKBLUE}[*] Phase 4: Forward DNS Enumeration{Colors.ENDC}")
|
|
|
|
forward_results = {
|
|
'discovered_hosts': set(),
|
|
'successful_queries': {},
|
|
'domain_patterns': set()
|
|
}
|
|
|
|
# Common hostname patterns for internal networks
|
|
common_hostnames = [
|
|
'dc', 'dc1', 'dc2', 'dc01', 'dc02', 'domain', 'ad', 'ldap',
|
|
'dns', 'dns1', 'dns2', 'ns', 'ns1', 'ns2',
|
|
'mail', 'exchange', 'smtp', 'pop', 'imap',
|
|
'web', 'www', 'intranet', 'portal', 'sharepoint',
|
|
'db', 'database', 'sql', 'mysql', 'oracle',
|
|
'file', 'files', 'fs', 'nas', 'share', 'fileserver',
|
|
'backup', 'bkp', 'archive',
|
|
'fw', 'firewall', 'gw', 'gateway', 'router',
|
|
'monitor', 'nagios', 'zabbix', 'snmp',
|
|
'print', 'printer', 'cups',
|
|
'vm', 'vmware', 'vcenter', 'esxi',
|
|
'admin', 'mgmt', 'management', 'console'
|
|
]
|
|
|
|
# Extract potential domains from any hostname patterns we found
|
|
potential_domains = set(['local', 'internal', 'corp', 'domain', 'ad'])
|
|
|
|
# If we have DNS servers, try to extract domain from their configuration
|
|
for dns_server in dns_servers[:3]:
|
|
try:
|
|
# Try to get the DNS server's domain
|
|
cmd = f"dig @{dns_server} . NS +short +time=3"
|
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=5)
|
|
if result.returncode == 0:
|
|
for line in result.stdout.strip().split('\n'):
|
|
if '.' in line:
|
|
domain_parts = line.strip('.').split('.')
|
|
if len(domain_parts) >= 2:
|
|
potential_domains.add('.'.join(domain_parts[-2:]))
|
|
except:
|
|
pass
|
|
|
|
print(f"{Colors.OKCYAN} Testing {len(common_hostnames)} hostnames across {len(potential_domains)} domains...{Colors.ENDC}")
|
|
|
|
queries_to_test = []
|
|
for domain in potential_domains:
|
|
for hostname in common_hostnames:
|
|
fqdn = f"{hostname}.{domain}"
|
|
queries_to_test.append(fqdn)
|
|
|
|
# Limit total queries to reasonable number
|
|
if len(queries_to_test) > 200:
|
|
queries_to_test = queries_to_test[:200]
|
|
|
|
with ThreadPoolExecutor(max_workers=20) as executor:
|
|
future_to_query = {
|
|
executor.submit(forward_dns_lookup, query, dns_servers[0] if dns_servers else '8.8.8.8', timeout): query
|
|
for query in queries_to_test
|
|
}
|
|
|
|
successful_count = 0
|
|
for future in as_completed(future_to_query):
|
|
query = future_to_query[future]
|
|
try:
|
|
ip = future.result()
|
|
if ip:
|
|
forward_results['discovered_hosts'].add(ip)
|
|
forward_results['successful_queries'][query] = ip
|
|
forward_results['domain_patterns'].add(query.split('.', 1)[1])
|
|
successful_count += 1
|
|
|
|
if debug:
|
|
print(f"{Colors.OKGREEN} {query} -> {ip}{Colors.ENDC}")
|
|
|
|
except Exception:
|
|
pass
|
|
|
|
print(f"{Colors.OKGREEN} → {successful_count} successful DNS queries, {len(forward_results['discovered_hosts'])} unique hosts{Colors.ENDC}")
|
|
|
|
return forward_results
|
|
|
|
def reverse_dns_lookup(ip, dns_server, timeout=3):
|
|
"""Perform reverse DNS lookup for an IP address."""
|
|
try:
|
|
cmd = f"dig @{dns_server} -x {ip} +short +time={timeout}"
|
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout+1)
|
|
|
|
if result.returncode == 0 and result.stdout.strip():
|
|
hostname = result.stdout.strip().split('\n')[0].rstrip('.')
|
|
if hostname and not hostname.startswith(';'):
|
|
return hostname
|
|
except:
|
|
pass
|
|
|
|
return None
|
|
|
|
def forward_dns_lookup(hostname, dns_server, timeout=3):
|
|
"""Perform forward DNS lookup for a hostname."""
|
|
try:
|
|
cmd = f"dig @{dns_server} {hostname} A +short +time={timeout}"
|
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout+1)
|
|
|
|
if result.returncode == 0 and result.stdout.strip():
|
|
ip = result.stdout.strip().split('\n')[0]
|
|
# Validate it's actually an IP
|
|
try:
|
|
ipaddress.ip_address(ip)
|
|
return ip
|
|
except ValueError:
|
|
pass
|
|
except:
|
|
pass
|
|
|
|
return None
|
|
|
|
def compile_dns_intelligence(zone_results, reverse_results, forward_results, debug=False):
|
|
"""Compile all DNS intelligence into actionable target list."""
|
|
print(f"\n{Colors.OKBLUE}[*] Phase 5: Compiling DNS Intelligence{Colors.ENDC}")
|
|
|
|
intelligence = {
|
|
'high_value_targets': set(), # Hosts from DNS that likely exist
|
|
'medium_value_targets': set(), # Hosts from patterns/inference
|
|
'discovered_domains': set(),
|
|
'dns_summary': {},
|
|
'recommendations': []
|
|
}
|
|
|
|
# Compile all discovered hosts
|
|
all_discovered_hosts = set()
|
|
|
|
# Zone transfer results (highest confidence)
|
|
if zone_results['discovered_hosts']:
|
|
all_discovered_hosts.update(zone_results['discovered_hosts'])
|
|
intelligence['high_value_targets'].update(zone_results['discovered_hosts'])
|
|
intelligence['recommendations'].append(f"Zone transfers revealed {len(zone_results['discovered_hosts'])} hosts")
|
|
|
|
# Reverse DNS results (high confidence - these hosts have DNS records)
|
|
if reverse_results['discovered_hosts']:
|
|
all_discovered_hosts.update(reverse_results['discovered_hosts'])
|
|
intelligence['high_value_targets'].update(reverse_results['discovered_hosts'])
|
|
intelligence['recommendations'].append(f"Reverse DNS found {len(reverse_results['discovered_hosts'])} hosts")
|
|
|
|
# Forward DNS results (high confidence - these hosts resolve)
|
|
if forward_results['discovered_hosts']:
|
|
all_discovered_hosts.update(forward_results['discovered_hosts'])
|
|
intelligence['high_value_targets'].update(forward_results['discovered_hosts'])
|
|
intelligence['recommendations'].append(f"Forward DNS enumeration found {len(forward_results['discovered_hosts'])} hosts")
|
|
|
|
# Compile discovered domains
|
|
intelligence['discovered_domains'].update(zone_results['discovered_domains'])
|
|
intelligence['discovered_domains'].update(forward_results['domain_patterns'])
|
|
|
|
# Generate summary
|
|
intelligence['dns_summary'] = {
|
|
'total_discovered_hosts': len(all_discovered_hosts),
|
|
'zone_transfer_hosts': len(zone_results['discovered_hosts']),
|
|
'reverse_dns_hosts': len(reverse_results['discovered_hosts']),
|
|
'forward_dns_hosts': len(forward_results['discovered_hosts']),
|
|
'discovered_domains': len(intelligence['discovered_domains']),
|
|
'successful_zone_transfers': len(zone_results['successful_transfers'])
|
|
}
|
|
|
|
print(f"{Colors.OKGREEN}[+] DNS Intelligence Summary:{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Total hosts discovered via DNS: {len(all_discovered_hosts)}{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] High-value targets (DNS confirmed): {len(intelligence['high_value_targets'])}{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Domains discovered: {len(intelligence['discovered_domains'])}{Colors.ENDC}")
|
|
|
|
if len(all_discovered_hosts) > 0:
|
|
intelligence['recommendations'].append("Focus initial scanning on DNS-discovered hosts")
|
|
print(f"{Colors.OKGREEN}[+] Recommendation: Prioritize the {len(all_discovered_hosts)} DNS-confirmed targets{Colors.ENDC}")
|
|
else:
|
|
intelligence['recommendations'].append("No DNS intelligence gathered - proceed with standard reachability testing")
|
|
print(f"{Colors.WARNING}[!] No hosts discovered via DNS - proceeding with connectivity testing{Colors.ENDC}")
|
|
|
|
return intelligence
|
|
|
|
def generate_dns_intelligence_reports(intelligence, dns_dir, timestamp, total_time):
|
|
"""Generate DNS intelligence reports."""
|
|
|
|
# Main DNS targets file
|
|
dns_targets_file = os.path.join(dns_dir, f"dns_discovered_targets_{timestamp}.txt")
|
|
with open(dns_targets_file, 'w') as f:
|
|
f.write(f"# DNS Intelligence Gathering Results\n")
|
|
f.write(f"# Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"# Duration: {total_time:.2f} seconds\n")
|
|
f.write(f"# Total DNS-confirmed targets: {len(intelligence['high_value_targets'])}\n\n")
|
|
|
|
for target in sorted(intelligence['high_value_targets'],
|
|
key=lambda x: ipaddress.ip_address(x) if x.replace('.','').isdigit() else x):
|
|
f.write(f"{target}\n")
|
|
|
|
# Detailed intelligence report
|
|
intel_report = os.path.join(dns_dir, f"dns_intelligence_report_{timestamp}.txt")
|
|
with open(intel_report, 'w') as f:
|
|
f.write("=" * 70 + "\n")
|
|
f.write("DNS INTELLIGENCE GATHERING REPORT\n")
|
|
f.write("=" * 70 + "\n\n")
|
|
f.write(f"Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"Duration: {total_time:.2f} seconds\n\n")
|
|
|
|
summary = intelligence['dns_summary']
|
|
f.write("DISCOVERY SUMMARY:\n")
|
|
f.write("-" * 17 + "\n")
|
|
f.write(f"Total Hosts Discovered: {summary['total_discovered_hosts']}\n")
|
|
f.write(f"Zone Transfer Hosts: {summary['zone_transfer_hosts']}\n")
|
|
f.write(f"Reverse DNS Hosts: {summary['reverse_dns_hosts']}\n")
|
|
f.write(f"Forward DNS Hosts: {summary['forward_dns_hosts']}\n")
|
|
f.write(f"Domains Discovered: {summary['discovered_domains']}\n")
|
|
f.write(f"Successful Zone Transfers: {summary['successful_zone_transfers']}\n\n")
|
|
|
|
f.write("RECOMMENDATIONS:\n")
|
|
f.write("-" * 15 + "\n")
|
|
for rec in intelligence['recommendations']:
|
|
f.write(f"• {rec}\n")
|
|
|
|
print(f"{Colors.OKBLUE}[*] DNS targets file: {dns_targets_file}{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Intelligence report: {intel_report}{Colors.ENDC}")
|
|
|
|
def run_smart_network_reachability_test(targets, base_dir=None, standalone=False, debug=False,
|
|
threads=None, timeout=None):
|
|
"""Run comprehensive reachability assessment: DNS intelligence + connectivity testing."""
|
|
|
|
test_timeout = timeout if timeout is not None else REACHABILITY_TIMEOUT
|
|
|
|
if base_dir:
|
|
reachability_dir = os.path.join(base_dir, "scans", "reachability")
|
|
os.makedirs(reachability_dir, exist_ok=True)
|
|
else:
|
|
reachability_dir = "./reachability_results"
|
|
os.makedirs(reachability_dir, exist_ok=True)
|
|
|
|
print(f"{Colors.OKGREEN}[+] Comprehensive Network Reachability Assessment{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Strategy: DNS intelligence + connectivity testing for maximum coverage{Colors.ENDC}")
|
|
|
|
start_time = time.time()
|
|
|
|
# Phase 1: DNS Intelligence Gathering
|
|
print(f"\n{Colors.OKGREEN}=== PHASE 1: DNS INTELLIGENCE GATHERING ==={Colors.ENDC}")
|
|
dns_intelligence = run_dns_intelligence_gathering(targets, base_dir, debug, test_timeout)
|
|
|
|
# Phase 2: Infrastructure Analysis and Connectivity Testing
|
|
print(f"\n{Colors.OKGREEN}=== PHASE 2: NETWORK CONNECTIVITY TESTING ==={Colors.ENDC}")
|
|
infrastructure = analyze_network_infrastructure(targets, debug)
|
|
subnet_results = test_subnet_reachability(infrastructure, debug, test_timeout)
|
|
connectivity_targets, decision_log = generate_conservative_target_list(infrastructure, subnet_results, debug)
|
|
|
|
# Phase 3: Combine and Validate Results
|
|
print(f"\n{Colors.OKGREEN}=== PHASE 3: RESULTS INTEGRATION ==={Colors.ENDC}")
|
|
final_results = integrate_dns_and_connectivity_results(
|
|
dns_intelligence, connectivity_targets, debug, test_timeout
|
|
)
|
|
|
|
total_time = time.time() - start_time
|
|
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
|
|
|
# Generate comprehensive reports
|
|
return generate_comprehensive_reachability_reports(
|
|
final_results, dns_intelligence, infrastructure, subnet_results,
|
|
decision_log, reachability_dir, timestamp, total_time, standalone, debug
|
|
)
|
|
|
|
def integrate_dns_and_connectivity_results(dns_intelligence, connectivity_targets, debug=False, timeout=3):
|
|
"""Integrate DNS intelligence with connectivity test results."""
|
|
print(f"{Colors.OKBLUE}[*] Integrating DNS intelligence with connectivity results...{Colors.ENDC}")
|
|
|
|
results = {
|
|
'dns_confirmed': list(dns_intelligence['high_value_targets']),
|
|
'connectivity_confirmed': connectivity_targets,
|
|
'validated_targets': [], # DNS targets that also pass connectivity
|
|
'dns_only_targets': [], # DNS targets that don't respond to connectivity
|
|
'connectivity_only_targets': [], # Connectivity targets not in DNS
|
|
'final_target_list': [],
|
|
'validation_summary': {}
|
|
}
|
|
|
|
dns_targets = set(dns_intelligence['high_value_targets'])
|
|
conn_targets = set(connectivity_targets)
|
|
|
|
# Find overlaps and differences
|
|
overlap_targets = dns_targets.intersection(conn_targets)
|
|
dns_only = dns_targets - conn_targets
|
|
connectivity_only = conn_targets - dns_targets
|
|
|
|
print(f"{Colors.OKCYAN} DNS-discovered targets: {len(dns_targets)}{Colors.ENDC}")
|
|
print(f"{Colors.OKCYAN} Connectivity-confirmed targets: {len(conn_targets)}{Colors.ENDC}")
|
|
print(f"{Colors.OKCYAN} Overlap (DNS + connectivity): {len(overlap_targets)}{Colors.ENDC}")
|
|
print(f"{Colors.OKCYAN} DNS-only targets: {len(dns_only)}{Colors.ENDC}")
|
|
print(f"{Colors.OKCYAN} Connectivity-only targets: {len(connectivity_only)}{Colors.ENDC}")
|
|
|
|
# Validate DNS-only targets with quick connectivity test
|
|
if dns_only:
|
|
print(f"{Colors.OKBLUE}[*] Validating {len(dns_only)} DNS-only targets...{Colors.ENDC}")
|
|
|
|
with ThreadPoolExecutor(max_workers=20) as executor:
|
|
future_to_target = {
|
|
executor.submit(test_basic_connectivity, target, timeout): target
|
|
for target in dns_only
|
|
}
|
|
|
|
validated_count = 0
|
|
for future in as_completed(future_to_target):
|
|
target = future_to_target[future]
|
|
try:
|
|
is_reachable = future.result()
|
|
if is_reachable:
|
|
results['validated_targets'].append(target)
|
|
validated_count += 1
|
|
if debug:
|
|
print(f"{Colors.OKGREEN} ✓ {target} (DNS + validated){Colors.ENDC}")
|
|
else:
|
|
results['dns_only_targets'].append(target)
|
|
if debug:
|
|
print(f"{Colors.WARNING} - {target} (DNS only, no connectivity){Colors.ENDC}")
|
|
except Exception:
|
|
results['dns_only_targets'].append(target)
|
|
|
|
print(f"{Colors.OKGREEN} → {validated_count}/{len(dns_only)} DNS targets validated via connectivity{Colors.ENDC}")
|
|
|
|
# Build final target list with prioritization
|
|
results['connectivity_confirmed'] = list(conn_targets)
|
|
results['connectivity_only_targets'] = list(connectivity_only)
|
|
|
|
# Priority order for final list:
|
|
# 1. Overlap targets (DNS + connectivity confirmed) - HIGHEST priority
|
|
# 2. Validated DNS targets (DNS + newly validated) - HIGH priority
|
|
# 3. Connectivity-only targets - MEDIUM priority
|
|
# 4. DNS-only targets (DNS but no connectivity) - LOW priority
|
|
|
|
final_targets = []
|
|
|
|
# Add overlap targets (highest confidence)
|
|
final_targets.extend(sorted(overlap_targets))
|
|
|
|
# Add validated DNS targets
|
|
final_targets.extend(sorted(results['validated_targets']))
|
|
|
|
# Add connectivity-only targets
|
|
final_targets.extend(sorted(connectivity_only))
|
|
|
|
# Add DNS-only targets (might be offline but worth trying)
|
|
final_targets.extend(sorted(results['dns_only_targets']))
|
|
|
|
results['final_target_list'] = final_targets
|
|
|
|
# Generate summary
|
|
results['validation_summary'] = {
|
|
'total_unique_targets': len(final_targets),
|
|
'dns_discovered': len(dns_targets),
|
|
'connectivity_confirmed': len(conn_targets),
|
|
'high_confidence': len(overlap_targets) + len(results['validated_targets']),
|
|
'medium_confidence': len(connectivity_only),
|
|
'low_confidence': len(results['dns_only_targets']),
|
|
'coverage_improvement': len(final_targets) - max(len(dns_targets), len(conn_targets))
|
|
}
|
|
|
|
print(f"\n{Colors.OKGREEN}[+] Integration Complete:{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Final target list: {len(final_targets)} unique targets{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[*] High confidence: {results['validation_summary']['high_confidence']} targets (DNS + connectivity){Colors.ENDC}")
|
|
print(f"{Colors.WARNING}[*] Medium confidence: {results['validation_summary']['medium_confidence']} targets (connectivity only){Colors.ENDC}")
|
|
print(f"{Colors.OKCYAN}[*] Low confidence: {results['validation_summary']['low_confidence']} targets (DNS only){Colors.ENDC}")
|
|
|
|
coverage_improvement = results['validation_summary']['coverage_improvement']
|
|
if coverage_improvement > 0:
|
|
print(f"{Colors.OKGREEN}[+] Combined approach found {coverage_improvement} additional targets vs single method{Colors.ENDC}")
|
|
|
|
return results
|
|
|
|
def generate_comprehensive_reachability_reports(final_results, dns_intelligence, infrastructure,
|
|
subnet_results, decision_log, reachability_dir,
|
|
timestamp, total_time, standalone, debug):
|
|
"""Generate comprehensive reports combining DNS and connectivity intelligence."""
|
|
|
|
final_targets = final_results['final_target_list']
|
|
|
|
# 1. Main reachable targets file (prioritized)
|
|
reachable_file = os.path.join(reachability_dir, f"reachable_targets_{timestamp}.txt")
|
|
with open(reachable_file, 'w') as f:
|
|
f.write(f"# Comprehensive Network Reachability Assessment Results\n")
|
|
f.write(f"# Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"# Assessment Duration: {total_time:.2f} seconds\n")
|
|
f.write(f"# Method: DNS Intelligence + Connectivity Testing\n")
|
|
f.write(f"# Total Targets: {len(final_targets)}\n")
|
|
f.write(f"# High Confidence: {final_results['validation_summary']['high_confidence']}\n")
|
|
f.write(f"# Medium Confidence: {final_results['validation_summary']['medium_confidence']}\n")
|
|
f.write(f"# Low Confidence: {final_results['validation_summary']['low_confidence']}\n\n")
|
|
f.write(f"# Target Priority Order:\n")
|
|
f.write(f"# 1. DNS + Connectivity confirmed (lines 1-{len(final_results['validated_targets']) + len(set(final_results['dns_confirmed']).intersection(set(final_results['connectivity_confirmed'])))})\n")
|
|
f.write(f"# 2. Connectivity-only confirmed\n")
|
|
f.write(f"# 3. DNS-only targets\n\n")
|
|
|
|
for target in final_targets:
|
|
f.write(f"{target}\n")
|
|
|
|
# 2. High confidence targets only
|
|
high_confidence_file = os.path.join(reachability_dir, f"high_confidence_targets_{timestamp}.txt")
|
|
high_conf_targets = (set(final_results['dns_confirmed']).intersection(set(final_results['connectivity_confirmed'])) |
|
|
set(final_results['validated_targets']))
|
|
|
|
with open(high_confidence_file, 'w') as f:
|
|
f.write(f"# High Confidence Targets Only (DNS + Connectivity Confirmed)\n")
|
|
f.write(f"# Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"# Count: {len(high_conf_targets)}\n\n")
|
|
for target in sorted(high_conf_targets):
|
|
f.write(f"{target}\n")
|
|
|
|
# 3. Comprehensive assessment report
|
|
assessment_report = os.path.join(reachability_dir, f"comprehensive_assessment_{timestamp}.txt")
|
|
with open(assessment_report, 'w') as f:
|
|
f.write("=" * 80 + "\n")
|
|
f.write("COMPREHENSIVE NETWORK REACHABILITY ASSESSMENT\n")
|
|
f.write("=" * 80 + "\n\n")
|
|
f.write(f"Assessment Date: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"Total Duration: {total_time:.2f} seconds\n")
|
|
f.write(f"Assessment Method: DNS Intelligence + Network Connectivity Testing\n\n")
|
|
|
|
f.write("METHODOLOGY:\n")
|
|
f.write("-" * 12 + "\n")
|
|
f.write("1. DNS Intelligence Gathering:\n")
|
|
f.write(" - Zone transfer attempts\n")
|
|
f.write(" - Reverse DNS sweeps\n")
|
|
f.write(" - Forward DNS enumeration\n")
|
|
f.write("2. Network Connectivity Testing:\n")
|
|
f.write(" - Subnet-based reachability\n")
|
|
f.write(" - Infrastructure validation\n")
|
|
f.write("3. Results Integration and Validation\n\n")
|
|
|
|
f.write("DNS INTELLIGENCE RESULTS:\n")
|
|
f.write("-" * 25 + "\n")
|
|
dns_summary = dns_intelligence['dns_summary']
|
|
f.write(f"Total DNS-discovered hosts: {dns_summary['total_discovered_hosts']}\n")
|
|
f.write(f"Zone transfer hosts: {dns_summary['zone_transfer_hosts']}\n")
|
|
f.write(f"Reverse DNS hosts: {dns_summary['reverse_dns_hosts']}\n")
|
|
f.write(f"Forward DNS hosts: {dns_summary['forward_dns_hosts']}\n")
|
|
f.write(f"Discovered domains: {dns_summary['discovered_domains']}\n\n")
|
|
|
|
f.write("CONNECTIVITY TESTING RESULTS:\n")
|
|
f.write("-" * 29 + "\n")
|
|
f.write(f"Reachable subnets: {len(subnet_results['reachable_subnets'])}\n")
|
|
f.write(f"Unreachable subnets: {len(subnet_results['unreachable_subnets'])}\n")
|
|
f.write(f"Connectivity-confirmed targets: {len(final_results['connectivity_confirmed'])}\n\n")
|
|
|
|
f.write("FINAL ASSESSMENT SUMMARY:\n")
|
|
f.write("-" * 25 + "\n")
|
|
summary = final_results['validation_summary']
|
|
f.write(f"Total Unique Targets: {summary['total_unique_targets']}\n")
|
|
f.write(f"High Confidence (DNS + Connectivity): {summary['high_confidence']}\n")
|
|
f.write(f"Medium Confidence (Connectivity only): {summary['medium_confidence']}\n")
|
|
f.write(f"Low Confidence (DNS only): {summary['low_confidence']}\n")
|
|
f.write(f"Coverage Improvement: +{summary['coverage_improvement']} targets vs single method\n\n")
|
|
|
|
f.write("RECOMMENDATIONS:\n")
|
|
f.write("-" * 15 + "\n")
|
|
f.write("1. Prioritize high-confidence targets for initial scanning\n")
|
|
f.write("2. Use medium-confidence targets for comprehensive coverage\n")
|
|
f.write("3. Test low-confidence targets last (may be offline)\n")
|
|
if dns_summary['zone_transfer_hosts'] > 0:
|
|
f.write("4. Zone transfers were successful - high intelligence value\n")
|
|
if summary['coverage_improvement'] > 0:
|
|
f.write(f"5. Combined approach provided {summary['coverage_improvement']} additional targets\n")
|
|
|
|
# Print final summary
|
|
print(f"\n{Colors.OKGREEN}{'='*70}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] Comprehensive Network Reachability Assessment Complete!{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] Assessment Duration: {total_time:.2f} seconds{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] Total Targets Found: {len(final_targets)}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] High Confidence Targets: {final_results['validation_summary']['high_confidence']}{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] All targets: {reachable_file}{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] High confidence only: {high_confidence_file}{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Full assessment report: {assessment_report}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}{'='*70}{Colors.ENDC}")
|
|
|
|
return final_targets, [], [] # Compatible with existing code
|
|
|
|
def run_nmap_discovery(base_dir, targets, stealth=False, quick=False, enhanced=False, debug=False):
|
|
"""Run comprehensive port scanning with enhanced options."""
|
|
nmap_dir = os.path.join(base_dir, "scans", "nmap")
|
|
target_file = os.path.join(nmap_dir, "targets.txt")
|
|
|
|
# Write targets to file
|
|
with open(target_file, 'w') as f:
|
|
f.write('\n'.join(targets))
|
|
|
|
print(f"{Colors.OKGREEN}[+] Starting {'Enhanced ' if enhanced else ''}Nmap Port Scanning{Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Targets already validated by reachability testing{Colors.ENDC}")
|
|
|
|
# Use targets directly since they've been validated by reachability testing
|
|
alive_hosts = targets
|
|
|
|
# Write alive hosts file for consistency with existing structure
|
|
alive_file = os.path.join(nmap_dir, "alive_hosts.txt")
|
|
with open(alive_file, 'w') as f:
|
|
f.write('\n'.join(alive_hosts))
|
|
|
|
# Copy to targets directory for reference
|
|
alive_copy = os.path.join(base_dir, "targets", "alive_hosts.txt")
|
|
with open(alive_copy, 'w') as f:
|
|
f.write('\n'.join(alive_hosts))
|
|
|
|
print(f"{Colors.OKGREEN}[+] Scanning {len(alive_hosts)} validated targets{Colors.ENDC}")
|
|
|
|
# For large target lists, break into smaller chunks to avoid timeouts
|
|
chunk_size = 200 if len(alive_hosts) > 500 else len(alive_hosts)
|
|
target_chunks = [alive_hosts[i:i + chunk_size] for i in range(0, len(alive_hosts), chunk_size)]
|
|
|
|
if len(target_chunks) > 1:
|
|
print(f"{Colors.OKBLUE}[*] Breaking {len(alive_hosts)} targets into {len(target_chunks)} chunks of ~{chunk_size} for efficiency{Colors.ENDC}")
|
|
|
|
# Enhanced scanning modes
|
|
timing = "-T2" if stealth else "-T3"
|
|
|
|
# Adjust timeouts based on target count for efficiency
|
|
if len(alive_hosts) > 500:
|
|
# Very large scan - use shorter timeouts
|
|
host_timeout = "120s"
|
|
max_retries = "1"
|
|
print(f"{Colors.OKBLUE}[*] Large target set detected - using shorter timeouts for efficiency{Colors.ENDC}")
|
|
elif len(alive_hosts) > 200:
|
|
# Medium scan - moderate timeouts
|
|
host_timeout = "240s"
|
|
max_retries = "1"
|
|
else:
|
|
# Small scan - normal timeouts
|
|
host_timeout = "300s"
|
|
max_retries = "2"
|
|
|
|
if quick:
|
|
# Quick mode: Low hanging fruit ports only, skip UDP for speed
|
|
print(f"{Colors.OKBLUE}[*] Running Quick Mode: Low hanging fruit TCP ports (no UDP){Colors.ENDC}")
|
|
|
|
# Focus on most common services for quick wins
|
|
quick_ports = "21,22,23,25,53,80,110,135,139,143,443,993,995,1723,3306,3389,5432,5900,8080,8443"
|
|
|
|
# Process chunks for quick scan
|
|
for i, chunk in enumerate(target_chunks, 1):
|
|
chunk_file = os.path.join(nmap_dir, f"chunk_{i}_targets.txt")
|
|
with open(chunk_file, 'w') as f:
|
|
f.write('\n'.join(chunk))
|
|
|
|
print(f"{Colors.OKCYAN}[*] Quick scan chunk {i}/{len(target_chunks)} ({len(chunk)} targets)...{Colors.ENDC}")
|
|
tcp_cmd = f"nmap -sSV -Pn -n -p {quick_ports} {timing} --max-parallelism 50 --max-retries {max_retries} --host-timeout {host_timeout} -iL {chunk_file} -oA {os.path.join(nmap_dir, f'tcp_quick_chunk_{i}')}"
|
|
|
|
result = run_command(tcp_cmd, debug=debug, stealth=stealth)
|
|
if result is None:
|
|
print(f"{Colors.WARNING}[!] Quick scan chunk {i} timed out, continuing with next chunk...{Colors.ENDC}")
|
|
|
|
else:
|
|
# Standard mode: Top 1000 TCP + 500 UDP, then enhanced scans
|
|
print(f"{Colors.OKBLUE}[*] Running Standard Mode: Progressive scanning for fast results{Colors.ENDC}")
|
|
|
|
# Phase 1: TCP top 1000 ports in chunks
|
|
print(f"{Colors.OKBLUE}[*] Phase 1: Running TCP top 1000 ports...{Colors.ENDC}")
|
|
for i, chunk in enumerate(target_chunks, 1):
|
|
chunk_file = os.path.join(nmap_dir, f"chunk_{i}_targets.txt")
|
|
with open(chunk_file, 'w') as f:
|
|
f.write('\n'.join(chunk))
|
|
|
|
print(f"{Colors.OKCYAN}[*] TCP scan chunk {i}/{len(target_chunks)} ({len(chunk)} targets)...{Colors.ENDC}")
|
|
tcp_cmd = f"nmap -sSV -Pn -n --top-ports 1000 {timing} --max-parallelism 50 --max-retries {max_retries} --host-timeout {host_timeout} -iL {chunk_file} -oA {os.path.join(nmap_dir, f'tcp_top1000_chunk_{i}')}"
|
|
|
|
tcp_result = run_command(tcp_cmd, debug=debug, stealth=stealth)
|
|
if tcp_result is None:
|
|
print(f"{Colors.WARNING}[!] TCP chunk {i} timed out, continuing with next chunk...{Colors.ENDC}")
|
|
|
|
# Phase 2: UDP top 500 ports in chunks
|
|
print(f"{Colors.OKBLUE}[*] Phase 2: Running UDP top 500 ports...{Colors.ENDC}")
|
|
for i, chunk in enumerate(target_chunks, 1):
|
|
chunk_file = os.path.join(nmap_dir, f"chunk_{i}_targets.txt")
|
|
|
|
print(f"{Colors.OKCYAN}[*] UDP scan chunk {i}/{len(target_chunks)} ({len(chunk)} targets)...{Colors.ENDC}")
|
|
udp_cmd = f"nmap -sU --top-ports 500 {timing} --max-parallelism 25 --max-retries 1 -iL {chunk_file} -oA {os.path.join(nmap_dir, f'udp_top500_chunk_{i}')}"
|
|
|
|
udp_result = run_command(udp_cmd, debug=debug, stealth=stealth)
|
|
if udp_result is None:
|
|
print(f"{Colors.WARNING}[!] UDP chunk {i} timed out, continuing with next chunk...{Colors.ENDC}")
|
|
|
|
# Phase 3: Enhanced scans if enhanced mode enabled
|
|
if enhanced:
|
|
print(f"{Colors.OKBLUE}[*] Phase 3: Running Enhanced Comprehensive Scans{Colors.ENDC}")
|
|
|
|
for i, chunk in enumerate(target_chunks, 1):
|
|
chunk_file = os.path.join(nmap_dir, f"chunk_{i}_targets.txt")
|
|
|
|
# Full TCP port scan with comprehensive service detection
|
|
print(f"{Colors.OKCYAN}[*] Enhanced TCP scan chunk {i}/{len(target_chunks)}...{Colors.ENDC}")
|
|
tcp_full_cmd = f"nmap -sT -sV -sC -A --version-all {timing} -p- --max-parallelism 50 --max-retries {max_retries} --host-timeout 600s -iL {chunk_file} -oA {os.path.join(nmap_dir, f'tcp_full_enhanced_chunk_{i}')}"
|
|
|
|
tcp_full_result = run_command(tcp_full_cmd, debug=debug, stealth=stealth)
|
|
if tcp_full_result is None:
|
|
print(f"{Colors.WARNING}[!] Enhanced TCP chunk {i} timed out, continuing...{Colors.ENDC}")
|
|
|
|
# Comprehensive UDP scan
|
|
print(f"{Colors.OKCYAN}[*] Enhanced UDP scan chunk {i}/{len(target_chunks)}...{Colors.ENDC}")
|
|
udp_enhanced_cmd = f"nmap -sU -sV --top-ports 1000 {timing} --max-parallelism 25 --max-retries 1 -iL {chunk_file} -oA {os.path.join(nmap_dir, f'udp_top1000_enhanced_chunk_{i}')}"
|
|
|
|
udp_enhanced_result = run_command(udp_enhanced_cmd, debug=debug, stealth=stealth)
|
|
if udp_enhanced_result is None:
|
|
print(f"{Colors.WARNING}[!] Enhanced UDP chunk {i} timed out, continuing...{Colors.ENDC}")
|
|
|
|
# Phase 4: Additional scanning phases (post-discovery enumeration)
|
|
print(f"{Colors.OKBLUE}[*] Phase 4: Post-Discovery Service Enumeration{Colors.ENDC}")
|
|
|
|
# DNS enumeration
|
|
print(f"{Colors.OKCYAN}[*] Running DNS enumeration...{Colors.ENDC}")
|
|
run_enhanced_dns_enumeration(base_dir, alive_hosts, debug)
|
|
|
|
# SMB enumeration
|
|
print(f"{Colors.OKCYAN}[*] Running SMB enumeration...{Colors.ENDC}")
|
|
run_enhanced_smb_enumeration(base_dir, alive_hosts, debug)
|
|
|
|
# Web enumeration
|
|
print(f"{Colors.OKCYAN}[*] Running web enumeration...{Colors.ENDC}")
|
|
run_enhanced_web_enumeration(base_dir, alive_hosts, stealth, debug)
|
|
|
|
# Final summary
|
|
print(f"\n{Colors.OKGREEN}[+] Nmap Discovery Phase Complete{Colors.ENDC}")
|
|
if len(target_chunks) > 1:
|
|
print(f"{Colors.OKBLUE}[*] Processed {len(target_chunks)} chunks covering {len(alive_hosts)} targets{Colors.ENDC}")
|
|
|
|
# Count completed scan files
|
|
scan_files = list(Path(nmap_dir).glob("*.gnmap"))
|
|
print(f"{Colors.OKBLUE}[*] Generated {len(scan_files)} nmap result files{Colors.ENDC}")
|
|
|
|
# Parse and summarize discovered services
|
|
discovered_services = discover_services_from_nmap(base_dir)
|
|
total_services = sum(len(services) for services in discovered_services.values())
|
|
hosts_with_services = len(discovered_services)
|
|
|
|
if hosts_with_services > 0:
|
|
print(f"{Colors.OKGREEN}[*] Discovered {total_services} services across {hosts_with_services} responsive hosts{Colors.ENDC}")
|
|
|
|
# Show top service types
|
|
service_counts = {}
|
|
for host_services in discovered_services.values():
|
|
for service in host_services:
|
|
service_name = service['service']
|
|
service_counts[service_name] = service_counts.get(service_name, 0) + 1
|
|
|
|
top_services = sorted(service_counts.items(), key=lambda x: x[1], reverse=True)[:5]
|
|
if top_services:
|
|
print(f"{Colors.OKBLUE}[*] Top services found: {', '.join([f'{svc}({count})' for svc, count in top_services])}{Colors.ENDC}")
|
|
else:
|
|
print(f"{Colors.WARNING}[!] No responsive hosts with open ports discovered{Colors.ENDC}")
|
|
print(f"{Colors.OKCYAN}[*] This could be due to: firewalls, timeouts, or network filtering{Colors.ENDC}")
|
|
|
|
return alive_hosts
|
|
|
|
def run_enhanced_web_enumeration(base_dir, targets, stealth=False, debug=False):
|
|
"""Enhanced web enumeration with multiple tools."""
|
|
web_dir = os.path.join(base_dir, "scans", "web")
|
|
|
|
print(f"{Colors.OKGREEN}[+] Starting Enhanced Web Enumeration{Colors.ENDC}")
|
|
|
|
# Discover web services from nmap results
|
|
services = discover_services_from_nmap(base_dir)
|
|
web_services = []
|
|
|
|
for ip, service_list in services.items():
|
|
for service in service_list:
|
|
if (service['service'] in ['http', 'https', 'http-proxy', 'ssl/http'] or
|
|
service['port'] in ['80', '443', '8080', '8443', '8000', '8888']):
|
|
|
|
protocol = "https" if service['ssl'] or service['port'] in ['443', '8443'] else "http"
|
|
web_services.append({
|
|
'ip': ip,
|
|
'port': service['port'],
|
|
'protocol': protocol,
|
|
'service': service['service']
|
|
})
|
|
|
|
if not web_services:
|
|
print(f"{Colors.WARNING}[!] No web services identified{Colors.ENDC}")
|
|
return
|
|
|
|
print(f"{Colors.OKGREEN}[+] Found {len(web_services)} web services for enhanced enumeration{Colors.ENDC}")
|
|
|
|
for web_service in web_services:
|
|
ip = web_service['ip']
|
|
port = web_service['port']
|
|
protocol = web_service['protocol']
|
|
base_url = f"{protocol}://{ip}:{port}"
|
|
|
|
print(f"{Colors.OKCYAN}[*] Enhanced enumeration of {base_url}{Colors.ENDC}")
|
|
|
|
# Create service-specific directory
|
|
service_dir = os.path.join(web_dir, f"{protocol}_{port}")
|
|
os.makedirs(service_dir, exist_ok=True)
|
|
|
|
# 1. Basic HTTP Information
|
|
print(f"{Colors.OKCYAN}[*] Gathering basic HTTP information...{Colors.ENDC}")
|
|
|
|
# Curl for headers and basic info
|
|
curl_cmd = f"curl -sSikL --max-time 10 {base_url}/"
|
|
curl_output = os.path.join(service_dir, f"curl_headers_{ip}_{port}.txt")
|
|
run_command(curl_cmd, curl_output, debug=debug, stealth=stealth)
|
|
|
|
# Curl robots.txt
|
|
robots_cmd = f"curl -sSik --max-time 10 {base_url}/robots.txt"
|
|
robots_output = os.path.join(service_dir, f"robots_{ip}_{port}.txt")
|
|
run_command(robots_cmd, robots_output, debug=debug, stealth=stealth)
|
|
|
|
# 2. WhatWeb for technology identification
|
|
which_whatweb = run_command("which whatweb", debug=debug)
|
|
if which_whatweb and which_whatweb.returncode == 0:
|
|
print(f"{Colors.OKCYAN}[*] Running WhatWeb technology identification...{Colors.ENDC}")
|
|
whatweb_cmd = f"whatweb --color=never --no-errors -a 3 -v {base_url}"
|
|
whatweb_output = os.path.join(service_dir, f"whatweb_{ip}_{port}.txt")
|
|
run_command(whatweb_cmd, whatweb_output, debug=debug, stealth=stealth)
|
|
|
|
# 3. Nikto vulnerability scanning
|
|
which_nikto = run_command("which nikto", debug=debug)
|
|
if which_nikto and which_nikto.returncode == 0:
|
|
print(f"{Colors.OKCYAN}[*] Running Nikto vulnerability scan...{Colors.ENDC}")
|
|
nikto_cmd = f"nikto -ask=no -h {base_url}"
|
|
if stealth:
|
|
nikto_cmd += " -T 2"
|
|
nikto_output = os.path.join(service_dir, f"nikto_{ip}_{port}.txt")
|
|
run_command(nikto_cmd, nikto_output, debug=debug, stealth=stealth)
|
|
|
|
# 4. Directory brute forcing with gobuster (prioritized) and feroxbuster
|
|
print(f"{Colors.OKCYAN}[*] Running directory enumeration...{Colors.ENDC}")
|
|
|
|
# Primary wordlists to try (in order of preference)
|
|
wordlists = [
|
|
"/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt",
|
|
"/usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt",
|
|
"/usr/share/wordlists/dirb/common.txt",
|
|
"/usr/share/seclists/Discovery/Web-Content/common.txt"
|
|
]
|
|
|
|
# Find the first available wordlist
|
|
wordlist_to_use = None
|
|
for wordlist in wordlists:
|
|
if os.path.exists(wordlist):
|
|
wordlist_to_use = wordlist
|
|
break
|
|
|
|
if not wordlist_to_use:
|
|
print(f"{Colors.WARNING}[!] No wordlists found for directory enumeration{Colors.ENDC}")
|
|
wordlist_to_use = "/usr/share/wordlists/dirb/common.txt" # fallback
|
|
|
|
# Try directory enumeration tools in priority order
|
|
dir_tools = [
|
|
("gobuster", f"gobuster dir -u {base_url}/ -w {wordlist_to_use} -x txt,html,php,asp,aspx,jsp,xml,js,css,zip,tar,gz,bak,old,log -t 10 -k --no-error -q -o"),
|
|
("feroxbuster", f"feroxbuster -u {base_url}/ -t 10 -w {wordlist_to_use} -x txt,html,php,asp,aspx,jsp,xml,js,css,zip,tar,gz,bak,old,log -v -k -q -o")
|
|
]
|
|
|
|
# Adjust for stealth mode
|
|
if stealth:
|
|
dir_tools = [
|
|
("gobuster", f"gobuster dir -u {base_url}/ -w {wordlist_to_use} -x txt,html,php,asp,aspx,jsp,xml,js,css,zip,tar,gz,bak,old,log -t 5 -k --no-error -q --delay 200ms -o"),
|
|
("feroxbuster", f"feroxbuster -u {base_url}/ -t 5 -w {wordlist_to_use} -x txt,html,php,asp,aspx,jsp,xml,js,css,zip,tar,gz,bak,old,log -v -k -q --rate-limit 10 -o")
|
|
]
|
|
|
|
for tool_name, tool_cmd in dir_tools:
|
|
which_tool = run_command(f"which {tool_name}", debug=debug)
|
|
if which_tool and which_tool.returncode == 0:
|
|
print(f"{Colors.OKCYAN}[*] Running {tool_name} directory enumeration...{Colors.ENDC}")
|
|
tool_output = os.path.join(service_dir, f"{tool_name}_{ip}_{port}.txt")
|
|
full_cmd = f"{tool_cmd} {tool_output}"
|
|
run_command(full_cmd, debug=debug, stealth=stealth)
|
|
break
|
|
|
|
# 5. SSL/TLS Analysis if HTTPS
|
|
if protocol == "https":
|
|
print(f"{Colors.OKCYAN}[*] Running SSL/TLS analysis...{Colors.ENDC}")
|
|
|
|
# SSLScan
|
|
which_sslscan = run_command("which sslscan", debug=debug)
|
|
if which_sslscan and which_sslscan.returncode == 0:
|
|
sslscan_cmd = f"sslscan --show-certificate --no-colour {ip}:{port}"
|
|
sslscan_output = os.path.join(service_dir, f"sslscan_{ip}_{port}.txt")
|
|
run_command(sslscan_cmd, sslscan_output, debug=debug, stealth=stealth)
|
|
|
|
# TestSSL.sh if available
|
|
which_testssl = run_command("which testssl.sh", debug=debug)
|
|
if which_testssl and which_testssl.returncode == 0:
|
|
testssl_cmd = f"testssl.sh {ip}:{port}"
|
|
testssl_output = os.path.join(service_dir, f"testssl_{ip}_{port}.txt")
|
|
run_command(testssl_cmd, testssl_output, debug=debug, stealth=stealth)
|
|
|
|
# 6. Add manual commands for further testing
|
|
manual_commands = [
|
|
f"# Manual enumeration commands for {base_url}",
|
|
f"wpscan --url {base_url}/ --enumerate vp,vt,tt,cb,dbe,u,m",
|
|
f"cmsmap -t {base_url}/",
|
|
f"python3 /opt/dirsearch/dirsearch.py -u {base_url}/ -e *",
|
|
f"ffuf -u {base_url}/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt",
|
|
f"gobuster dir -u {base_url}/ -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -x txt,html,php,asp,aspx,jsp -t 50",
|
|
f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip} http-post-form '/login.php:username=^USER^&password=^PASS^:invalid'",
|
|
f"sqlmap -u '{base_url}/?id=1' --batch --banner",
|
|
f"nuclei -u {base_url} -t /root/nuclei-templates/"
|
|
]
|
|
|
|
add_manual_command(base_dir, f"Web Service {base_url}", manual_commands)
|
|
|
|
def run_enhanced_smb_enumeration(base_dir, targets, debug=False):
|
|
"""Enhanced SMB enumeration with multiple tools and techniques."""
|
|
smb_dir = os.path.join(base_dir, "scans", "smb")
|
|
|
|
print(f"{Colors.OKGREEN}[+] Starting Enhanced SMB Enumeration{Colors.ENDC}")
|
|
|
|
# Discover SMB services
|
|
services = discover_services_from_nmap(base_dir)
|
|
smb_targets = []
|
|
|
|
for ip, service_list in services.items():
|
|
for service in service_list:
|
|
if (service['service'] in ['microsoft-ds', 'smb', 'netbios-ssn'] or
|
|
service['port'] in ['139', '445']):
|
|
if ip not in smb_targets:
|
|
smb_targets.append(ip)
|
|
|
|
if not smb_targets:
|
|
print(f"{Colors.WARNING}[!] No SMB services identified{Colors.ENDC}")
|
|
return
|
|
|
|
print(f"{Colors.OKGREEN}[+] Found {len(smb_targets)} SMB targets for enhanced enumeration{Colors.ENDC}")
|
|
|
|
for target in smb_targets:
|
|
print(f"{Colors.OKCYAN}[*] Enhanced SMB enumeration of {target}{Colors.ENDC}")
|
|
|
|
# 1. Enum4Linux comprehensive enumeration
|
|
which_enum4linux = run_command("which enum4linux", debug=debug)
|
|
if which_enum4linux and which_enum4linux.returncode == 0:
|
|
print(f"{Colors.OKCYAN}[*] Running enum4linux comprehensive scan...{Colors.ENDC}")
|
|
enum4linux_cmd = f"enum4linux -a -M -l -d {target}"
|
|
enum4linux_output = os.path.join(smb_dir, f"enum4linux_{target}.txt")
|
|
run_command(enum4linux_cmd, enum4linux_output, debug=debug)
|
|
|
|
# 2. SMBClient share enumeration
|
|
which_smbclient = run_command("which smbclient", debug=debug)
|
|
if which_smbclient and which_smbclient.returncode == 0:
|
|
print(f"{Colors.OKCYAN}[*] Running smbclient share enumeration...{Colors.ENDC}")
|
|
smbclient_cmd = f"smbclient -L //{target} -N -I {target}"
|
|
smbclient_output = os.path.join(smb_dir, f"smbclient_{target}.txt")
|
|
run_command(smbclient_cmd, smbclient_output, debug=debug)
|
|
|
|
# 3. SMBMap detailed enumeration
|
|
which_smbmap = run_command("which smbmap", debug=debug)
|
|
if which_smbmap and which_smbmap.returncode == 0:
|
|
print(f"{Colors.OKCYAN}[*] Running smbmap detailed enumeration...{Colors.ENDC}")
|
|
|
|
# Share permissions
|
|
smbmap_cmd1 = f"smbmap -H {target} -u null -p ''"
|
|
smbmap_output1 = os.path.join(smb_dir, f"smbmap_shares_{target}.txt")
|
|
run_command(smbmap_cmd1, smbmap_output1, debug=debug)
|
|
|
|
# Recursive listing
|
|
smbmap_cmd2 = f"smbmap -H {target} -u null -p '' -r"
|
|
smbmap_output2 = os.path.join(smb_dir, f"smbmap_recursive_{target}.txt")
|
|
run_command(smbmap_cmd2, smbmap_output2, debug=debug)
|
|
|
|
# 4. NBTScan NetBIOS enumeration
|
|
which_nbtscan = run_command("which nbtscan", debug=debug)
|
|
if which_nbtscan and which_nbtscan.returncode == 0:
|
|
print(f"{Colors.OKCYAN}[*] Running nbtscan NetBIOS enumeration...{Colors.ENDC}")
|
|
nbtscan_cmd = f"nbtscan -rvh {target}"
|
|
nbtscan_output = os.path.join(smb_dir, f"nbtscan_{target}.txt")
|
|
run_command(nbtscan_cmd, nbtscan_output, debug=debug)
|
|
|
|
# 5. RPCClient enumeration
|
|
which_rpcclient = run_command("which rpcclient", debug=debug)
|
|
if which_rpcclient and which_rpcclient.returncode == 0:
|
|
print(f"{Colors.OKCYAN}[*] Running rpcclient enumeration...{Colors.ENDC}")
|
|
rpcclient_cmd = f'echo "enumdomusers; enumdomgroups; querydominfo; exit" | rpcclient -U "" {target}'
|
|
rpcclient_output = os.path.join(smb_dir, f"rpcclient_{target}.txt")
|
|
run_command(rpcclient_cmd, rpcclient_output, debug=debug)
|
|
|
|
# 6. Advanced Nmap SMB scripts
|
|
print(f"{Colors.OKCYAN}[*] Running advanced Nmap SMB scripts...{Colors.ENDC}")
|
|
nmap_smb_cmd = f"nmap -p 139,445 --script 'smb-os-discovery,smb-security-mode,smb-enum-shares,smb-enum-users,smb-enum-domains,smb-enum-groups,smb-enum-processes,smb-enum-sessions,smb-server-stats' {target}"
|
|
nmap_smb_output = os.path.join(smb_dir, f"nmap_smb_advanced_{target}.txt")
|
|
run_command(nmap_smb_cmd, nmap_smb_output, debug=debug)
|
|
|
|
# 7. Add manual commands
|
|
manual_commands = [
|
|
f"# Manual SMB enumeration commands for {target}",
|
|
f"crackmapexec smb {target} --shares",
|
|
f"crackmapexec smb {target} --users",
|
|
f"crackmapexec smb {target} --groups",
|
|
f"crackmapexec smb {target} --pass-pol",
|
|
f"impacket-samrdump {target}",
|
|
f"impacket-rpcdump {target}",
|
|
f"smbclient //{target}/SHARE -U username%password",
|
|
f"mount -t cifs //{target}/SHARE /mnt/smb -o username=,password=",
|
|
f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {target} smb"
|
|
]
|
|
|
|
add_manual_command(base_dir, f"SMB Service {target}", manual_commands)
|
|
|
|
def run_enhanced_database_enumeration(base_dir, targets, debug=False):
|
|
"""Enhanced database enumeration for various database services."""
|
|
db_dir = os.path.join(base_dir, "scans", "databases")
|
|
os.makedirs(db_dir, exist_ok=True)
|
|
|
|
print(f"{Colors.OKGREEN}[+] Starting Enhanced Database Enumeration{Colors.ENDC}")
|
|
|
|
# Discover database services
|
|
services = discover_services_from_nmap(base_dir)
|
|
db_services = {}
|
|
|
|
for ip, service_list in services.items():
|
|
for service in service_list:
|
|
service_name = service['service'].lower()
|
|
port = service['port']
|
|
|
|
# Identify database services
|
|
if any(db in service_name for db in ['mysql', 'mssql', 'postgresql', 'oracle', 'mongodb', 'redis']):
|
|
if ip not in db_services:
|
|
db_services[ip] = []
|
|
db_services[ip].append({
|
|
'service': service_name,
|
|
'port': port,
|
|
'protocol': service['protocol']
|
|
})
|
|
|
|
if not db_services:
|
|
print(f"{Colors.WARNING}[!] No database services identified{Colors.ENDC}")
|
|
return
|
|
|
|
print(f"{Colors.OKGREEN}[+] Found database services on {len(db_services)} hosts{Colors.ENDC}")
|
|
|
|
for ip, services in db_services.items():
|
|
for service in services:
|
|
service_name = service['service']
|
|
port = service['port']
|
|
|
|
print(f"{Colors.OKCYAN}[*] Enhanced enumeration of {service_name} on {ip}:{port}{Colors.ENDC}")
|
|
|
|
# MySQL enumeration
|
|
if 'mysql' in service_name:
|
|
print(f"{Colors.OKCYAN}[*] Running MySQL enumeration...{Colors.ENDC}")
|
|
|
|
# Nmap MySQL scripts
|
|
mysql_nmap_cmd = f"nmap -p {port} --script 'mysql-audit,mysql-databases,mysql-dump-hashes,mysql-empty-password,mysql-enum,mysql-info,mysql-query,mysql-users,mysql-variables,mysql-vuln-cve2012-2122' {ip}"
|
|
mysql_output = os.path.join(db_dir, f"mysql_nmap_{ip}_{port}.txt")
|
|
run_command(mysql_nmap_cmd, mysql_output, debug=debug)
|
|
|
|
# Manual commands
|
|
mysql_manual = [
|
|
f"# MySQL enumeration for {ip}:{port}",
|
|
f"mysql -h {ip} -P {port} -u root -p",
|
|
f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip} mysql",
|
|
f"ncrack -v --user root -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip}:{port}"
|
|
]
|
|
add_manual_command(base_dir, f"MySQL {ip}:{port}", mysql_manual)
|
|
|
|
# MSSQL enumeration
|
|
elif 'mssql' in service_name or 'ms-sql' in service_name:
|
|
print(f"{Colors.OKCYAN}[*] Running MSSQL enumeration...{Colors.ENDC}")
|
|
|
|
# Nmap MSSQL scripts
|
|
mssql_nmap_cmd = f"nmap -p {port} --script 'ms-sql-info,ms-sql-empty-password,ms-sql-xp-cmdshell,ms-sql-config,ms-sql-ntlm-info,ms-sql-tables,ms-sql-hasdbaccess,ms-sql-query' {ip}"
|
|
mssql_output = os.path.join(db_dir, f"mssql_nmap_{ip}_{port}.txt")
|
|
run_command(mssql_nmap_cmd, mssql_output, debug=debug)
|
|
|
|
# Manual commands
|
|
mssql_manual = [
|
|
f"# MSSQL enumeration for {ip}:{port}",
|
|
f"impacket-mssqlclient sa@{ip} -port {port}",
|
|
f"sqsh -S {ip}:{port} -U sa -P",
|
|
f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip} mssql"
|
|
]
|
|
add_manual_command(base_dir, f"MSSQL {ip}:{port}", mssql_manual)
|
|
|
|
# PostgreSQL enumeration
|
|
elif 'postgresql' in service_name:
|
|
print(f"{Colors.OKCYAN}[*] Running PostgreSQL enumeration...{Colors.ENDC}")
|
|
|
|
# Nmap PostgreSQL scripts
|
|
pgsql_nmap_cmd = f"nmap -p {port} --script 'pgsql-brute' {ip}"
|
|
pgsql_output = os.path.join(db_dir, f"postgresql_nmap_{ip}_{port}.txt")
|
|
run_command(pgsql_nmap_cmd, pgsql_output, debug=debug)
|
|
|
|
# Manual commands
|
|
pgsql_manual = [
|
|
f"# PostgreSQL enumeration for {ip}:{port}",
|
|
f"psql -h {ip} -p {port} -U postgres",
|
|
f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip} postgres"
|
|
]
|
|
add_manual_command(base_dir, f"PostgreSQL {ip}:{port}", pgsql_manual)
|
|
|
|
# MongoDB enumeration
|
|
elif 'mongodb' in service_name or 'mongod' in service_name:
|
|
print(f"{Colors.OKCYAN}[*] Running MongoDB enumeration...{Colors.ENDC}")
|
|
|
|
# Nmap MongoDB scripts
|
|
mongo_nmap_cmd = f"nmap -p {port} --script 'mongodb-databases,mongodb-info' {ip}"
|
|
mongo_output = os.path.join(db_dir, f"mongodb_nmap_{ip}_{port}.txt")
|
|
run_command(mongo_nmap_cmd, mongo_output, debug=debug)
|
|
|
|
# Manual commands
|
|
mongo_manual = [
|
|
f"# MongoDB enumeration for {ip}:{port}",
|
|
f"mongo {ip}:{port}",
|
|
f"mongo {ip}:{port}/admin --eval 'db.runCommand(\"listCollections\")'",
|
|
f"mongo {ip}:{port} --eval 'show dbs'"
|
|
]
|
|
add_manual_command(base_dir, f"MongoDB {ip}:{port}", mongo_manual)
|
|
|
|
# Redis enumeration
|
|
elif 'redis' in service_name:
|
|
print(f"{Colors.OKCYAN}[*] Running Redis enumeration...{Colors.ENDC}")
|
|
|
|
# Nmap Redis scripts
|
|
redis_nmap_cmd = f"nmap -p {port} --script 'redis-info' {ip}"
|
|
redis_output = os.path.join(db_dir, f"redis_nmap_{ip}_{port}.txt")
|
|
run_command(redis_nmap_cmd, redis_output, debug=debug)
|
|
|
|
# Redis-cli enumeration
|
|
which_redis = run_command("which redis-cli", debug=debug)
|
|
if which_redis and which_redis.returncode == 0:
|
|
redis_info_cmd = f"redis-cli -h {ip} -p {port} INFO"
|
|
redis_info_output = os.path.join(db_dir, f"redis_info_{ip}_{port}.txt")
|
|
run_command(redis_info_cmd, redis_info_output, debug=debug)
|
|
|
|
# Manual commands
|
|
redis_manual = [
|
|
f"# Redis enumeration for {ip}:{port}",
|
|
f"redis-cli -h {ip} -p {port}",
|
|
f"redis-cli -h {ip} -p {port} CONFIG GET '*'",
|
|
f"redis-cli -h {ip} -p {port} INFO",
|
|
f"redis-cli -h {ip} -p {port} CLIENT LIST"
|
|
]
|
|
add_manual_command(base_dir, f"Redis {ip}:{port}", redis_manual)
|
|
|
|
def run_enhanced_dns_enumeration(base_dir, targets, debug=False):
|
|
"""Enhanced DNS enumeration with subdomain discovery and zone transfers."""
|
|
dns_dir = os.path.join(base_dir, "scans", "dns")
|
|
|
|
print(f"{Colors.OKGREEN}[+] Starting Enhanced DNS Enumeration{Colors.ENDC}")
|
|
|
|
# Classify targets and discover domains
|
|
rfc1918_networks, non_rfc1918_ips, hostnames = classify_network_ranges(targets)
|
|
|
|
# Discover additional domains from reverse lookups
|
|
discovered_domains = set(hostnames)
|
|
|
|
# Parse DNS output files for additional domains
|
|
for dns_file in Path(dns_dir).glob("*.txt"):
|
|
try:
|
|
with open(dns_file, 'r') as f:
|
|
content = f.read()
|
|
domain_patterns = [
|
|
r'([a-zA-Z0-9-]+\.(?:[a-zA-Z]{2,})+)',
|
|
r'([a-zA-Z0-9-]+\.(?:local|corp|internal|lan|domain|ad))',
|
|
]
|
|
|
|
for pattern in domain_patterns:
|
|
matches = re.findall(pattern, content, re.IGNORECASE)
|
|
for match in matches:
|
|
if '.' in match and not match.startswith('.'):
|
|
domain = match.strip('.')
|
|
if len(domain.split('.')) >= 2:
|
|
discovered_domains.add(domain)
|
|
except Exception as e:
|
|
if debug:
|
|
print(f"{Colors.WARNING}[!] Error parsing {dns_file}: {e}{Colors.ENDC}")
|
|
|
|
# Enhanced subdomain enumeration
|
|
if discovered_domains:
|
|
print(f"{Colors.OKBLUE}[*] Enhanced subdomain enumeration for {len(discovered_domains)} domains{Colors.ENDC}")
|
|
|
|
subdomain_dir = os.path.join(dns_dir, "subdomains")
|
|
os.makedirs(subdomain_dir, exist_ok=True)
|
|
|
|
for domain in discovered_domains:
|
|
if len(domain.split('.')) >= 2:
|
|
print(f"{Colors.OKCYAN}[*] Comprehensive subdomain enumeration for {domain}{Colors.ENDC}")
|
|
safe_domain = domain.replace('.', '_')
|
|
|
|
# Multiple subdomain enumeration techniques
|
|
subdomain_tools = [
|
|
("sublist3r", f"sublist3r -d {domain} -o"),
|
|
("amass", f"amass enum -d {domain} -o"),
|
|
("subfinder", f"subfinder -d {domain} -o"),
|
|
("assetfinder", f"assetfinder --subs-only {domain}")
|
|
]
|
|
|
|
for tool_name, tool_cmd in subdomain_tools:
|
|
which_tool = run_command(f"which {tool_name}", debug=debug)
|
|
if which_tool and which_tool.returncode == 0:
|
|
print(f"{Colors.OKCYAN}[*] Running {tool_name} subdomain enumeration...{Colors.ENDC}")
|
|
tool_output = os.path.join(subdomain_dir, f"{tool_name}_{safe_domain}.txt")
|
|
full_cmd = f"{tool_cmd} {tool_output}"
|
|
run_command(full_cmd, debug=debug)
|
|
|
|
# Add manual subdomain commands
|
|
subdomain_manual = [
|
|
f"# Advanced subdomain enumeration for {domain}",
|
|
f"gobuster dns -d {domain} -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 50",
|
|
f"python3 /opt/Sublist3r/sublist3r.py -d {domain} -b -t 100",
|
|
f"amass enum -passive -d {domain}",
|
|
f"curl -s 'https://dns.bufferover.run/dns?q=.{domain}' | jq -r .FDNS_A[]",
|
|
f"theHarvester -d {domain} -b all",
|
|
f"subfinder -d {domain} -all -recursive",
|
|
f"assetfinder --subs-only {domain} | sort -u"
|
|
]
|
|
add_manual_command(base_dir, f"Subdomain Enumeration {domain}", subdomain_manual)
|
|
|
|
def run_quick_reconnaissance(base_dir, targets, debug=False):
|
|
"""Run quick reconnaissance scans for immediate results on pre-verified targets."""
|
|
print(f"{Colors.OKGREEN}[+] Phase 1: Quick Reconnaissance (Fast Results){Colors.ENDC}")
|
|
print(f"{Colors.OKBLUE}[*] Skipping connectivity checks - targets already verified by reachability testing{Colors.ENDC}")
|
|
|
|
# Since targets are already verified as reachable, jump straight to port scanning
|
|
# Fast top ports scan on verified hosts
|
|
print(f"{Colors.OKBLUE}[*] Running fast top ports scan on {len(targets)} verified hosts...{Colors.ENDC}")
|
|
|
|
# Limit to first 10 targets for quick results
|
|
quick_targets = targets[:10] if len(targets) > 10 else targets
|
|
|
|
fast_ports_cmd = ["nmap", "-n", "--top-ports", "100", "--max-retries", "1",
|
|
"--max-rtt-timeout", "500ms", "--max-scan-delay", "5ms", "-Pn"] + quick_targets
|
|
try:
|
|
result = subprocess.run(fast_ports_cmd, capture_output=True, text=True, timeout=120)
|
|
if result.returncode == 0:
|
|
# Parse for open ports
|
|
current_host = None
|
|
quick_results = []
|
|
for line in result.stdout.split('\n'):
|
|
if "Nmap scan report for" in line:
|
|
current_host = line.split()[-1]
|
|
quick_results.append(current_host)
|
|
elif "/tcp" in line and "open" in line:
|
|
port = line.split('/')[0]
|
|
service = line.split()[-1] if len(line.split()) > 2 else "unknown"
|
|
print(f"{Colors.OKGREEN}[+] Quick service found: {current_host}:{port} ({service}){Colors.ENDC}")
|
|
return quick_results
|
|
except subprocess.TimeoutExpired:
|
|
print(f"{Colors.WARNING}[!] Fast port scan timed out, continuing...{Colors.ENDC}")
|
|
|
|
return targets # Return original targets if scan fails
|
|
|
|
def run_enhanced_enumeration(base_dir, targets, stealth=False, debug=False):
|
|
"""Run comprehensive enhanced enumeration across all discovered services."""
|
|
print(f"{Colors.OKGREEN}[+] Starting Enhanced Service Enumeration{Colors.ENDC}")
|
|
|
|
# Enhanced enumeration modules
|
|
enumeration_modules = [
|
|
("Enhanced Web Enumeration", run_enhanced_web_enumeration),
|
|
("Enhanced SMB Enumeration", run_enhanced_smb_enumeration),
|
|
("Enhanced Database Enumeration", run_enhanced_database_enumeration),
|
|
("Enhanced DNS Enumeration", run_enhanced_dns_enumeration),
|
|
]
|
|
|
|
for module_name, module_func in enumeration_modules:
|
|
try:
|
|
print(f"\n{Colors.OKBLUE}[*] Running {module_name}...{Colors.ENDC}")
|
|
if module_name == "Enhanced DNS Enumeration":
|
|
module_func(base_dir, targets, debug=debug)
|
|
else:
|
|
module_func(base_dir, targets, debug=debug)
|
|
except Exception as e:
|
|
print(f"{Colors.FAIL}[!] Error in {module_name}: {e}{Colors.ENDC}")
|
|
if debug:
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
def generate_summary_report(base_dir):
|
|
"""Generate a comprehensive summary report of all findings."""
|
|
reports_dir = os.path.join(base_dir, "reports")
|
|
report_file = os.path.join(reports_dir, "trashpanda_summary.txt")
|
|
|
|
print(f"{Colors.OKGREEN}[+] Generating comprehensive summary report: {report_file}{Colors.ENDC}")
|
|
|
|
with open(report_file, 'w') as f:
|
|
f.write("=" * 80 + "\n")
|
|
f.write("TRASHPANDA COMPREHENSIVE PENETRATION TESTING REPORT\n")
|
|
f.write("=" * 80 + "\n\n")
|
|
f.write(f"Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"Operator: operator\n")
|
|
f.write(f"Engagement Directory: {base_dir}\n\n")
|
|
|
|
# Enhanced directory structure overview
|
|
f.write("DIRECTORY STRUCTURE:\n")
|
|
f.write("-" * 20 + "\n")
|
|
main_dirs = ["tools", "scans", "logs", "loot", "payloads", "targets",
|
|
"screenshots", "reports", "notes", "exploits", "wordlists", "pcaps"]
|
|
|
|
for main_dir in main_dirs:
|
|
dir_path = os.path.join(base_dir, main_dir)
|
|
if os.path.exists(dir_path):
|
|
file_count = len([f for f in os.listdir(dir_path)
|
|
if os.path.isfile(os.path.join(dir_path, f))])
|
|
subdir_count = len([d for d in os.listdir(dir_path)
|
|
if os.path.isdir(os.path.join(dir_path, d))])
|
|
f.write(f"├── {main_dir:15} ({file_count} files, {subdir_count} subdirs)\n")
|
|
|
|
f.write("\n")
|
|
|
|
# Enhanced scan results summary
|
|
f.write("SCAN RESULTS SUMMARY:\n")
|
|
f.write("-" * 21 + "\n")
|
|
scans_dir = os.path.join(base_dir, "scans")
|
|
if os.path.exists(scans_dir):
|
|
scan_types = ["nmap", "dns", "snmp", "smb", "web", "ssl", "vulns",
|
|
"databases", "ldap", "ftp", "ssh", "reachability"]
|
|
for scan_type in scan_types:
|
|
scan_dir = os.path.join(scans_dir, scan_type)
|
|
if os.path.exists(scan_dir):
|
|
file_count = len([f for f in os.listdir(scan_dir)
|
|
if os.path.isfile(os.path.join(scan_dir, f))])
|
|
f.write(f"├── {scan_type.upper():12} scans: {file_count} files\n")
|
|
|
|
f.write("\n")
|
|
|
|
# Service discovery summary
|
|
services = discover_services_from_nmap(base_dir)
|
|
if services:
|
|
f.write("DISCOVERED SERVICES:\n")
|
|
f.write("-" * 20 + "\n")
|
|
for ip, service_list in services.items():
|
|
f.write(f"Target: {ip}\n")
|
|
for service in service_list:
|
|
ssl_indicator = " (SSL)" if service['ssl'] else ""
|
|
f.write(f" ├── {service['protocol']}/{service['port']} - {service['service']}{ssl_indicator}\n")
|
|
f.write("\n")
|
|
|
|
# Key files inventory
|
|
f.write("KEY FILES:\n")
|
|
f.write("-" * 10 + "\n")
|
|
|
|
key_files = [
|
|
("targets/alive_hosts.txt", "Live hosts discovered"),
|
|
("scans/reachability/reachable_targets_*.txt", "Network reachability results"),
|
|
("scans/nmap/*.gnmap", "Nmap scan results"),
|
|
("scans/dns/dns_enumeration_summary.txt", "DNS enumeration summary"),
|
|
("scans/snmp/snmp_enumeration_summary.txt", "SNMP enumeration summary"),
|
|
("scans/_manual_commands.txt", "Manual commands for further testing"),
|
|
("pcaps/capture_*.pcap", "Network traffic capture"),
|
|
("logs/engagement.log", "Engagement activity log")
|
|
]
|
|
|
|
for file_pattern, description in key_files:
|
|
file_path = os.path.join(base_dir, file_pattern.replace("*", ""))
|
|
if "*" in file_pattern:
|
|
import glob
|
|
matches = glob.glob(os.path.join(base_dir, file_pattern))
|
|
if matches:
|
|
f.write(f"✓ {description}: {len(matches)} file(s)\n")
|
|
else:
|
|
f.write(f"✗ {description}: Not found\n")
|
|
elif os.path.exists(file_path):
|
|
f.write(f"✓ {description}: Available\n")
|
|
else:
|
|
f.write(f"✗ {description}: Not found\n")
|
|
|
|
f.write("\n" + "=" * 80 + "\n")
|
|
f.write("RECOMMENDED NEXT STEPS:\n")
|
|
f.write("1. Review manual commands in scans/_manual_commands.txt\n")
|
|
f.write("2. Analyze discovered services for vulnerabilities\n")
|
|
f.write("3. Check web services for common web application vulnerabilities\n")
|
|
f.write("4. Review SMB shares for sensitive information\n")
|
|
f.write("5. Test discovered databases for default credentials\n")
|
|
f.write("6. Perform credential stuffing attacks if usernames discovered\n")
|
|
f.write("7. Document all findings in notes/ directory\n")
|
|
f.write("8. Store any discovered credentials in loot/ directory\n")
|
|
f.write("=" * 80 + "\n")
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="TrashPanda - Professional Penetration Testing Framework v2.4",
|
|
epilog="""
|
|
Examples:
|
|
%(prog)s targets.txt # Standard scan with reachability test
|
|
%(prog)s -e targets.txt # Enhanced comprehensive enumeration
|
|
%(prog)s -r targets.txt # Reachability testing only
|
|
%(prog)s -c # Just create directory structure
|
|
%(prog)s -n targets.txt # Only run Nmap scans
|
|
%(prog)s targets.txt -s # Stealth mode scanning
|
|
%(prog)s 192.168.1.0/24 -q # Quick scan mode
|
|
""",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter
|
|
)
|
|
|
|
# Target specification
|
|
parser.add_argument("targets", nargs='?', help="Target file, IP, IP range, or CIDR")
|
|
|
|
# Directory options
|
|
parser.add_argument("-d", "--directory", help="Engagement directory name", default="/root/operator")
|
|
parser.add_argument("-c", "--create-dirs", action="store_true", help="Only create directory structure and exit")
|
|
|
|
# Scan modes
|
|
parser.add_argument("-e", "--enhanced", action="store_true", help="Enable enhanced comprehensive enumeration mode")
|
|
parser.add_argument("-s", "--stealth", action="store_true", help="Enable stealth mode")
|
|
parser.add_argument("-q", "--quick", action="store_true", help="Quick mode")
|
|
parser.add_argument("-f", "--full-tcp", action="store_true", help="Include full TCP port scan")
|
|
|
|
# Reachability testing
|
|
parser.add_argument("-r", "--reachability-only", action="store_true", help="Only run network reachability testing")
|
|
parser.add_argument("--skip-reachability", action="store_true", help="Skip initial reachability testing")
|
|
parser.add_argument("--reachability-threads", type=int, default=REACHABILITY_THREADS, help="Threads for reachability testing")
|
|
parser.add_argument("--reachability-timeout", type=int, default=REACHABILITY_TIMEOUT, help="Timeout for reachability tests")
|
|
|
|
# Individual module flags
|
|
parser.add_argument("-n", "--nmap-only", action="store_true", help="Only run Nmap scans")
|
|
parser.add_argument("--dns-only", action="store_true", help="Only run DNS enumeration")
|
|
parser.add_argument("--snmp-only", action="store_true", help="Only run SNMP enumeration")
|
|
parser.add_argument("--smb-only", action="store_true", help="Only run SMB enumeration")
|
|
parser.add_argument("-w", "--web-only", action="store_true", help="Only run web enumeration")
|
|
parser.add_argument("--ssl-only", action="store_true", help="Only run SSL enumeration")
|
|
parser.add_argument("-v", "--vulns-only", action="store_true", help="Only run vulnerability scripts")
|
|
|
|
# Module exclusions
|
|
parser.add_argument("--no-dns", action="store_true", help="Skip DNS enumeration")
|
|
parser.add_argument("--no-snmp", action="store_true", help="Skip SNMP enumeration")
|
|
parser.add_argument("--no-smb", action="store_true", help="Skip SMB enumeration")
|
|
parser.add_argument("--no-web", action="store_true", help="Skip web enumeration")
|
|
parser.add_argument("--no-ssl", action="store_true", help="Skip SSL enumeration")
|
|
parser.add_argument("--no-vulns", action="store_true", help="Skip vulnerability scripts")
|
|
|
|
# Packet capture options
|
|
parser.add_argument("--no-pcap", action="store_true", help="Skip tcpdump packet capture")
|
|
parser.add_argument("-p", "--pcap-duration", type=int, default=TCPDUMP_DURATION, help=f"TCPDump capture duration in seconds")
|
|
parser.add_argument("-i", "--pcap-interface", default="any", help="Network interface for packet capture")
|
|
|
|
# Debug options
|
|
parser.add_argument("--debug", action="store_true", help="Enable debug output")
|
|
|
|
args = parser.parse_args()
|
|
|
|
print_banner()
|
|
|
|
# Create penetration testing structure
|
|
base_dir = create_pentest_structure(args.directory)
|
|
|
|
# Initialize comprehensive logging
|
|
csv_log_file, verbose_log_file = setup_logging(base_dir)
|
|
log_command.csv_file = csv_log_file # Store for log_command function
|
|
|
|
# Setup console output logging if not in debug mode
|
|
if not args.debug:
|
|
original_stdout = sys.stdout
|
|
sys.stdout = LoggingPrint(original_stdout)
|
|
|
|
log_verbose("TrashPanda session started", 'INFO')
|
|
log_verbose(f"Arguments: {' '.join(sys.argv)}", 'INFO')
|
|
|
|
# If only creating directories, exit here
|
|
if args.create_dirs:
|
|
print(f"{Colors.OKGREEN}[+] Directory structure created. Exiting as requested.{Colors.ENDC}")
|
|
log_verbose("Directory creation only mode - exiting", 'INFO')
|
|
sys.exit(0)
|
|
|
|
# Parse targets
|
|
if args.targets:
|
|
print(f"{Colors.OKBLUE}[*] Parsing targets...{Colors.ENDC}")
|
|
targets = parse_targets(args.targets)
|
|
else:
|
|
# Use default target file
|
|
default_targets = os.path.join(base_dir, "targets", "targets.txt")
|
|
if os.path.exists(default_targets):
|
|
with open(default_targets, 'r') as f:
|
|
content = [line.strip() for line in f if line.strip() and not line.startswith('#')]
|
|
|
|
if content:
|
|
print(f"{Colors.OKBLUE}[*] Using default target file: {default_targets}{Colors.ENDC}")
|
|
targets = parse_targets(default_targets)
|
|
else:
|
|
print(f"{Colors.FAIL}[!] Default target file is empty{Colors.ENDC}")
|
|
sys.exit(1)
|
|
else:
|
|
print(f"{Colors.FAIL}[!] No targets specified and no default target file found{Colors.ENDC}")
|
|
sys.exit(1)
|
|
|
|
if not targets:
|
|
print(f"{Colors.FAIL}[!] No valid targets found{Colors.ENDC}")
|
|
sys.exit(1)
|
|
|
|
print(f"{Colors.OKGREEN}[+] Loaded {len(targets)} targets{Colors.ENDC}")
|
|
|
|
# Apply public IP safety filter
|
|
targets = filter_public_ips_from_targets(targets)
|
|
|
|
if not targets:
|
|
print(f"{Colors.FAIL}[!] No valid targets remaining after filtering{Colors.ENDC}")
|
|
sys.exit(1)
|
|
|
|
print(f"{Colors.OKGREEN}[+] Proceeding with {len(targets)} filtered targets{Colors.ENDC}")
|
|
|
|
# Reachability-only mode
|
|
if args.reachability_only:
|
|
print(f"{Colors.OKBLUE}[*] Running standalone smart network reachability assessment{Colors.ENDC}")
|
|
reachable, unreachable, detailed = run_smart_network_reachability_test(
|
|
targets, base_dir, standalone=True, debug=args.debug,
|
|
threads=args.reachability_threads, timeout=args.reachability_timeout
|
|
)
|
|
sys.exit(0)
|
|
|
|
if args.enhanced:
|
|
print(f"{Colors.WARNING}[!] Enhanced mode enabled - comprehensive enumeration will take significantly longer{Colors.ENDC}")
|
|
if args.stealth:
|
|
print(f"{Colors.WARNING}[!] Stealth mode enabled - scans will be slower and quieter{Colors.ENDC}")
|
|
|
|
# Determine which modules to run
|
|
modules_selected = any([
|
|
args.nmap_only, args.dns_only, args.snmp_only, args.smb_only,
|
|
args.web_only, args.ssl_only, args.vulns_only
|
|
])
|
|
|
|
# Start tcpdump if requested
|
|
tcpdump_info = None
|
|
if not args.no_pcap and not modules_selected:
|
|
tcpdump_info = start_tcpdump(base_dir, args.pcap_duration, args.pcap_interface)
|
|
|
|
# Log start time and parameters
|
|
start_time = time.time()
|
|
engagement_log = os.path.join(base_dir, "logs", "engagement.log")
|
|
|
|
with open(engagement_log, 'a') as f:
|
|
f.write(f"\n=== SCAN SESSION ===\n")
|
|
f.write(f"Start Time: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"Target Count: {len(targets)}\n")
|
|
f.write(f"Enhanced Mode: {args.enhanced}\n")
|
|
f.write(f"Stealth Mode: {args.stealth}\n")
|
|
f.write(f"Quick Mode: {args.quick}\n")
|
|
f.write(f"Reachability Testing: {not args.skip_reachability}\n")
|
|
f.write(f"Arguments: {' '.join(sys.argv)}\n\n")
|
|
|
|
try:
|
|
# Phase 0: Network Reachability Testing (unless skipped)
|
|
if not args.skip_reachability:
|
|
print(f"{Colors.OKBLUE}[*] Phase 0: Smart Network Reachability Assessment{Colors.ENDC}")
|
|
reachable_targets, unreachable_targets, detailed_results = run_smart_network_reachability_test(
|
|
targets, base_dir, standalone=False, debug=args.debug,
|
|
threads=args.reachability_threads, timeout=args.reachability_timeout
|
|
)
|
|
|
|
if not reachable_targets:
|
|
print(f"{Colors.FAIL}[!] No targets are reachable from current network position{Colors.ENDC}")
|
|
print(f"{Colors.WARNING}[!] Check network connectivity or try from different location{Colors.ENDC}")
|
|
sys.exit(1)
|
|
|
|
# Use only reachable targets for further scanning
|
|
targets = reachable_targets
|
|
print(f"{Colors.OKGREEN}[+] Proceeding with {len(targets)} reachable targets{Colors.ENDC}")
|
|
else:
|
|
print(f"{Colors.WARNING}[!] Skipping reachability testing as requested{Colors.ENDC}")
|
|
|
|
alive_hosts = targets # Default to all targets
|
|
|
|
# Run individual modules if specified
|
|
if args.nmap_only:
|
|
alive_hosts = run_nmap_discovery(base_dir, targets, args.stealth, args.quick, args.enhanced, args.debug)
|
|
elif args.dns_only:
|
|
run_enhanced_dns_enumeration(base_dir, targets, args.debug)
|
|
elif args.snmp_only:
|
|
print(f"{Colors.OKBLUE}[*] SNMP enumeration module not implemented yet{Colors.ENDC}")
|
|
elif args.smb_only:
|
|
run_enhanced_smb_enumeration(base_dir, targets, args.debug)
|
|
elif args.web_only:
|
|
run_enhanced_web_enumeration(base_dir, targets, args.stealth, args.debug)
|
|
elif args.ssl_only:
|
|
print(f"{Colors.OKBLUE}[*] SSL enumeration module not implemented yet{Colors.ENDC}")
|
|
elif args.vulns_only:
|
|
print(f"{Colors.OKBLUE}[*] Vulnerability scanning module not implemented yet{Colors.ENDC}")
|
|
else:
|
|
# Progressive scan mode - reorganized for quicker results
|
|
scan_mode = "Enhanced" if args.enhanced else ("Quick" if args.quick else "Standard")
|
|
print(f"{Colors.OKGREEN}[+] Starting {scan_mode} progressive enumeration scan{Colors.ENDC}")
|
|
|
|
# Phase 1: Quick Reconnaissance (fast results first)
|
|
quick_hits = run_quick_reconnaissance(base_dir, targets, args.debug)
|
|
|
|
# Phase 2: Comprehensive Nmap Discovery and Port Scanning
|
|
alive_hosts = run_nmap_discovery(base_dir, targets, args.stealth, args.quick, args.enhanced, args.debug)
|
|
|
|
# Phase 3: Enhanced enumeration if requested
|
|
if args.enhanced:
|
|
run_enhanced_enumeration(base_dir, alive_hosts, args.stealth, args.debug)
|
|
|
|
# Stop tcpdump before generating report
|
|
if tcpdump_info:
|
|
print(f"{Colors.OKBLUE}[*] Stopping packet capture...{Colors.ENDC}")
|
|
stop_tcpdump(tcpdump_info)
|
|
|
|
# Generate comprehensive summary report
|
|
generate_summary_report(base_dir)
|
|
|
|
# Calculate runtime
|
|
end_time = time.time()
|
|
runtime = end_time - start_time
|
|
hours = int(runtime // 3600)
|
|
minutes = int((runtime % 3600) // 60)
|
|
seconds = int(runtime % 60)
|
|
|
|
# Log completion
|
|
with open(engagement_log, 'a') as f:
|
|
f.write(f"End Time: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"Total Runtime: {hours:02d}:{minutes:02d}:{seconds:02d}\n")
|
|
f.write(f"Status: Completed Successfully\n")
|
|
|
|
print(f"\n{Colors.OKGREEN}{'='*60}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] TrashPanda enumeration completed!{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] Runtime: {hours:02d}:{minutes:02d}:{seconds:02d}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] Results saved to: {base_dir}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] Manual commands: {os.path.join(base_dir, 'scans', '_manual_commands.txt')}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] Summary report: {os.path.join(base_dir, 'reports', 'trashpanda_summary.txt')}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] CSV commands log: {csv_log_file}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}[+] Verbose log: {verbose_log_file}{Colors.ENDC}")
|
|
if tcpdump_info:
|
|
print(f"{Colors.OKGREEN}[+] Packet capture: {tcpdump_info['pcap_file']}{Colors.ENDC}")
|
|
print(f"{Colors.OKGREEN}{'='*60}{Colors.ENDC}")
|
|
|
|
log_verbose(f"TrashPanda session completed successfully in {runtime:.2f} seconds", 'INFO')
|
|
|
|
except KeyboardInterrupt:
|
|
print(f"\n{Colors.WARNING}[!] Scan interrupted by user{Colors.ENDC}")
|
|
log_verbose("Scan interrupted by user (KeyboardInterrupt)", 'WARNING')
|
|
if 'tcpdump_info' in locals() and tcpdump_info:
|
|
stop_tcpdump(tcpdump_info)
|
|
generate_summary_report(base_dir)
|
|
sys.exit(1)
|
|
|
|
except Exception as e:
|
|
print(f"\n{Colors.FAIL}[!] Unexpected error: {e}{Colors.ENDC}")
|
|
log_verbose(f"Unexpected error: {e}", 'ERROR')
|
|
if 'tcpdump_info' in locals() and tcpdump_info:
|
|
stop_tcpdump(tcpdump_info)
|
|
if args.debug:
|
|
import traceback
|
|
traceback.print_exc()
|
|
log_verbose(f"Traceback: {traceback.format_exc()}", 'ERROR')
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main() |