#!/usr/bin/env python3 """ AUTARCH - Autonomous Tactical Agent for Reconnaissance, Counterintelligence, and Hacking By darkHal Security Group and Setec Security Labs Main entry point for the AUTARCH framework. """ import sys import shutil import argparse import importlib.util from pathlib import Path from textwrap import dedent # Version info VERSION = "1.3" BUILD_DATE = "2026-01-14" # Ensure the framework directory is in the path FRAMEWORK_DIR = Path(__file__).parent sys.path.insert(0, str(FRAMEWORK_DIR)) from core.banner import Colors, clear_screen, display_banner def get_epilog(): """Get detailed help epilog text.""" return f"""{Colors.BOLD}CATEGORIES:{Colors.RESET} defense Defensive security tools (hardening, audits, monitoring) offense Penetration testing (Metasploit integration, exploits) counter Counter-intelligence (threat hunting, anomaly detection) analyze Forensics & analysis (file analysis, strings, hashes) osint Open source intelligence (email, username, domain lookup) simulate Attack simulation (port scan, payloads, stress test) {Colors.BOLD}MODULES:{Colors.RESET} chat Interactive LLM chat interface agent Autonomous AI agent with tool access msf Metasploit Framework interface defender System hardening and security checks counter Threat detection and hunting analyze File forensics and analysis recon OSINT reconnaissance (email, username, phone, domain) adultscan Adult site username scanner simulate Attack simulation tools {Colors.BOLD}EXAMPLES:{Colors.RESET} {Colors.DIM}# Start interactive menu{Colors.RESET} python autarch.py {Colors.DIM}# Run a specific module{Colors.RESET} python autarch.py -m chat python autarch.py -m adultscan python autarch.py --module recon {Colors.DIM}# List all available modules{Colors.RESET} python autarch.py -l python autarch.py --list {Colors.DIM}# Quick OSINT username scan{Colors.RESET} python autarch.py osint {Colors.DIM}# Show current configuration{Colors.RESET} python autarch.py --show-config {Colors.DIM}# Re-run setup wizard{Colors.RESET} python autarch.py --setup {Colors.DIM}# Skip setup (run without LLM){Colors.RESET} python autarch.py --skip-setup {Colors.DIM}# Use alternate config file{Colors.RESET} python autarch.py -c /path/to/config.conf {Colors.BOLD}FILES:{Colors.RESET} autarch_settings.conf Main configuration file user_manual.md Comprehensive user manual custom_adultsites.json Custom adult sites storage custom_sites.inf Bulk import domains file GUIDE.md Quick reference guide DEVLOG.md Development log {Colors.BOLD}CONFIGURATION:{Colors.RESET} LLM settings: model_path Path to GGUF model file n_ctx Context window size (default: 4096) n_threads CPU threads (default: 4) n_gpu_layers GPU layers to offload (default: 0) temperature Sampling temperature (default: 0.7) MSF settings: host Metasploit RPC host (default: 127.0.0.1) port Metasploit RPC port (default: 55553) ssl Use SSL connection (default: true) autoconnect Auto-start msfrpcd on launch (default: true) {Colors.BOLD}METASPLOIT AUTO-CONNECT:{Colors.RESET} On startup, AUTARCH will: 1. Scan for existing msfrpcd server 2. If found: stop it and prompt for new credentials 3. Start msfrpcd with sudo (for raw socket module support) 4. Connect to the server To skip autoconnect: python autarch.py --no-msf Quick connect: python autarch.py --msf-user msf --msf-pass secret Without sudo: python autarch.py --msf-no-sudo {Colors.BOLD}MORE INFO:{Colors.RESET} Documentation: See GUIDE.md for full documentation Development: See DEVLOG.md for development history {Colors.DIM}Project AUTARCH - By darkHal Security Group and Setec Security Labs{Colors.RESET} """ def create_parser(): """Create the argument parser.""" parser = argparse.ArgumentParser( prog='autarch', description=f'{Colors.BOLD}AUTARCH{Colors.RESET} - Autonomous Tactical Agent for Reconnaissance, Counterintelligence, and Hacking', epilog=get_epilog(), formatter_class=argparse.RawDescriptionHelpFormatter, add_help=False # We'll add custom help ) # Help and version parser.add_argument( '-h', '--help', action='store_true', help='Show this help message and exit' ) parser.add_argument( '-v', '--version', action='store_true', help='Show version information and exit' ) # Configuration parser.add_argument( '-c', '--config', metavar='FILE', help='Use alternate configuration file' ) parser.add_argument( '--show-config', action='store_true', help='Display current configuration and exit' ) parser.add_argument( '--manual', action='store_true', help='Show the user manual' ) parser.add_argument( '--setup', action='store_true', help='Run the setup wizard' ) parser.add_argument( '--skip-setup', action='store_true', help='Skip first-time setup (run without LLM)' ) # Module execution parser.add_argument( '-m', '--module', metavar='NAME', help='Run a specific module directly' ) parser.add_argument( '-l', '--list', action='store_true', help='List all available modules' ) parser.add_argument( '--list-category', metavar='CAT', choices=['defense', 'offense', 'counter', 'analyze', 'osint', 'simulate', 'core'], help='List modules in a specific category' ) # Display options parser.add_argument( '--no-banner', action='store_true', help='Suppress the ASCII banner' ) parser.add_argument( '-q', '--quiet', action='store_true', help='Minimal output mode' ) parser.add_argument( '--verbose', action='store_true', help='Enable verbose output' ) # Web UI options parser.add_argument( '--web', action='store_true', help='Start the web dashboard' ) parser.add_argument( '--web-port', type=int, metavar='PORT', help='Web dashboard port (default: 8181)' ) # Web service management parser.add_argument( '--service', metavar='ACTION', choices=['start', 'stop', 'restart', 'status', 'enable', 'disable', 'install'], help='Manage AUTARCH web service (start|stop|restart|status|enable|disable|install)' ) # MCP server parser.add_argument( '--mcp', choices=['stdio', 'sse'], nargs='?', const='stdio', metavar='MODE', help='Start MCP server (stdio for Claude Desktop/Code, sse for web clients)' ) parser.add_argument( '--mcp-port', type=int, default=8081, metavar='PORT', help='MCP SSE server port (default: 8081)' ) # UPnP options parser.add_argument( '--upnp-refresh', action='store_true', help='Refresh all UPnP port mappings and exit (for cron use)' ) # Metasploit options parser.add_argument( '--no-msf', action='store_true', help='Skip Metasploit autoconnect on startup' ) parser.add_argument( '--msf-user', metavar='USER', help='MSF RPC username for quick connect' ) parser.add_argument( '--msf-pass', metavar='PASS', help='MSF RPC password for quick connect' ) parser.add_argument( '--msf-no-sudo', action='store_true', help='Do not use sudo when starting msfrpcd (limits some modules)' ) # Quick commands (positional) parser.add_argument( 'command', nargs='?', choices=['chat', 'agent', 'osint', 'scan', 'analyze'], help='Quick command to run' ) parser.add_argument( 'target', nargs='?', help='Target for quick commands (username, IP, file, etc.)' ) return parser def show_version(): """Display version information.""" print(f""" {Colors.BOLD}AUTARCH{Colors.RESET} - Autonomous Tactical Agent Version: {VERSION} Build: {BUILD_DATE} {Colors.DIM}By darkHal Security Group and Setec Security Labs{Colors.RESET} Components: - Core Framework v{VERSION} - LLM Integration llama-cpp-python - MSF Integration Metasploit RPC - Agent System Autonomous tools Modules: - chat Interactive LLM chat - agent Autonomous AI agent - msf Metasploit interface - defender System hardening (defense) - counter Threat detection (counter) - analyze Forensics tools (analyze) - recon OSINT reconnaissance (osint) - adultscan Adult site scanner (osint) - simulate Attack simulation (simulate) Python: {sys.version.split()[0]} Path: {FRAMEWORK_DIR} """) def show_config(): """Display current configuration.""" from core.config import get_config config = get_config() print(f"\n{Colors.BOLD}AUTARCH Configuration{Colors.RESET}") print(f"{Colors.DIM}{'─' * 50}{Colors.RESET}\n") print(f"{Colors.CYAN}Config File:{Colors.RESET} {config.config_path}") print() # LLM Settings print(f"{Colors.CYAN}LLM Settings:{Colors.RESET}") llama = config.get_llama_settings() for key, value in llama.items(): print(f" {key:20} = {value}") # Autarch Settings print(f"\n{Colors.CYAN}Autarch Settings:{Colors.RESET}") print(f" {'first_run':20} = {config.get('autarch', 'first_run')}") print(f" {'modules_path':20} = {config.get('autarch', 'modules_path')}") print(f" {'verbose':20} = {config.get('autarch', 'verbose')}") # MSF Settings print(f"\n{Colors.CYAN}Metasploit Settings:{Colors.RESET}") try: from core.msf import get_msf_manager msf = get_msf_manager() settings = msf.get_settings() for key, value in settings.items(): if key == 'password': value = '*' * len(value) if value else '(not set)' print(f" {key:20} = {value}") except: print(f" {Colors.DIM}(MSF not configured){Colors.RESET}") print() def list_modules(category=None): """List available modules.""" from core.menu import MainMenu, CATEGORIES menu = MainMenu() menu.load_modules() print(f"\n{Colors.BOLD}Available Modules{Colors.RESET}") print(f"{Colors.DIM}{'─' * 60}{Colors.RESET}\n") if category: # List specific category cat_info = CATEGORIES.get(category, {}) modules = menu.get_modules_by_category(category) color = cat_info.get('color', Colors.WHITE) print(f"{color}{Colors.BOLD}{category.upper()}{Colors.RESET} - {cat_info.get('description', '')}") print() if modules: for name, info in modules.items(): print(f" {color}{name:15}{Colors.RESET} {info.description}") print(f" {Colors.DIM}{'':15} v{info.version} by {info.author}{Colors.RESET}") else: print(f" {Colors.DIM}No modules in this category{Colors.RESET}") else: # List all categories for cat_name, cat_info in CATEGORIES.items(): modules = menu.get_modules_by_category(cat_name) if not modules: continue color = cat_info.get('color', Colors.WHITE) print(f"{color}{Colors.BOLD}{cat_name.upper()}{Colors.RESET} - {cat_info.get('description', '')}") for name, info in modules.items(): print(f" {color}[{name}]{Colors.RESET} {info.description}") print() print(f"{Colors.DIM}Total modules: {len(menu.modules)}{Colors.RESET}") print(f"{Colors.DIM}Run with: python autarch.py -m {Colors.RESET}\n") def run_module(module_name, quiet=False): """Run a specific module directly.""" modules_path = FRAMEWORK_DIR / 'modules' module_file = modules_path / f"{module_name}.py" if not module_file.exists(): print(f"{Colors.RED}[X] Module not found: {module_name}{Colors.RESET}") print(f"{Colors.DIM}Use --list to see available modules{Colors.RESET}") sys.exit(1) try: spec = importlib.util.spec_from_file_location(module_name, module_file) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) if hasattr(module, 'run'): if not quiet: clear_screen() display_banner() print(f"{Colors.GREEN}[+] Running module: {module_name}{Colors.RESET}") print(f"{Colors.DIM}{'─' * 50}{Colors.RESET}\n") module.run() else: print(f"{Colors.RED}[X] Module '{module_name}' has no run() function{Colors.RESET}") sys.exit(1) except Exception as e: print(f"{Colors.RED}[X] Module error: {e}{Colors.RESET}") sys.exit(1) def quick_osint(username): """Quick OSINT username lookup.""" print(f"\n{Colors.CYAN}Quick OSINT: {username}{Colors.RESET}") print(f"{Colors.DIM}{'─' * 40}{Colors.RESET}\n") # Run adultscan with username try: from modules.adultscan import AdultScanner scanner = AdultScanner() scanner.scan_username(username) scanner.display_results() except Exception as e: print(f"{Colors.RED}Error: {e}{Colors.RESET}") def quick_scan(target): """Quick port scan.""" print(f"\n{Colors.CYAN}Quick Scan: {target}{Colors.RESET}") print(f"{Colors.DIM}{'─' * 40}{Colors.RESET}\n") try: from modules.simulate import Simulator sim = Simulator() # Would need to modify simulator to accept target directly # For now, just inform user print(f"Use: python autarch.py -m simulate") print(f"Then select Port Scanner and enter: {target}") except Exception as e: print(f"{Colors.RED}Error: {e}{Colors.RESET}") def manage_service(action): """Manage the AUTARCH web dashboard systemd service.""" import subprocess SERVICE_NAME = "autarch-web" SERVICE_FILE = FRAMEWORK_DIR / "scripts" / "autarch-web.service" SYSTEMD_PATH = Path("/etc/systemd/system/autarch-web.service") if action == 'install': # Install the service file if not SERVICE_FILE.exists(): print(f"{Colors.RED}[X] Service file not found: {SERVICE_FILE}{Colors.RESET}") return try: subprocess.run(['sudo', 'cp', str(SERVICE_FILE), str(SYSTEMD_PATH)], check=True) subprocess.run(['sudo', 'systemctl', 'daemon-reload'], check=True) print(f"{Colors.GREEN}[+] Service installed: {SYSTEMD_PATH}{Colors.RESET}") print(f"{Colors.DIM} Enable with: python autarch.py --service enable{Colors.RESET}") print(f"{Colors.DIM} Start with: python autarch.py --service start{Colors.RESET}") except subprocess.CalledProcessError as e: print(f"{Colors.RED}[X] Install failed: {e}{Colors.RESET}") return if not SYSTEMD_PATH.exists(): print(f"{Colors.YELLOW}[!] Service not installed. Run: python autarch.py --service install{Colors.RESET}") return cmd_map = { 'start': ['sudo', 'systemctl', 'start', SERVICE_NAME], 'stop': ['sudo', 'systemctl', 'stop', SERVICE_NAME], 'restart': ['sudo', 'systemctl', 'restart', SERVICE_NAME], 'enable': ['sudo', 'systemctl', 'enable', SERVICE_NAME], 'disable': ['sudo', 'systemctl', 'disable', SERVICE_NAME], } if action == 'status': result = subprocess.run( ['systemctl', 'is-active', SERVICE_NAME], capture_output=True, text=True ) is_active = result.stdout.strip() result2 = subprocess.run( ['systemctl', 'is-enabled', SERVICE_NAME], capture_output=True, text=True ) is_enabled = result2.stdout.strip() color = Colors.GREEN if is_active == 'active' else Colors.RED print(f"\n {Colors.BOLD}AUTARCH Web Service{Colors.RESET}") print(f" {'─' * 30}") print(f" Status: {color}{is_active}{Colors.RESET}") print(f" Enabled: {is_enabled}") print() # Show journal output result3 = subprocess.run( ['journalctl', '-u', SERVICE_NAME, '-n', '5', '--no-pager'], capture_output=True, text=True ) if result3.stdout.strip(): print(f" {Colors.DIM}Recent logs:{Colors.RESET}") for line in result3.stdout.strip().split('\n'): print(f" {Colors.DIM}{line}{Colors.RESET}") return if action in cmd_map: try: subprocess.run(cmd_map[action], check=True) print(f"{Colors.GREEN}[+] Service {action}: OK{Colors.RESET}") except subprocess.CalledProcessError as e: print(f"{Colors.RED}[X] Service {action} failed: {e}{Colors.RESET}") def check_first_run(): """Check if this is the first run and execute setup if needed.""" from core.config import get_config config = get_config() if config.is_first_run(): from modules.setup import run as run_setup if not run_setup(): print("Setup cancelled. Exiting.") sys.exit(1) def msf_autoconnect(skip: bool = False, username: str = None, password: str = None, use_sudo: bool = True): """Handle Metasploit autoconnect on startup. Args: skip: Skip autoconnect entirely username: Optional username for quick connect password: Optional password for quick connect use_sudo: Run msfrpcd with sudo (default True for raw socket support) """ if skip: return from core.msf import get_msf_manager, msf_startup_autoconnect, msf_quick_connect, MSGPACK_AVAILABLE if not MSGPACK_AVAILABLE: print(f"{Colors.DIM} [MSF] msgpack not available - skipping autoconnect{Colors.RESET}") return # If credentials provided via command line, use quick connect if password: msf_quick_connect(username=username, password=password, use_sudo=use_sudo) else: # Use interactive autoconnect msf_startup_autoconnect() def run_setup_wizard(): """Run the setup wizard.""" from modules.setup import run as run_setup run_setup() def main(): """Main entry point for AUTARCH.""" parser = create_parser() args = parser.parse_args() # Handle help if args.help: if not args.quiet: display_banner() parser.print_help() sys.exit(0) # Handle version if args.version: show_version() sys.exit(0) # Handle config file override if args.config: from core import config as config_module config_module._config = config_module.Config(args.config) # Handle show config if args.show_config: show_config() sys.exit(0) # Handle manual if getattr(args, 'manual', False): manual_path = FRAMEWORK_DIR / 'user_manual.md' if manual_path.exists(): # Try to use less/more for paging import subprocess pager = 'less' if shutil.which('less') else ('more' if shutil.which('more') else None) if pager: subprocess.run([pager, str(manual_path)]) else: print(manual_path.read_text()) else: print(f"{Colors.RED}[X] User manual not found: {manual_path}{Colors.RESET}") sys.exit(0) # Handle setup if args.setup: if not args.no_banner: clear_screen() display_banner() run_setup_wizard() sys.exit(0) # Handle skip setup if args.skip_setup: from modules.setup import SetupWizard wizard = SetupWizard() wizard.skip_setup() sys.exit(0) # Handle service management if args.service: manage_service(args.service) sys.exit(0) # Handle MCP server if args.mcp: from core.mcp_server import run_stdio, run_sse if args.mcp == 'sse': print(f"{Colors.CYAN}[*] Starting AUTARCH MCP server (SSE) on port {args.mcp_port}{Colors.RESET}") run_sse(port=args.mcp_port) else: run_stdio() sys.exit(0) # Handle web dashboard if args.web: from web.app import create_app from core.config import get_config from core.paths import get_data_dir config = get_config() app = create_app() host = config.get('web', 'host', fallback='0.0.0.0') port = args.web_port or config.get_int('web', 'port', fallback=8181) # Auto-generate self-signed TLS cert for HTTPS (required for WebUSB over LAN) ssl_ctx = None use_https = config.get('web', 'https', fallback='true').lower() != 'false' if use_https: import os, subprocess as _sp cert_dir = os.path.join(get_data_dir(), 'certs') os.makedirs(cert_dir, exist_ok=True) cert_path = os.path.join(cert_dir, 'autarch.crt') key_path = os.path.join(cert_dir, 'autarch.key') if not os.path.exists(cert_path) or not os.path.exists(key_path): print(f"{Colors.CYAN}[*] Generating self-signed TLS certificate...{Colors.RESET}") _sp.run([ 'openssl', 'req', '-x509', '-newkey', 'rsa:2048', '-keyout', key_path, '-out', cert_path, '-days', '3650', '-nodes', '-subj', '/CN=AUTARCH/O=darkHal', ], check=True, capture_output=True) ssl_ctx = (cert_path, key_path) proto = 'https' else: proto = 'http' print(f"{Colors.GREEN}[+] Starting AUTARCH Web Dashboard on {proto}://{host}:{port}{Colors.RESET}") app.run(host=host, port=port, debug=False, ssl_context=ssl_ctx) sys.exit(0) # Handle UPnP refresh (for cron) if args.upnp_refresh: from core.upnp import get_upnp_manager upnp = get_upnp_manager() results = upnp.refresh_all() for r in results: status = "OK" if r['success'] else "FAIL" print(f" {r['port']}/{r['protocol']}: {status}") sys.exit(0) # Handle list modules if args.list: list_modules() sys.exit(0) if args.list_category: list_modules(args.list_category) sys.exit(0) # Handle direct module execution if args.module: run_module(args.module, args.quiet) sys.exit(0) # Handle quick commands if args.command: if not args.no_banner: clear_screen() display_banner() if args.command == 'chat': run_module('chat', args.quiet) elif args.command == 'agent': run_module('agent', args.quiet) elif args.command == 'osint': if args.target: quick_osint(args.target) else: print(f"{Colors.RED}Usage: autarch osint {Colors.RESET}") elif args.command == 'scan': if args.target: quick_scan(args.target) else: print(f"{Colors.RED}Usage: autarch scan {Colors.RESET}") elif args.command == 'analyze': if args.target: run_module('analyze', args.quiet) else: run_module('analyze', args.quiet) sys.exit(0) # Default: run interactive menu try: # Display banner first if not args.no_banner: clear_screen() display_banner() # Check for first run and execute setup check_first_run() # Metasploit autoconnect msf_autoconnect( skip=args.no_msf, username=args.msf_user, password=args.msf_pass, use_sudo=not args.msf_no_sudo ) # Apply CLI display flags to config for this session from core.config import get_config cfg = get_config() if args.verbose: cfg.set('autarch', 'verbose', 'true') if args.quiet: cfg.set('autarch', 'quiet', 'true') if args.no_banner: cfg.set('autarch', 'no_banner', 'true') # Start the main menu from core.menu import MainMenu menu = MainMenu() menu.run() except KeyboardInterrupt: print(f"\n\n{Colors.CYAN}Exiting AUTARCH...{Colors.RESET}") sys.exit(0) except Exception as e: print(f"\n{Colors.RED}Fatal error: {e}{Colors.RESET}") if '--verbose' in sys.argv: import traceback traceback.print_exc() sys.exit(1) if __name__ == "__main__": main()