98103466d8
Sanitized version of red team infrastructure automation platform. Operational content (implant pipelines, lures, credential capture) replaced with documented stubs. Architecture and infrastructure automation code intact.
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() |