Add Port Scanner, fix Hack Hijack SSE, fix debug console, fix tab layout bugs

- Add advanced Port Scanner with live SSE output, nmap integration, result export
- Add Port Scanner to sidebar nav and register blueprint
- Fix Hack Hijack scan: replace polling with SSE streaming, add live output box
  and real-time port discovery table; add port_found_cb/status_cb to module
- Fix debug console: capture print()/stdout/stderr via _PrintCapture wrapper,
  install handler at startup (not just on toggle), fix SSE stream history replay
- Add missing CSS: .card, .tabs, .btn-sm, .form-control, --primary, --surface
- Fix tab switching bug: style.display='' falls back to CSS display:none;
  use explicit 'block' in hack_hijack, c2_framework, net_mapper, password_toolkit,
  report_engine, social_eng, webapp_scanner
- Fix defense/linux layout: rewrite with card-based layout, remove slow
  load_modules() call on every page request
- Fix sms_forge missing run() function warning on startup
- Fix port scanner JS: </style> was used instead of </script> closing tag
- Port scanner advanced options: remove collapsible toggle, show as always-visible bar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
DigiJ 2026-03-08 18:09:49 -07:00
parent 6c8e9235c9
commit ddc03e7a55
19 changed files with 1325 additions and 196 deletions

View File

@ -370,7 +370,9 @@ class HackHijackService:
def scan_target(self, target: str, scan_type: str = 'quick',
custom_ports: List[int] = None,
timeout: float = 3.0,
progress_cb=None) -> ScanResult:
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),
@ -396,11 +398,16 @@ class HackHijackService:
# 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:
@ -408,28 +415,40 @@ class HackHijackService:
total = len(sorted_ports)
results_lock = threading.Lock()
open_ports = []
scanned = [0]
def scan_port(port):
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,), daemon=True)
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()
if progress_cb and i % 10 == 0:
progress_cb(i, total)
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

View File

@ -11,6 +11,12 @@ AUTHOR = "AUTARCH"
VERSION = "1.0"
CATEGORY = "offense"
def run():
"""CLI entry point — this module is used via the web UI."""
print("SMS Forge is managed through the AUTARCH web interface.")
print("Navigate to Offense → SMS Forge in the dashboard.")
import os
import csv
import json

View File

@ -102,6 +102,7 @@ def create_app():
from web.routes.sms_forge import sms_forge_bp
from web.routes.starlink_hack import starlink_hack_bp
from web.routes.rcs_tools import rcs_tools_bp
from web.routes.port_scanner import port_scanner_bp
app.register_blueprint(auth_bp)
app.register_blueprint(dashboard_bp)
@ -163,6 +164,7 @@ def create_app():
app.register_blueprint(sms_forge_bp)
app.register_blueprint(starlink_hack_bp)
app.register_blueprint(rcs_tools_bp)
app.register_blueprint(port_scanner_bp)
# Start network discovery advertising (mDNS + Bluetooth)
try:

View File

@ -48,11 +48,7 @@ def index():
@defense_bp.route('/linux')
@login_required
def linux_index():
from core.menu import MainMenu
menu = MainMenu()
menu.load_modules()
modules = {k: v for k, v in menu.modules.items() if v.category == 'defense'}
return render_template('defense_linux.html', modules=modules)
return render_template('defense_linux.html')
@defense_bp.route('/linux/audit', methods=['POST'])

View File

@ -1,13 +1,16 @@
"""Hack Hijack — web routes for scanning and taking over compromised systems."""
import json
import queue
import threading
import time
import uuid
from flask import Blueprint, render_template, request, jsonify, Response
from web.auth import login_required
hack_hijack_bp = Blueprint('hack_hijack', __name__)
# Running scans keyed by job_id
# job_id -> {'q': Queue, 'result': dict|None, 'error': str|None, 'done': bool, 'cancel': bool}
_running_scans: dict = {}
@ -37,34 +40,86 @@ def start_scan():
if not target:
return jsonify({'ok': False, 'error': 'Target IP required'})
# Validate scan type
if scan_type not in ('quick', 'full', 'nmap', 'custom'):
scan_type = 'quick'
job_id = str(uuid.uuid4())[:8]
result_holder = {'result': None, 'error': None, 'done': False}
_running_scans[job_id] = result_holder
q = queue.Queue()
job = {'q': q, 'result': None, 'error': None, 'done': False, 'cancel': False}
_running_scans[job_id] = job
def _push(evt_type, **kw):
kw['type'] = evt_type
kw['ts'] = time.time()
q.put(kw)
def do_scan():
try:
svc = _svc()
# Build a progress callback that feeds the queue
def progress_cb(current, total, message=''):
_push('progress', current=current, total=total,
pct=round(current * 100 / total) if total else 0,
msg=message)
def port_found_cb(port_info):
_push('port_found',
port=port_info.get('port') or (port_info.port if hasattr(port_info, 'port') else 0),
service=getattr(port_info, 'service', port_info.get('service', '')),
banner=getattr(port_info, 'banner', port_info.get('banner', ''))[:80])
def status_cb(msg):
_push('status', msg=msg)
r = svc.scan_target(
target,
scan_type=scan_type,
custom_ports=custom_ports,
timeout=3.0,
progress_cb=progress_cb,
port_found_cb=port_found_cb,
status_cb=status_cb,
)
result_holder['result'] = r.to_dict()
job['result'] = r.to_dict()
except Exception as e:
result_holder['error'] = str(e)
job['error'] = str(e)
_push('error', msg=str(e))
finally:
result_holder['done'] = True
job['done'] = True
_push('done', ok=job['error'] is None)
threading.Thread(target=do_scan, daemon=True).start()
return jsonify({'ok': True, 'job_id': job_id,
'message': f'Scan started on {target} ({scan_type})'})
@hack_hijack_bp.route('/hack-hijack/scan/<job_id>/stream')
@login_required
def scan_stream(job_id):
"""SSE stream for live scan progress."""
job = _running_scans.get(job_id)
if not job:
def _err():
yield f"data: {json.dumps({'type': 'error', 'msg': 'Job not found'})}\n\n"
return Response(_err(), mimetype='text/event-stream')
def generate():
q = job['q']
while True:
try:
item = q.get(timeout=0.5)
yield f"data: {json.dumps(item)}\n\n"
if item.get('type') == 'done':
break
except queue.Empty:
if job['done']:
break
yield ': keepalive\n\n'
return Response(generate(), mimetype='text/event-stream',
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'})
@hack_hijack_bp.route('/hack-hijack/scan/<job_id>', methods=['GET'])
@login_required
def scan_status(job_id):
@ -75,7 +130,6 @@ def scan_status(job_id):
return jsonify({'ok': True, 'done': False, 'message': 'Scan in progress...'})
if holder['error']:
return jsonify({'ok': False, 'error': holder['error'], 'done': True})
# Clean up
_running_scans.pop(job_id, None)
return jsonify({'ok': True, 'done': True, 'result': holder['result']})

473
web/routes/port_scanner.py Normal file
View File

@ -0,0 +1,473 @@
"""Advanced Port Scanner — streaming SSE-based port scanner with nmap integration."""
import json
import queue
import socket
import subprocess
import threading
import time
import uuid
from datetime import datetime, timezone
from typing import Optional
from flask import Blueprint, render_template, request, jsonify, Response
from web.auth import login_required
port_scanner_bp = Blueprint('port_scanner', __name__, url_prefix='/port-scanner')
# job_id -> {'q': Queue, 'result': dict|None, 'done': bool, 'cancel': bool}
_jobs: dict = {}
# ── Common port lists ──────────────────────────────────────────────────────────
QUICK_PORTS = [
21, 22, 23, 25, 53, 80, 110, 111, 135, 139, 143, 443, 445, 993, 995,
1723, 3306, 3389, 5900, 8080, 8443, 8888,
]
COMMON_PORTS = [
20, 21, 22, 23, 25, 53, 67, 68, 69, 80, 88, 110, 111, 119, 123, 135,
137, 138, 139, 143, 161, 162, 179, 194, 389, 443, 445, 465, 500, 514,
515, 587, 593, 631, 636, 873, 902, 993, 995, 1080, 1194, 1433, 1521,
1723, 1883, 2049, 2121, 2181, 2222, 2375, 2376, 2483, 2484, 3000, 3306,
3389, 3690, 4000, 4040, 4333, 4444, 4567, 4899, 5000, 5432, 5601, 5672,
5900, 5984, 6000, 6379, 6443, 6881, 7000, 7001, 7080, 7443, 7474, 8000,
8001, 8008, 8080, 8081, 8082, 8083, 8088, 8089, 8161, 8333, 8443, 8444,
8500, 8888, 8983, 9000, 9001, 9042, 9090, 9092, 9200, 9300, 9418, 9999,
10000, 11211, 15432, 15672, 27017, 27018, 27019, 28017, 50000, 54321,
]
SERVICE_MAP = {
20: 'FTP-data', 21: 'FTP', 22: 'SSH', 23: 'Telnet', 25: 'SMTP',
53: 'DNS', 67: 'DHCP', 68: 'DHCP', 69: 'TFTP', 80: 'HTTP',
88: 'Kerberos', 110: 'POP3', 111: 'RPC', 119: 'NNTP', 123: 'NTP',
135: 'MS-RPC', 137: 'NetBIOS-NS', 138: 'NetBIOS-DGM', 139: 'NetBIOS-SSN',
143: 'IMAP', 161: 'SNMP', 162: 'SNMP-Trap', 179: 'BGP', 194: 'IRC',
389: 'LDAP', 443: 'HTTPS', 445: 'SMB', 465: 'SMTPS', 500: 'IKE/ISAKMP',
514: 'Syslog/RSH', 515: 'LPD', 587: 'SMTP-Submission', 631: 'IPP',
636: 'LDAPS', 873: 'rsync', 993: 'IMAPS', 995: 'POP3S',
1080: 'SOCKS', 1194: 'OpenVPN', 1433: 'MSSQL', 1521: 'Oracle',
1723: 'PPTP', 1883: 'MQTT', 2049: 'NFS', 2181: 'Zookeeper',
2222: 'SSH-alt', 2375: 'Docker', 2376: 'Docker-TLS', 3000: 'Grafana',
3306: 'MySQL', 3389: 'RDP', 3690: 'SVN', 4444: 'Meterpreter',
5000: 'Flask/Dev', 5432: 'PostgreSQL', 5601: 'Kibana', 5672: 'AMQP/RabbitMQ',
5900: 'VNC', 5984: 'CouchDB', 6379: 'Redis', 6443: 'Kubernetes-API',
7474: 'Neo4j', 8080: 'HTTP-Alt', 8443: 'HTTPS-Alt', 8500: 'Consul',
8888: 'Jupyter/HTTP-Alt', 9000: 'SonarQube/PHP-FPM', 9001: 'Tor/Supervisor',
9042: 'Cassandra', 9090: 'Prometheus/Cockpit', 9092: 'Kafka',
9200: 'Elasticsearch', 9300: 'Elasticsearch-node', 9418: 'Git',
10000: 'Webmin', 11211: 'Memcached', 15672: 'RabbitMQ-Mgmt',
27017: 'MongoDB', 27018: 'MongoDB', 27019: 'MongoDB', 50000: 'DB2',
}
PROBE_MAP = {
21: b'',
22: b'',
23: b'',
25: b'',
80: b'HEAD / HTTP/1.0\r\nHost: localhost\r\n\r\n',
110: b'',
143: b'',
443: b'',
3306: b'',
5432: b'',
6379: b'INFO\r\n',
8080: b'HEAD / HTTP/1.0\r\nHost: localhost\r\n\r\n',
8443: b'',
8888: b'HEAD / HTTP/1.0\r\nHost: localhost\r\n\r\n',
9200: b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n',
27017: b'',
}
def _push(q: queue.Queue, event_type: str, data: dict) -> None:
data['type'] = event_type
data['ts'] = time.time()
q.put(data)
def _grab_banner(sock: socket.socket, port: int, timeout: float = 2.0) -> str:
try:
sock.settimeout(timeout)
probe = PROBE_MAP.get(port, b'')
if probe:
sock.sendall(probe)
raw = sock.recv(2048)
return raw.decode('utf-8', errors='replace').strip()[:512]
except Exception:
return ''
def _identify_service(port: int, banner: str) -> str:
bl = banner.lower()
if 'ssh-' in bl:
return 'SSH'
if 'ftp' in bl or '220 ' in bl[:10]:
return 'FTP'
if 'smtp' in bl or ('220 ' in bl and 'mail' in bl):
return 'SMTP'
if 'http/' in bl or '<html' in bl or '<!doctype' 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'
if 'rabbitmq' in bl:
return 'RabbitMQ'
if 'elastic' in bl:
return 'Elasticsearch'
return SERVICE_MAP.get(port, 'unknown')
def _scan_port(host: str, port: int, timeout: float) -> Optional[dict]:
"""TCP connect scan a single port. Returns port info dict or None if closed."""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
err = sock.connect_ex((host, port))
if err == 0:
banner = _grab_banner(sock, port)
sock.close()
service = _identify_service(port, banner)
return {
'port': port,
'protocol': 'tcp',
'state': 'open',
'service': service,
'banner': banner,
}
sock.close()
except Exception:
pass
return None
def _run_nmap_scan(host: str, ports: list, options: dict, q: queue.Queue,
job: dict) -> Optional[list]:
"""Run nmap and parse output. Returns list of port dicts or None if nmap unavailable."""
import shutil
nmap = shutil.which('nmap')
if not nmap:
_push(q, 'warning', {'msg': 'nmap not found — falling back to TCP connect scan'})
return None
port_str = ','.join(str(p) for p in sorted(ports))
cmd = [nmap, '-Pn', '--open', '-p', port_str]
if options.get('service_detection'):
cmd += ['-sV', '--version-intensity', '5']
if options.get('os_detection'):
cmd += ['-O', '--osscan-guess']
if options.get('aggressive'):
cmd += ['-A']
if options.get('timing'):
cmd += [f'-T{options["timing"]}']
else:
cmd += ['-T4']
cmd += ['-oN', '-', '--host-timeout', '120s', host]
_push(q, 'nmap_start', {'cmd': ' '.join(cmd[:-1] + ['<target>'])})
open_ports = []
os_guess = ''
nmap_raw_lines = []
try:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
text=True, bufsize=1)
for line in proc.stdout:
line = line.rstrip()
if job.get('cancel'):
proc.terminate()
break
nmap_raw_lines.append(line)
_push(q, 'nmap_line', {'line': line})
# Parse open port lines: "80/tcp open http Apache httpd 2.4.54"
stripped = line.strip()
if '/tcp' in stripped or '/udp' in stripped:
parts = stripped.split()
if len(parts) >= 2 and parts[1] == 'open':
port_proto = parts[0].split('/')
port_num = int(port_proto[0])
proto = port_proto[1] if len(port_proto) > 1 else 'tcp'
service = parts[2] if len(parts) > 2 else SERVICE_MAP.get(port_num, 'unknown')
version = ' '.join(parts[3:]) if len(parts) > 3 else ''
open_ports.append({
'port': port_num,
'protocol': proto,
'state': 'open',
'service': service,
'banner': version,
})
_push(q, 'port_open', {
'port': port_num, 'protocol': proto,
'service': service, 'banner': version,
})
# OS detection line
if 'OS details:' in line or 'Running:' in line:
os_guess = line.split(':', 1)[-1].strip()
proc.wait(timeout=10)
except Exception as exc:
_push(q, 'warning', {'msg': f'nmap error: {exc} — falling back to TCP connect scan'})
return None
return open_ports, os_guess, '\n'.join(nmap_raw_lines)
def _socket_scan(host: str, ports: list, timeout: float, concurrency: int,
q: queue.Queue, job: dict) -> list:
"""Concurrent TCP connect scan. Returns list of open port dicts."""
open_ports = []
lock = threading.Lock()
scanned = [0]
total = len(ports)
start = time.time()
def worker(port: int):
if job.get('cancel'):
return
result = _scan_port(host, port, timeout)
with lock:
scanned[0] += 1
done = scanned[0]
if result:
with lock:
open_ports.append(result)
_push(q, 'port_open', {
'port': result['port'], 'protocol': result['protocol'],
'service': result['service'], 'banner': result['banner'],
})
# Progress every 25 ports or on first/last
if done == 1 or done % 25 == 0 or done == total:
elapsed = time.time() - start
rate = done / elapsed if elapsed > 0 else 0
eta = int((total - done) / rate) if rate > 0 else 0
_push(q, 'progress', {
'current': done, 'total': total,
'pct': round(done * 100 / total),
'eta': f'{eta}s' if eta < 3600 else f'{eta//60}m',
'port': port,
'open_count': len(open_ports),
})
sem = threading.Semaphore(concurrency)
threads = []
for port in sorted(ports):
if job.get('cancel'):
break
sem.acquire()
def _run(p=port):
try:
worker(p)
finally:
sem.release()
t = threading.Thread(target=_run, daemon=True)
threads.append(t)
t.start()
for t in threads:
t.join(timeout=timeout + 1)
return sorted(open_ports, key=lambda x: x['port'])
def _do_scan(job_id: str, host: str, ports: list, options: dict) -> None:
"""Main scan worker — runs in a background thread."""
job = _jobs[job_id]
q = job['q']
start = time.time()
_push(q, 'start', {
'target': host, 'total_ports': len(ports),
'mode': options.get('mode', 'custom'),
})
# Resolve hostname
ip = host
try:
ip = socket.gethostbyname(host)
if ip != host:
_push(q, 'info', {'msg': f'Resolved {host}{ip}'})
except Exception:
_push(q, 'warning', {'msg': f'Could not resolve {host} — using as-is'})
open_ports = []
os_guess = ''
nmap_raw = ''
try:
use_nmap = options.get('use_nmap', False)
timeout = float(options.get('timeout', 1.0))
concurrency = int(options.get('concurrency', 100))
if use_nmap:
nmap_result = _run_nmap_scan(ip, ports, options, q, job)
if nmap_result is not None:
open_ports, os_guess, nmap_raw = nmap_result
else:
# fallback
_push(q, 'info', {'msg': 'Running TCP connect scan fallback...'})
open_ports = _socket_scan(ip, ports, timeout, concurrency, q, job)
else:
_push(q, 'info', {'msg': f'Scanning {len(ports)} ports on {ip} '
f'(concurrency={concurrency}, timeout={timeout}s)'})
open_ports = _socket_scan(ip, ports, timeout, concurrency, q, job)
duration = round(time.time() - start, 2)
result = {
'target': host,
'ip': ip,
'scan_time': datetime.now(timezone.utc).isoformat(),
'duration': duration,
'ports_scanned': len(ports),
'open_ports': open_ports,
'os_guess': os_guess,
'nmap_raw': nmap_raw,
'options': options,
}
job['result'] = result
_push(q, 'done', {
'open_count': len(open_ports),
'ports_scanned': len(ports),
'duration': duration,
'os_guess': os_guess,
})
except Exception as exc:
_push(q, 'error', {'msg': str(exc)})
finally:
job['done'] = True
# ── Routes ────────────────────────────────────────────────────────────────────
@port_scanner_bp.route('/')
@login_required
def index():
return render_template('port_scanner.html')
@port_scanner_bp.route('/start', methods=['POST'])
@login_required
def start_scan():
data = request.get_json(silent=True) or {}
host = data.get('target', '').strip()
if not host:
return jsonify({'ok': False, 'error': 'Target required'})
mode = data.get('mode', 'common')
if mode == 'quick':
ports = list(QUICK_PORTS)
elif mode == 'common':
ports = list(COMMON_PORTS)
elif mode == 'full':
ports = list(range(1, 65536))
elif mode == 'custom':
raw = data.get('ports', '').strip()
ports = _parse_port_spec(raw)
if not ports:
return jsonify({'ok': False, 'error': 'No valid ports in custom range'})
else:
ports = list(COMMON_PORTS)
options = {
'mode': mode,
'use_nmap': bool(data.get('use_nmap', False)),
'service_detection': bool(data.get('service_detection', False)),
'os_detection': bool(data.get('os_detection', False)),
'aggressive': bool(data.get('aggressive', False)),
'timing': data.get('timing', '4'),
'timeout': float(data.get('timeout', 1.0)),
'concurrency': min(int(data.get('concurrency', 200)), 500),
}
job_id = str(uuid.uuid4())[:8]
job = {'q': queue.Queue(), 'result': None, 'done': False, 'cancel': False}
_jobs[job_id] = job
t = threading.Thread(target=_do_scan, args=(job_id, host, ports, options), daemon=True)
t.start()
return jsonify({'ok': True, 'job_id': job_id, 'port_count': len(ports)})
@port_scanner_bp.route('/stream/<job_id>')
@login_required
def stream(job_id):
job = _jobs.get(job_id)
if not job:
def err_gen():
yield f"data: {json.dumps({'type': 'error', 'msg': 'Job not found'})}\n\n"
return Response(err_gen(), mimetype='text/event-stream')
def generate():
q = job['q']
while True:
try:
item = q.get(timeout=0.5)
yield f"data: {json.dumps(item)}\n\n"
if item.get('type') in ('done', 'error'):
break
except queue.Empty:
if job['done']:
break
yield ': keepalive\n\n'
return Response(generate(), mimetype='text/event-stream',
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'})
@port_scanner_bp.route('/result/<job_id>')
@login_required
def get_result(job_id):
job = _jobs.get(job_id)
if not job:
return jsonify({'ok': False, 'error': 'Job not found'})
if not job['done']:
return jsonify({'ok': True, 'done': False})
return jsonify({'ok': True, 'done': True, 'result': job['result']})
@port_scanner_bp.route('/cancel/<job_id>', methods=['POST'])
@login_required
def cancel_scan(job_id):
job = _jobs.get(job_id)
if job:
job['cancel'] = True
return jsonify({'ok': True})
# ── Helpers ───────────────────────────────────────────────────────────────────
def _parse_port_spec(spec: str) -> list:
"""Parse port specification: '22,80,443', '1-1024', '22,80-100,443'."""
ports = set()
for part in spec.split(','):
part = part.strip()
if '-' in part:
try:
lo, hi = part.split('-', 1)
lo, hi = int(lo.strip()), int(hi.strip())
if 1 <= lo <= hi <= 65535:
ports.update(range(lo, hi + 1))
except ValueError:
pass
else:
try:
p = int(part)
if 1 <= p <= 65535:
ports.add(p)
except ValueError:
pass
return sorted(ports)

View File

@ -21,42 +21,83 @@ _debug_enabled: bool = False
_debug_handler_installed: bool = False
def _buf_append(level: str, name: str, raw: str, msg: str, exc: str = '') -> None:
"""Thread-safe append to the debug buffer."""
entry: dict = {'ts': time.time(), 'level': level, 'name': name, 'raw': raw, 'msg': msg}
if exc:
entry['exc'] = exc
_debug_buffer.append(entry)
class _DebugBufferHandler(logging.Handler):
"""Captures log records into the in-memory debug buffer."""
"""Captures ALL log records into the in-memory debug buffer (always active)."""
def emit(self, record: logging.LogRecord) -> None:
if not _debug_enabled:
return
try:
entry: dict = {
'ts': record.created,
'level': record.levelname,
'name': record.name,
'raw': record.getMessage(),
'msg': self.format(record),
}
exc_text = ''
if record.exc_info:
import traceback as _tb
entry['exc'] = ''.join(_tb.format_exception(*record.exc_info))
_debug_buffer.append(entry)
exc_text = ''.join(_tb.format_exception(*record.exc_info))
_buf_append(
level=record.levelname,
name=record.name,
raw=record.getMessage(),
msg=self.format(record),
exc=exc_text,
)
except Exception:
pass
class _PrintCapture:
"""Wraps sys.stdout or sys.stderr — passes through AND feeds lines to the debug buffer."""
def __init__(self, original, level: str = 'STDOUT'):
self._orig = original
self._level = level
self._line_buf = ''
def write(self, text: str) -> int:
self._orig.write(text)
self._line_buf += text
while '\n' in self._line_buf:
line, self._line_buf = self._line_buf.split('\n', 1)
if line.strip():
_buf_append(self._level, 'print', line, line)
return len(text)
def flush(self) -> None:
self._orig.flush()
def __getattr__(self, name):
return getattr(self._orig, name)
def _ensure_debug_handler() -> None:
"""Install logging handler + stdout/stderr capture once, at startup."""
global _debug_handler_installed
if _debug_handler_installed:
return
# Logging handler
handler = _DebugBufferHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter('%(name)s%(message)s'))
root = logging.getLogger()
root.addHandler(handler)
# Lower root level to DEBUG so records reach the handler
if root.level == logging.NOTSET or root.level > logging.DEBUG:
root.setLevel(logging.DEBUG)
# stdout / stderr capture
import sys as _sys
if not isinstance(_sys.stdout, _PrintCapture):
_sys.stdout = _PrintCapture(_sys.stdout, 'STDOUT')
if not isinstance(_sys.stderr, _PrintCapture):
_sys.stderr = _PrintCapture(_sys.stderr, 'STDERR')
_debug_handler_installed = True
# Install immediately so we capture from process start, not just after toggle
_ensure_debug_handler()
settings_bp = Blueprint('settings', __name__, url_prefix='/settings')
@ -429,28 +470,42 @@ def discovery_stop():
@settings_bp.route('/debug/toggle', methods=['POST'])
@login_required
def debug_toggle():
"""Enable or disable the debug log capture."""
"""Enable or disable the debug console UI (capture always runs)."""
global _debug_enabled
data = request.get_json(silent=True) or {}
_debug_enabled = bool(data.get('enabled', False))
if _debug_enabled:
_ensure_debug_handler()
logging.getLogger('autarch.debug').info('Debug console enabled')
logging.getLogger('autarch.debug').info('Debug console opened')
return jsonify({'ok': True, 'enabled': _debug_enabled})
@settings_bp.route('/debug/stream')
@login_required
def debug_stream():
"""SSE stream — pushes new log records to the browser as they arrive."""
"""SSE stream — pushes log records to the browser as they arrive.
On connect: sends the last 200 buffered entries as history, then streams
new entries live. Handles deque wrap-around correctly.
"""
def generate():
sent = 0
while True:
buf = list(_debug_buffer)
while sent < len(buf):
# Send last 200 entries as catch-up history
history_start = max(0, len(buf) - 200)
for entry in buf[history_start:]:
yield f"data: {json.dumps(entry)}\n\n"
sent = len(buf)
while True:
time.sleep(0.2)
buf = list(_debug_buffer)
n = len(buf)
if sent > n:
# deque wrapped; re-orient to current tail
sent = n
while sent < n:
yield f"data: {json.dumps(buf[sent])}\n\n"
sent += 1
time.sleep(0.25)
yield ': keepalive\n\n'
return Response(generate(), mimetype='text/event-stream',
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'})

View File

@ -9,6 +9,8 @@
--text-muted: #5c6078;
--accent: #6366f1;
--accent-hover: #818cf8;
--primary: #6366f1;
--surface: #222536;
--success: #22c55e;
--warning: #f59e0b;
--danger: #ef4444;
@ -101,6 +103,19 @@ pre { background: var(--bg-primary); border: 1px solid var(--border); border-rad
.flash-info { background: rgba(99,102,241,0.15); color: var(--accent); border: 1px solid rgba(99,102,241,0.3); }
/* Forms */
/* Standalone form control (used outside .form-group) */
.form-control {
display: block; width: 100%; padding: 8px 12px;
background: var(--bg-input); border: 1px solid var(--border);
border-radius: var(--radius); color: var(--text-primary);
font-size: 0.9rem; font-family: inherit;
}
.form-control:focus { outline: none; border-color: var(--accent); }
.form-control[type="text"], .form-control[type="number"],
.form-control[type="password"], .form-control[type="email"] { height: 38px; }
select.form-control { height: 38px; cursor: pointer; }
textarea.form-control { font-family: monospace; resize: vertical; }
.form-group { margin-bottom: 16px; text-align: left; }
.form-group label { display: block; margin-bottom: 6px; font-size: 0.85rem; color: var(--text-secondary); }
.form-group input, .form-group select, .form-group textarea {
@ -133,8 +148,10 @@ pre { background: var(--bg-primary); border: 1px solid var(--border); border-rad
.btn-warning:hover { background: rgba(245,158,11,0.3); }
.btn-danger { background: rgba(239,68,68,0.2); border-color: var(--danger); color: var(--danger); }
.btn-danger:hover { background: rgba(239,68,68,0.3); }
.btn-small { padding: 6px 12px; font-size: 0.8rem; }
.btn-small, .btn-sm { padding: 6px 12px; font-size: 0.8rem; }
.btn-full { width: 100%; }
.btn-outline { background: transparent; border-color: var(--accent); color: var(--accent); }
.btn-outline:hover { background: rgba(99,102,241,0.15); }
.btn-group { display: flex; gap: 8px; flex-wrap: wrap; }
/* Page header */
@ -176,6 +193,18 @@ pre { background: var(--bg-primary); border: 1px solid var(--border); border-rad
.section h2 { font-size: 1rem; margin-bottom: 16px; }
.section h3 { font-size: 0.9rem; margin-bottom: 12px; color: var(--text-secondary); }
/* Generic card — used throughout module pages */
.card {
background: var(--bg-card); border: 1px solid var(--border);
border-radius: var(--radius); padding: 20px; margin-bottom: 16px;
}
.card h3 { font-size: 0.95rem; margin-bottom: 12px; }
.card h4 { font-size: 0.85rem; margin-bottom: 8px; color: var(--text-secondary); }
/* Tab container — alias for .tab-bar */
.tabs { display: flex; gap: 0; border-bottom: 1px solid var(--border); margin-bottom: 20px; }
.tabs .tab { padding: 10px 18px; }
/* Tables */
.data-table { width: 100%; border-collapse: collapse; }
.data-table th {

View File

@ -2256,10 +2256,12 @@ var _DBG_LEVELS = {
WARNING: { cls: 'dbg-warn', sym: '⚠' },
ERROR: { cls: 'dbg-err', sym: '✕' },
CRITICAL: { cls: 'dbg-crit', sym: '☠' },
STDOUT: { cls: 'dbg-info', sym: '»' },
STDERR: { cls: 'dbg-warn', sym: '»' },
};
// Output-tagged logger names (treated as operational output in "Output Only" mode)
var _OUTPUT_LOGGERS = ['msf', 'agent', 'autarch', 'output', 'scanner', 'tools'];
var _OUTPUT_LOGGERS = ['msf', 'agent', 'autarch', 'output', 'scanner', 'tools', 'print'];
function debugToggle(enabled) {
fetch('/settings/debug/toggle', {
@ -2325,6 +2327,8 @@ function _dbgShouldShow(entry) {
case 'verbose': return lvl !== 'DEBUG' && lvl !== 'NOTSET';
case 'warn': return lvl === 'WARNING' || lvl === 'ERROR' || lvl === 'CRITICAL';
case 'output':
var lvlO = (entry.level || '').toUpperCase();
if (lvlO === 'STDOUT' || lvlO === 'STDERR') return true;
var name = (entry.name || '').toLowerCase();
return _OUTPUT_LOGGERS.some(function(pfx) { return name.indexOf(pfx) >= 0; });
}

View File

@ -29,6 +29,7 @@
<div class="nav-section">
<ul class="nav-links">
<li><a href="{{ url_for('dashboard.index') }}" class="{% if request.endpoint == 'dashboard.index' %}active{% endif %}">Dashboard</a></li>
<li><a href="{{ url_for('port_scanner.index') }}" class="{% if request.blueprint == 'port_scanner' %}active{% endif %}">&#x1F50D; Port Scanner</a></li>
<li><a href="{{ url_for('targets.index') }}" class="{% if request.blueprint == 'targets' %}active{% endif %}">Targets</a></li>
</ul>
</div>

View File

@ -97,7 +97,7 @@ let refreshTimer=null;
function switchTab(name){
document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',['dashboard','agents','generate'][i]===name));
document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
document.getElementById('tab-'+name).style.display='';
document.getElementById('tab-'+name).style.display='block';
if(name==='dashboard'||name==='agents') refreshDashboard();
}

View File

@ -1,29 +1,29 @@
{% extends "base.html" %}
{% block title %}Linux Defense - AUTARCH{% endblock %}
{% block title %}Linux Defense AUTARCH{% endblock %}
{% block content %}
<div class="page-header" style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
<div>
<h1>Linux Defense</h1>
<p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
Linux system hardening, iptables firewall management, and log analysis.
System hardening, iptables firewall management, and log analysis
</p>
</div>
<a href="{{ url_for('defense.index') }}" class="btn btn-sm" style="margin-left:auto">&larr; Defense</a>
</div>
<!-- Security Audit -->
<div class="section">
<h2>Security Audit</h2>
<div class="tool-actions">
<button id="btn-audit" class="btn btn-primary" onclick="linuxRunAudit()">Run Full Audit</button>
<div class="card">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem;flex-wrap:wrap;gap:0.5rem">
<h3 style="margin:0">Security Audit</h3>
<button id="btn-audit" class="btn btn-primary btn-sm" onclick="linuxRunAudit()">Run Full Audit</button>
</div>
<div style="display:flex;gap:24px;align-items:flex-start;flex-wrap:wrap">
<div class="score-display">
<div class="score-value" id="audit-score">--</div>
<div class="score-label">Security Score</div>
<div style="text-align:center;padding:12px 20px;background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius);min-width:120px">
<div id="audit-score" style="font-size:2.5rem;font-weight:700;line-height:1">--</div>
<div style="font-size:0.78rem;color:var(--text-secondary);margin-top:4px">Security Score</div>
</div>
<div style="flex:1;min-width:300px">
<div style="flex:1;min-width:280px">
<table class="data-table">
<thead><tr><th>Check</th><th>Status</th><th>Details</th></tr></thead>
<tbody id="audit-results">
@ -34,94 +34,53 @@
</div>
</div>
<!-- Individual Checks -->
<div class="section">
<h2>Quick Checks</h2>
<div class="tool-grid">
<div class="tool-card">
<h4>Firewall</h4>
<p>Check iptables/ufw/firewalld status</p>
<button class="btn btn-small" onclick="linuxRunCheck('firewall')">Run</button>
<pre class="output-panel tool-result" id="check-result-firewall"></pre>
</div>
<div class="tool-card">
<h4>SSH Config</h4>
<p>Check SSH hardening settings</p>
<button class="btn btn-small" onclick="linuxRunCheck('ssh')">Run</button>
<pre class="output-panel tool-result" id="check-result-ssh"></pre>
</div>
<div class="tool-card">
<h4>Open Ports</h4>
<p>Scan for high-risk listening ports</p>
<button class="btn btn-small" onclick="linuxRunCheck('ports')">Run</button>
<pre class="output-panel tool-result" id="check-result-ports"></pre>
</div>
<div class="tool-card">
<h4>Users</h4>
<p>Check UID 0 users and empty passwords</p>
<button class="btn btn-small" onclick="linuxRunCheck('users')">Run</button>
<pre class="output-panel tool-result" id="check-result-users"></pre>
</div>
<div class="tool-card">
<h4>Permissions</h4>
<p>Check critical file permissions</p>
<button class="btn btn-small" onclick="linuxRunCheck('permissions')">Run</button>
<pre class="output-panel tool-result" id="check-result-permissions"></pre>
</div>
<div class="tool-card">
<h4>Services</h4>
<p>Check for dangerous services</p>
<button class="btn btn-small" onclick="linuxRunCheck('services')">Run</button>
<pre class="output-panel tool-result" id="check-result-services"></pre>
<!-- Quick Checks -->
<div class="card">
<h3>Quick Checks</h3>
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px">
{% for check_id, check_name, check_desc in [
('firewall', 'Firewall', 'Check iptables/ufw/firewalld status'),
('ssh', 'SSH Config', 'Check SSH hardening settings'),
('ports', 'Open Ports', 'Scan for high-risk listening ports'),
('users', 'Users', 'Check UID 0 users and empty passwords'),
('permissions', 'Permissions', 'Check critical file permissions'),
('services', 'Services', 'Check for dangerous services')
] %}
<div style="background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius);padding:14px">
<div style="font-weight:600;font-size:0.88rem;margin-bottom:4px">{{ check_name }}</div>
<div style="color:var(--text-secondary);font-size:0.78rem;margin-bottom:10px">{{ check_desc }}</div>
<button class="btn btn-sm" style="width:100%" onclick="linuxRunCheck('{{ check_id }}')">Run</button>
<pre class="output-panel tool-result" id="check-result-{{ check_id }}" style="display:none;margin-top:10px;font-size:0.75rem;min-height:0"></pre>
</div>
{% endfor %}
</div>
</div>
<!-- Firewall Manager -->
<div class="section">
<h2>Firewall Manager (iptables)</h2>
<div class="tool-actions">
<button class="btn btn-small" onclick="linuxLoadFwRules()">Refresh Rules</button>
<div class="card">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem;flex-wrap:wrap;gap:0.5rem">
<h3 style="margin:0">Firewall Manager (iptables)</h3>
<button class="btn btn-sm" onclick="linuxLoadFwRules()">Refresh Rules</button>
</div>
<pre class="output-panel scrollable" id="fw-rules">Click "Refresh Rules" to load current iptables rules.</pre>
<div style="margin-top:12px">
<div class="input-row">
<input type="text" id="block-ip" placeholder="IP address to block">
<button class="btn btn-danger btn-small" onclick="linuxBlockIP()">Block IP</button>
<button class="btn btn-small" onclick="linuxUnblockIP()">Unblock IP</button>
</div>
<pre class="output-panel" id="fw-result" style="min-height:0"></pre>
<pre class="output-panel" id="fw-rules" style="max-height:300px;overflow-y:auto">Click "Refresh Rules" to load current iptables rules.</pre>
<div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap">
<input type="text" id="block-ip" class="form-control" placeholder="IP address to block" style="flex:1;min-width:180px">
<button class="btn btn-sm btn-danger" onclick="linuxBlockIP()">Block IP</button>
<button class="btn btn-sm" onclick="linuxUnblockIP()">Unblock IP</button>
</div>
<pre class="output-panel" id="fw-result" style="min-height:0;margin-top:8px"></pre>
</div>
<!-- Log Analysis -->
<div class="section">
<h2>Log Analysis</h2>
<div class="tool-actions">
<button id="btn-logs" class="btn btn-primary" onclick="linuxAnalyzeLogs()">Analyze Logs</button>
<div class="card">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem;flex-wrap:wrap;gap:0.5rem">
<h3 style="margin:0">Log Analysis</h3>
<button id="btn-logs" class="btn btn-primary btn-sm" onclick="linuxAnalyzeLogs()">Analyze Logs</button>
</div>
<pre class="output-panel scrollable" id="log-output">Click "Analyze Logs" to parse auth and web server logs.</pre>
<pre class="output-panel" id="log-output" style="max-height:350px;overflow-y:auto">Click "Analyze Logs" to parse auth and web server logs.</pre>
</div>
{% if modules %}
<div class="section">
<h2>Defense Modules</h2>
<ul class="module-list">
{% for name, info in modules.items() %}
<li class="module-item">
<div>
<div class="module-name">{{ name }}</div>
<div class="module-desc">{{ info.description }}</div>
</div>
<div class="module-meta">v{{ info.version }}</div>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<script>
/* ── Linux Defense (routes prefixed with /defense/linux/) ── */
function linuxRunAudit() {
var btn = document.getElementById('btn-audit');
setLoading(btn, true);

View File

@ -15,7 +15,9 @@
<!-- Scan Tab -->
<div id="tab-scan" class="tab-content active">
<div class="card" style="max-width:700px">
<div style="display:grid;grid-template-columns:360px 1fr;gap:1.5rem;align-items:start">
<!-- Config panel -->
<div class="card">
<h3>Target Scan</h3>
<div class="form-group">
<label>Target IP Address</label>
@ -34,10 +36,41 @@
<label>Custom Ports (comma-separated)</label>
<input type="text" id="hh-custom-ports" class="form-control" placeholder="22,80,443,445,4444,8080">
</div>
<button id="hh-scan-btn" class="btn btn-primary" onclick="startScan()">Scan for Compromises</button>
<div id="hh-scan-status" style="margin-top:1rem;display:none">
<div class="spinner-inline"></div>
<span id="hh-scan-msg">Scanning...</span>
<div style="display:flex;gap:0.5rem">
<button id="hh-scan-btn" class="btn btn-primary" onclick="startScan()" style="flex:1">Scan for Compromises</button>
<button id="hh-cancel-btn" class="btn" style="display:none;background:var(--danger);color:#fff" onclick="cancelScan()">Cancel</button>
</div>
<!-- Progress bar -->
<div id="hh-progress-wrap" style="display:none;margin-top:1rem">
<div style="display:flex;justify-content:space-between;font-size:0.8rem;margin-bottom:4px">
<span id="hh-prog-label">Scanning…</span>
<span id="hh-prog-pct">0%</span>
</div>
<div style="background:var(--border);border-radius:4px;height:6px;overflow:hidden">
<div id="hh-prog-bar" style="height:100%;background:var(--accent);transition:width 0.3s;width:0"></div>
</div>
</div>
</div>
<!-- Live output -->
<div class="card" style="display:flex;flex-direction:column">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.5rem">
<h3 style="margin:0">Live Output</h3>
<button class="btn btn-sm" onclick="clearOutput()">Clear</button>
</div>
<pre id="hh-output" style="background:#0d0d14;color:#c9d1d9;font-family:monospace;font-size:0.78rem;
padding:12px;border-radius:var(--radius);height:340px;overflow-y:auto;
white-space:pre-wrap;word-break:break-all;margin:0;border:1px solid var(--border)">Ready. Enter target and click Scan.</pre>
<!-- Live found ports mini-table -->
<div id="hh-live-ports" style="margin-top:0.75rem;display:none">
<h4 style="font-size:0.8rem;color:var(--text-secondary);margin-bottom:6px">Live Discovered Ports</h4>
<table class="data-table" style="font-size:0.78rem">
<thead><tr><th>Port</th><th>Service</th><th>Banner</th></tr></thead>
<tbody id="hh-live-ports-body"></tbody>
</table>
</div>
</div>
</div>
@ -141,8 +174,6 @@
.conf-high{color:var(--danger);font-weight:700}
.conf-medium{color:#f59e0b;font-weight:600}
.conf-low{color:var(--text-muted)}
.spinner-inline{display:inline-block;width:14px;height:14px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin 0.8s linear infinite;vertical-align:middle;margin-right:6px}
@keyframes spin{to{transform:rotate(360deg)}}
.cat-eternalblue{color:var(--danger)}
.cat-rat{color:#f59e0b}
.cat-shell{color:#6366f1}
@ -150,18 +181,26 @@
.cat-proxy{color:#8b5cf6}
.cat-miner{color:#06b6d4}
.cat-generic{color:var(--text-secondary)}
/* output line colors */
#hh-output .ln-status{color:#7dd3fc}
#hh-output .ln-open{color:#4ade80;font-weight:700}
#hh-output .ln-warn{color:#fbbf24}
#hh-output .ln-error{color:#f87171}
#hh-output .ln-done{color:#a78bfa;font-weight:700}
#hh-output .ln-prog{color:#6b7280}
</style>
<script>
let currentScanResult = null;
let currentSessionId = null;
let pollTimer = null;
let activeStream = null;
let currentJobId = null;
function switchTab(name){
document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',
['scan','results','sessions','history'][i]===name));
document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
document.getElementById('tab-'+name).style.display='';
document.getElementById('tab-'+name).style.display='block';
if(name==='sessions') loadSessions();
if(name==='history') loadHistory();
}
@ -171,57 +210,148 @@ function toggleCustomPorts(){
document.getElementById('hh-scan-type').value==='custom'?'':'none';
}
function appendOutput(text, cls){
const out = document.getElementById('hh-output');
const line = document.createElement('span');
if(cls) line.className = cls;
line.textContent = text + '\n';
out.appendChild(line);
out.scrollTop = out.scrollHeight;
}
function clearOutput(){
document.getElementById('hh-output').textContent = '';
}
function startScan(){
const target = document.getElementById('hh-target').value.trim();
if(!target){alert('Enter a target IP');return}
if(!target){ alert('Enter a target IP'); return; }
const scanType = document.getElementById('hh-scan-type').value;
let customPorts = [];
if(scanType === 'custom'){
customPorts = document.getElementById('hh-custom-ports').value
.split(',').map(p=>parseInt(p.trim())).filter(p=>p>0&&p<65536);
if(!customPorts.length){alert('Enter valid ports');return}
if(!customPorts.length){ alert('Enter valid ports'); return; }
}
// Reset UI
clearOutput();
document.getElementById('hh-live-ports').style.display = 'none';
document.getElementById('hh-live-ports-body').innerHTML = '';
document.getElementById('hh-scan-btn').disabled = true;
document.getElementById('hh-scan-status').style.display='';
document.getElementById('hh-scan-msg').textContent='Scanning '+target+'...';
document.getElementById('hh-cancel-btn').style.display = '';
document.getElementById('hh-progress-wrap').style.display = '';
document.getElementById('hh-prog-bar').style.width = '0';
document.getElementById('hh-prog-pct').textContent = '0%';
document.getElementById('hh-prog-label').textContent = 'Starting…';
appendOutput(`[*] Starting ${scanType} scan on ${target}…`, 'ln-status');
fetch('/hack-hijack/scan', {method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({target, scan_type: scanType, custom_ports: customPorts})})
.then(r=>r.json()).then(d=>{
if(!d.ok){showScanError(d.error);return}
pollScan(d.job_id);
}).catch(e=>showScanError(e.message));
if(!d.ok){ scanDone(false, d.error); return; }
currentJobId = d.job_id;
openStream(d.job_id);
}).catch(e=>scanDone(false, e.message));
}
function pollScan(jobId){
if(pollTimer) clearInterval(pollTimer);
pollTimer=setInterval(()=>{
fetch('/hack-hijack/scan/'+jobId).then(r=>r.json()).then(d=>{
if(!d.done) return;
clearInterval(pollTimer);pollTimer=null;
document.getElementById('hh-scan-btn').disabled=false;
document.getElementById('hh-scan-status').style.display='none';
if(!d.ok){showScanError(d.error);return}
function openStream(jobId){
if(activeStream){ activeStream.close(); activeStream = null; }
const es = new EventSource('/hack-hijack/scan/' + jobId + '/stream');
activeStream = es;
es.onmessage = function(e){
try{ handleEvent(JSON.parse(e.data)); } catch(ex){}
};
es.onerror = function(){
es.close(); activeStream = null;
appendOutput('[!] Stream connection lost', 'ln-error');
scanDone(false, 'Stream disconnected');
};
}
function handleEvent(ev){
switch(ev.type){
case 'progress': {
const pct = ev.pct || 0;
document.getElementById('hh-prog-bar').style.width = pct + '%';
document.getElementById('hh-prog-pct').textContent = pct + '%';
if(ev.msg) document.getElementById('hh-prog-label').textContent = ev.msg;
if(ev.msg) appendOutput('[~] ' + ev.msg, 'ln-prog');
break;
}
case 'status':
appendOutput('[*] ' + ev.msg, 'ln-status');
document.getElementById('hh-prog-label').textContent = ev.msg;
break;
case 'port_found': {
const svc = ev.service ? ` (${ev.service})` : '';
const banner = ev.banner ? ` "${ev.banner.slice(0,60)}"` : '';
appendOutput(`[+] OPEN ${ev.port}/tcp${svc}${banner}`, 'ln-open');
addLivePort(ev.port, ev.service, ev.banner);
break;
}
case 'error':
appendOutput('[!] ' + ev.msg, 'ln-error');
break;
case 'done':
if(activeStream){ activeStream.close(); activeStream = null; }
document.getElementById('hh-prog-bar').style.width = '100%';
document.getElementById('hh-prog-pct').textContent = '100%';
if(ev.ok){
appendOutput('[✓] Scan complete — fetching results…', 'ln-done');
fetchResult(currentJobId);
} else {
appendOutput('[!] Scan failed', 'ln-error');
scanDone(false, 'Scan failed');
}
break;
}
}
function addLivePort(port, service, banner){
const tbody = document.getElementById('hh-live-ports-body');
document.getElementById('hh-live-ports').style.display = '';
const tr = document.createElement('tr');
tr.innerHTML = `<td>${port}</td><td>${esc(service||'—')}</td>
<td style="font-family:monospace;font-size:0.72rem;max-width:300px;overflow:hidden;text-overflow:ellipsis">${esc((banner||'').slice(0,80))}</td>`;
tbody.appendChild(tr);
}
function fetchResult(jobId){
fetch('/hack-hijack/scan/' + jobId)
.then(r=>r.json()).then(d=>{
if(!d.done){ setTimeout(()=>fetchResult(jobId), 500); return; }
if(!d.ok){ scanDone(false, d.error); return; }
currentScanResult = d.result;
appendOutput(`[✓] Done — ${d.result.open_ports.length} ports, ${d.result.backdoors.length} backdoor indicators`, 'ln-done');
scanDone(true);
renderResults(d.result);
switchTab('results');
}).catch(()=>{});
},1500);
}).catch(e=>scanDone(false, e.message));
}
function showScanError(msg){
function scanDone(ok, errMsg){
document.getElementById('hh-scan-btn').disabled = false;
document.getElementById('hh-scan-status').style.display='none';
alert('Scan error: '+msg);
document.getElementById('hh-cancel-btn').style.display = 'none';
document.getElementById('hh-progress-wrap').style.display = 'none';
if(!ok && errMsg) appendOutput('[!] Error: ' + errMsg, 'ln-error');
currentJobId = null;
}
function cancelScan(){
if(activeStream){ activeStream.close(); activeStream = null; }
appendOutput('[x] Scan cancelled by user', 'ln-warn');
scanDone(false);
}
function renderResults(r){
document.getElementById('hh-no-results').style.display='none';
document.getElementById('hh-results').style.display='';
document.getElementById('res-target').textContent=r.target;
document.getElementById('res-time').textContent=r.scan_time.replace('T',' ').slice(0,19)+' UTC';
document.getElementById('res-ports-count').textContent=r.open_ports.length;
document.getElementById('res-backdoors-count').textContent=r.backdoors.length;
document.getElementById('res-time').textContent=(r.scan_time||'').replace('T',' ').slice(0,19)+' UTC';
document.getElementById('res-ports-count').textContent=(r.open_ports||[]).length;
document.getElementById('res-backdoors-count').textContent=(r.backdoors||[]).length;
document.getElementById('res-duration').textContent=r.duration;
if(r.os_guess){
document.getElementById('res-os').style.display='';
@ -231,7 +361,7 @@ function renderResults(r){
// Ports table
const pb=document.getElementById('hh-ports-body');
pb.innerHTML='';
r.open_ports.forEach(p=>{
(r.open_ports||[]).forEach(p=>{
const tr=document.createElement('tr');
tr.innerHTML=`<td>${p.port}</td><td>${p.protocol}</td><td>${p.service||'—'}</td>
<td style="font-family:monospace;font-size:0.75rem;max-width:400px;overflow:hidden;text-overflow:ellipsis">${esc(p.banner||'')}</td>`;
@ -241,7 +371,7 @@ function renderResults(r){
// Backdoors
const bs=document.getElementById('hh-backdoors-section');
const bb=document.getElementById('hh-backdoors-body');
if(r.backdoors.length){
if((r.backdoors||[]).length){
bs.style.display='';
bb.innerHTML='';
r.backdoors.forEach((b,i)=>{
@ -285,7 +415,6 @@ function tryTakeover(idx){
} else {
alert(d.message||d.error||'Takeover result received');
if(d.msf_command){
// Copy MSF command to clipboard
navigator.clipboard.writeText(d.msf_command).then(()=>{
alert('MSF command copied to clipboard');
}).catch(()=>{});
@ -311,7 +440,7 @@ function loadSessions(){
onclick="openShell('${esc(s.session_id)}','${esc(s.host)}:${s.port}','')">
<div style="display:flex;justify-content:space-between;align-items:center">
<div><strong>${esc(s.type)}</strong> &rarr; ${esc(s.host)}:${s.port}</div>
<div style="font-size:0.75rem;color:var(--text-muted)">${s.connected_at.slice(0,19)}</div>
<div style="font-size:0.75rem;color:var(--text-muted)">${(s.connected_at||'').slice(0,19)}</div>
</div></div>`).join('');
});
}

View File

@ -90,7 +90,7 @@ let discPoll=null;
function switchTab(name){
document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',['discover','map','scans'][i]===name));
document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
document.getElementById('tab-'+name).style.display='';
document.getElementById('tab-'+name).style.display='block';
if(name==='scans') loadScans();
}

View File

@ -201,7 +201,7 @@ function switchTab(name){
document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',
['identify','crack','generate','spray','wordlists'][i]===name));
document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
document.getElementById('tab-'+name).style.display='';
document.getElementById('tab-'+name).style.display='block';
if(name==='crack') loadTools();
if(name==='wordlists') loadWordlists();
}

View File

@ -0,0 +1,402 @@
{% extends "base.html" %}
{% block title %}Port Scanner — AUTARCH{% endblock %}
{% block content %}
<div class="page-header">
<h1>Port Scanner</h1>
<p class="text-muted" style="font-size:0.85rem;color:var(--text-secondary)">
Advanced TCP port scanner with nmap integration and real-time live output
</p>
</div>
<div style="display:grid;grid-template-columns:380px 1fr;gap:1.25rem;align-items:start">
<!-- ── Config Panel ── -->
<div>
<div class="card">
<h3>Scan Configuration</h3>
<div class="form-group">
<label>Target Host / IP</label>
<input type="text" id="ps-target" class="form-control"
placeholder="192.168.1.1 or hostname.local"
onkeypress="if(event.key==='Enter')startScan()">
</div>
<div class="form-group">
<label>Scan Mode</label>
<select id="ps-mode" class="form-control" onchange="onModeChange()">
<option value="quick">Quick — 22 common ports (~1s)</option>
<option value="common" selected>Common — 110 well-known ports (~5s)</option>
<option value="full">Full — All 65,535 ports (may take minutes)</option>
<option value="custom">Custom — Specify ports / ranges</option>
</select>
</div>
<div class="form-group" id="custom-ports-group" style="display:none">
<label>Ports / Ranges (e.g. 22,80,443,8000-9000)</label>
<input type="text" id="ps-custom-ports" class="form-control"
placeholder="22,80,443,1024-2048">
</div>
<!-- Options -->
<div style="padding:12px;background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius);margin-bottom:14px">
<div style="font-size:0.75rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:10px">Options</div>
<div style="display:flex;flex-wrap:wrap;gap:10px;margin-bottom:10px">
<label style="display:flex;align-items:center;gap:6px;font-size:0.83rem;cursor:pointer">
<input type="checkbox" id="opt-nmap" style="width:auto"> Use nmap
</label>
<label style="display:flex;align-items:center;gap:6px;font-size:0.83rem;cursor:pointer">
<input type="checkbox" id="opt-svc" style="width:auto"> Service detection (-sV)
</label>
<label style="display:flex;align-items:center;gap:6px;font-size:0.83rem;cursor:pointer">
<input type="checkbox" id="opt-os" style="width:auto"> OS fingerprint (-O)
</label>
<label style="display:flex;align-items:center;gap:6px;font-size:0.83rem;cursor:pointer">
<input type="checkbox" id="opt-agg" style="width:auto"> Aggressive (-A)
</label>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px">
<div>
<div style="font-size:0.75rem;color:var(--text-muted);margin-bottom:3px">Timeout (s)</div>
<input type="number" id="opt-timeout" class="form-control" value="1.0" min="0.1" max="10" step="0.1">
</div>
<div>
<div style="font-size:0.75rem;color:var(--text-muted);margin-bottom:3px">Concurrency</div>
<input type="number" id="opt-concurrency" class="form-control" value="200" min="1" max="500">
</div>
<div>
<div style="font-size:0.75rem;color:var(--text-muted);margin-bottom:3px">nmap Timing</div>
<select id="opt-timing" class="form-control">
<option value="2">T2 — Polite</option>
<option value="3">T3 — Normal</option>
<option value="4" selected>T4 — Aggressive</option>
<option value="5">T5 — Insane</option>
</select>
</div>
</div>
</div>
<div style="display:flex;gap:8px">
<button id="ps-start-btn" class="btn btn-primary" style="flex:1" onclick="startScan()">
&#x25B6; Start Scan
</button>
<button id="ps-cancel-btn" class="btn btn-danger btn-small" style="display:none"
onclick="cancelScan()">Cancel</button>
</div>
</div>
<!-- Stats Card -->
<div class="card" id="stats-card" style="display:none">
<h3>Scan Stats</h3>
<table style="width:100%;font-size:0.85rem">
<tr><td style="color:var(--text-muted);padding:3px 0">Target</td>
<td id="st-target" style="text-align:right;color:var(--accent)"></td></tr>
<tr><td style="color:var(--text-muted);padding:3px 0">Ports Scanned</td>
<td id="st-scanned" style="text-align:right"></td></tr>
<tr><td style="color:var(--text-muted);padding:3px 0">Open Ports</td>
<td id="st-open" style="text-align:right;color:var(--success);font-weight:700"></td></tr>
<tr><td style="color:var(--text-muted);padding:3px 0">Duration</td>
<td id="st-duration" style="text-align:right"></td></tr>
<tr id="st-os-row" style="display:none">
<td style="color:var(--text-muted);padding:3px 0">OS Guess</td>
<td id="st-os" style="text-align:right;font-size:0.8rem"></td>
</tr>
</table>
</div>
</div>
<!-- ── Right Panel — Output + Results ── -->
<div>
<!-- Progress bar -->
<div id="ps-progress-wrap" style="display:none;margin-bottom:1rem">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
<span id="ps-progress-label" style="font-size:0.82rem;color:var(--text-secondary)">Initializing...</span>
<span id="ps-progress-pct" style="font-size:0.82rem;font-weight:600;color:var(--accent)">0%</span>
</div>
<div style="background:var(--bg-input);border-radius:4px;height:6px;overflow:hidden">
<div id="ps-progress-bar" style="height:100%;background:var(--accent);
transition:width 0.3s;border-radius:4px;width:0%"></div>
</div>
</div>
<!-- Live output box -->
<div class="card" style="padding:0;overflow:hidden">
<div style="display:flex;justify-content:space-between;align-items:center;
padding:10px 16px;border-bottom:1px solid var(--border)">
<span style="font-size:0.8rem;font-weight:600;letter-spacing:0.05em">LIVE OUTPUT</span>
<div style="display:flex;gap:6px;align-items:center">
<span id="ps-status-dot" style="width:8px;height:8px;border-radius:50%;
background:var(--text-muted);display:inline-block"></span>
<span id="ps-status-txt" style="font-size:0.75rem;color:var(--text-muted)">Idle</span>
<button class="btn btn-small" style="font-size:0.7rem;padding:3px 8px"
onclick="clearOutput()">Clear</button>
</div>
</div>
<pre id="ps-output" style="background:var(--bg-primary);color:#c8d3f5;
font-family:'Cascadia Code','Fira Code','Consolas',monospace;
font-size:0.78rem;line-height:1.5;padding:14px;margin:0;
height:340px;overflow-y:auto;white-space:pre-wrap;word-break:break-all"></pre>
</div>
<!-- Results table -->
<div id="ps-results-wrap" style="display:none;margin-top:1rem">
<div class="card" style="padding:0;overflow:hidden">
<div style="display:flex;justify-content:space-between;align-items:center;
padding:12px 16px;border-bottom:1px solid var(--border)">
<span style="font-weight:600;font-size:0.9rem">
Open Ports — <span id="res-count" style="color:var(--success)">0</span> found
</span>
<div style="display:flex;gap:6px">
<button class="btn btn-small" onclick="exportResults('txt')">Export TXT</button>
<button class="btn btn-small" onclick="exportResults('json')">Export JSON</button>
</div>
</div>
<table class="data-table">
<thead>
<tr>
<th style="width:80px">Port</th>
<th style="width:70px">Proto</th>
<th style="width:150px">Service</th>
<th>Banner / Version</th>
</tr>
</thead>
<tbody id="ps-results-body"></tbody>
</table>
</div>
</div>
</div>
</div>
<style>
#ps-output .ln-info { color: #c8d3f5; }
#ps-output .ln-open { color: #4ade80; font-weight: 700; }
#ps-output .ln-warn { color: #fbbf24; }
#ps-output .ln-error { color: #f87171; }
#ps-output .ln-nmap { color: #94a3b8; font-size: 0.73rem; }
#ps-output .ln-prog { color: #818cf8; }
#ps-output .ln-done { color: #34d399; font-weight: 700; }
#ps-output .ln-muted { color: #475569; }
.advanced-toggle { font-size: 0.82rem; color: var(--text-secondary); cursor: pointer;
user-select: none; display: inline-flex; align-items: center; gap: 4px; }
.advanced-toggle:hover { color: var(--text-primary); }
</style>
<script>
let _jobId = null;
let _es = null;
let _openPorts = [];
let _scanResult = null;
function onModeChange() {
const mode = document.getElementById('ps-mode').value;
document.getElementById('custom-ports-group').style.display =
mode === 'custom' ? '' : 'none';
}
function startScan() {
const target = document.getElementById('ps-target').value.trim();
if (!target) { alert('Enter a target host or IP address'); return; }
const mode = document.getElementById('ps-mode').value;
if (mode === 'custom' && !document.getElementById('ps-custom-ports').value.trim()) {
alert('Enter ports or ranges for custom mode'); return;
}
_openPorts = [];
_scanResult = null;
clearOutput();
document.getElementById('ps-results-wrap').style.display = 'none';
document.getElementById('stats-card').style.display = 'none';
document.getElementById('ps-progress-wrap').style.display = '';
const payload = {
target,
mode,
ports: document.getElementById('ps-custom-ports').value.trim(),
use_nmap: document.getElementById('opt-nmap').checked,
service_detection: document.getElementById('opt-svc').checked,
os_detection: document.getElementById('opt-os').checked,
aggressive: document.getElementById('opt-agg').checked,
timing: document.getElementById('opt-timing').value,
timeout: parseFloat(document.getElementById('opt-timeout').value) || 1.0,
concurrency: parseInt(document.getElementById('opt-concurrency').value) || 200,
};
fetch('/port-scanner/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}).then(r => r.json()).then(d => {
if (!d.ok) { appendLine('error', '✗ ' + d.error); return; }
_jobId = d.job_id;
setStatus('scanning', 'Scanning...');
document.getElementById('ps-start-btn').disabled = true;
document.getElementById('ps-cancel-btn').style.display = '';
appendLine('info', `► Scan started | job=${d.job_id} | ${d.port_count} ports queued`);
openStream(d.job_id);
}).catch(e => appendLine('error', '✗ ' + e.message));
}
function openStream(jobId) {
if (_es) { _es.close(); _es = null; }
_es = new EventSource('/port-scanner/stream/' + jobId);
_es.onmessage = e => handleEvent(JSON.parse(e.data));
_es.onerror = () => { setStatus('idle', 'Connection lost'); };
}
function handleEvent(ev) {
switch (ev.type) {
case 'start':
appendLine('info', `▶ Target: ${ev.target} | Ports: ${ev.total_ports} | Mode: ${ev.mode}`);
setProgress(0, ev.total_ports, '0%', 'Starting...');
document.getElementById('st-target').textContent = ev.target;
document.getElementById('st-scanned').textContent = ev.total_ports;
document.getElementById('stats-card').style.display = '';
break;
case 'info':
appendLine('info', ' ' + ev.msg);
break;
case 'warning':
appendLine('warn', '⚠ ' + ev.msg);
break;
case 'error':
appendLine('error', '✗ ' + ev.msg);
scanFinished();
break;
case 'progress':
setProgress(ev.current, ev.total, ev.pct + '%', `Port ${ev.port} | ${ev.open_count} open | ETA ${ev.eta}`);
break;
case 'port_open':
_openPorts.push(ev);
const svc = ev.service || 'unknown';
const banner = ev.banner ? ` — ${ev.banner.substring(0, 60)}` : '';
appendLine('open', ` ✔ ${String(ev.port).padEnd(6)} ${svc.padEnd(18)}${banner}`);
addResultRow(ev);
document.getElementById('res-count').textContent = _openPorts.length;
document.getElementById('st-open').textContent = _openPorts.length;
document.getElementById('ps-results-wrap').style.display = '';
break;
case 'nmap_start':
appendLine('info', ` nmap: ${ev.cmd}`);
break;
case 'nmap_line':
if (ev.line.trim()) appendLine('nmap', ' ' + ev.line);
break;
case 'done':
appendLine('done',
`\n✔ SCAN COMPLETE — ${ev.open_count} open ports found in ${ev.duration}s ` +
`(${ev.ports_scanned} ports scanned)`
);
if (ev.os_guess) {
appendLine('info', ` OS: ${ev.os_guess}`);
document.getElementById('st-os-row').style.display = '';
document.getElementById('st-os').textContent = ev.os_guess;
}
document.getElementById('st-duration').textContent = ev.duration + 's';
setProgress(100, 100, '100%', 'Complete');
scanFinished();
break;
}
}
function addResultRow(port) {
const tbody = document.getElementById('ps-results-body');
const tr = document.createElement('tr');
const banner = port.banner
? `<span style="font-family:monospace;font-size:0.75rem;color:var(--text-secondary)">${esc(port.banner.substring(0, 120))}</span>`
: '<span style="color:var(--text-muted)"></span>';
tr.innerHTML = `
<td><strong style="color:var(--success)">${port.port}</strong></td>
<td style="color:var(--text-muted)">${port.protocol}</td>
<td>${esc(port.service || '—')}</td>
<td>${banner}</td>`;
tbody.appendChild(tr);
}
function cancelScan() {
if (!_jobId) return;
fetch('/port-scanner/cancel/' + _jobId, { method: 'POST' });
appendLine('warn', '⚠ Scan cancelled by user');
scanFinished();
}
function scanFinished() {
if (_es) { _es.close(); _es = null; }
document.getElementById('ps-start-btn').disabled = false;
document.getElementById('ps-cancel-btn').style.display = 'none';
setStatus('idle', _openPorts.length + ' open ports');
_jobId = null;
}
function setStatus(state, text) {
const dot = document.getElementById('ps-status-dot');
const txt = document.getElementById('ps-status-txt');
txt.textContent = text;
dot.style.background = state === 'scanning' ? 'var(--success)'
: state === 'error' ? 'var(--danger)'
: 'var(--text-muted)';
if (state === 'scanning') {
dot.style.animation = 'ps-pulse 1.2s ease infinite';
} else {
dot.style.animation = '';
}
}
function setProgress(current, total, pct, label) {
document.getElementById('ps-progress-bar').style.width = (typeof pct === 'string' ? pct : pct + '%');
document.getElementById('ps-progress-pct').textContent = (typeof pct === 'string' ? pct : pct + '%');
document.getElementById('ps-progress-label').textContent = label;
}
function appendLine(cls, text) {
const out = document.getElementById('ps-output');
const el = document.createElement('span');
el.className = 'ln-' + cls;
el.textContent = text + '\n';
out.appendChild(el);
out.scrollTop = out.scrollHeight;
}
function clearOutput() {
document.getElementById('ps-output').innerHTML = '';
}
function exportResults(fmt) {
if (!_openPorts.length) { alert('No results to export'); return; }
let content, mime, ext;
if (fmt === 'json') {
content = JSON.stringify({ target: document.getElementById('ps-target').value,
scan_time: new Date().toISOString(),
open_ports: _openPorts }, null, 2);
mime = 'application/json'; ext = 'json';
} else {
const target = document.getElementById('ps-target').value;
const lines = [`# AUTARCH Port Scan Results`, `# Target: ${target}`,
`# Date: ${new Date().toISOString()}`, `# Open Ports: ${_openPorts.length}`, ''];
_openPorts.forEach(p => {
lines.push(`${p.port}/tcp\topen\t${p.service || 'unknown'}\t${p.banner || ''}`);
});
content = lines.join('\n'); mime = 'text/plain'; ext = 'txt';
}
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([content], { type: mime }));
a.download = `scan_${document.getElementById('ps-target').value}_${Date.now()}.${ext}`;
a.click();
}
function esc(s) {
return s ? String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') : '';
}
</script>
<style>
@keyframes ps-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
</style>
{% endblock %}

View File

@ -104,7 +104,7 @@ let currentReportId=null;
function switchTab(name){
document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',['reports','editor','templates'][i]===name));
document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
document.getElementById('tab-'+name).style.display='';
document.getElementById('tab-'+name).style.display='block';
if(name==='reports') loadReports();
if(name==='templates') loadTemplates();
}

View File

@ -323,7 +323,7 @@ function switchTab(name){
document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',
['harvest','pretexts','qr','campaigns'][i]===name));
document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
document.getElementById('tab-'+name).style.display='';
document.getElementById('tab-'+name).style.display='block';
if(name==='harvest'){loadPages();loadCaptures();}
if(name==='pretexts') loadPretexts();
if(name==='campaigns') loadCampaigns();

View File

@ -110,7 +110,7 @@ function switchTab(name){
document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',
['quick','dirbust','subdomain','vuln','crawl'][i]===name));
document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
document.getElementById('tab-'+name).style.display='';
document.getElementById('tab-'+name).style.display='block';
}
function quickScan(){