Autarch/web/templates/offense.html
DigiJ 2322f69516 v2.2.0 — Full arsenal expansion: 16 new security modules
Add WiFi Audit, API Fuzzer, Cloud Scanner, Threat Intel, Log Correlator,
Steganography, Anti-Forensics, BLE Scanner, Forensics, RFID/NFC, Malware
Sandbox, Password Toolkit, Web Scanner, Report Engine, Net Mapper, and
C2 Framework. Each module includes CLI interface, Flask routes, and web
UI template. Also includes Go DNS server source + binary, IP Capture
service, SYN Flood, Gone Fishing mail server, and hack hijack modules
from v2.0 work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 05:20:39 -08:00

686 lines
38 KiB
HTML

{% extends "base.html" %}
{% block title %}Offense - AUTARCH{% endblock %}
{% block content %}
<div class="page-header">
<h1>Offense</h1>
</div>
<!-- MSF Server Control -->
<div class="section">
<h2>Metasploit Server</h2>
<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
<div class="status-indicator" id="msf-status">
<span class="status-dot inactive"></span>Checking...
</div>
<div id="msf-version" style="font-size:0.8rem;color:var(--text-muted)"></div>
<div style="flex:1"></div>
<button id="btn-connect" class="btn btn-primary btn-small" onclick="msfConnect()" style="display:none">Connect</button>
<button id="btn-disconnect" class="btn btn-small" onclick="msfDisconnect()" style="display:none">Disconnect</button>
<button id="btn-start-server" class="btn btn-small" onclick="toggleServerPanel()" style="display:none">Start Server</button>
<button id="btn-stop-server" class="btn btn-danger btn-small" onclick="msfStopServer()" style="display:none">Stop Server</button>
</div>
<!-- Server config panel (hidden by default) -->
<div id="server-panel" style="display:none;margin-top:0.75rem;padding:0.75rem;background:var(--surface);border:1px solid var(--border);border-radius:6px">
<div style="display:grid;grid-template-columns:1fr 80px 1fr 1fr;gap:0.5rem;align-items:end">
<div class="form-group" style="margin-bottom:0">
<label for="msf-host">Host</label>
<input type="text" id="msf-host" value="127.0.0.1">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="msf-port">Port</label>
<input type="text" id="msf-port" value="55553">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="msf-user">Username</label>
<input type="text" id="msf-user" value="msf">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="msf-pass">Password</label>
<input type="password" id="msf-pass" placeholder="required">
</div>
</div>
<div style="display:flex;align-items:center;gap:0.75rem;margin-top:0.5rem">
<label style="font-size:0.85rem;display:flex;align-items:center;gap:0.3rem;cursor:pointer">
<input type="checkbox" id="msf-ssl" checked> SSL
</label>
<div style="flex:1"></div>
<button class="btn btn-small" onclick="msfSaveSettings()">Save Settings</button>
<button class="btn btn-primary btn-small" onclick="msfStartServer()">Start &amp; Connect</button>
<button class="btn btn-small" onclick="msfConnectOnly()">Connect Only</button>
</div>
<div id="server-msg" style="font-size:0.8rem;color:var(--text-muted);margin-top:0.5rem"></div>
</div>
</div>
<!-- Module Search -->
<div class="section">
<h2>Module Search</h2>
<div class="input-row">
<input type="text" id="msf-search" placeholder="Search modules (e.g., eternalblue, smb, ssh)" onkeypress="if(event.key==='Enter')searchMSFModules()">
<button id="btn-msf-search" class="btn btn-primary" onclick="searchMSFModules()">Search</button>
</div>
<div id="msf-search-results" class="results-stream" style="max-height:400px;overflow-y:auto">
<div class="empty-state">Search the offline module library or connected MSF instance.</div>
</div>
</div>
<!-- Run Module -->
<div class="section">
<h2>Run Module</h2>
<div class="tab-bar">
<button class="tab active" data-tab-group="run-tabs" data-tab="ssh" onclick="showTab('run-tabs','ssh')">SSH</button>
<button class="tab" data-tab-group="run-tabs" data-tab="portscan" onclick="showTab('run-tabs','portscan')">Port Scan</button>
<button class="tab" data-tab-group="run-tabs" data-tab="osdetect" onclick="showTab('run-tabs','osdetect')">OS Detect</button>
<button class="tab" data-tab-group="run-tabs" data-tab="vuln" onclick="showTab('run-tabs','vuln')">Vuln Scan</button>
<button class="tab" data-tab-group="run-tabs" data-tab="smb" onclick="showTab('run-tabs','smb')">SMB</button>
<button class="tab" data-tab-group="run-tabs" data-tab="http" onclick="showTab('run-tabs','http')">HTTP</button>
<button class="tab" data-tab-group="run-tabs" data-tab="exploit" onclick="showTab('run-tabs','exploit')">Exploit</button>
<button class="tab" data-tab-group="run-tabs" data-tab="custom" onclick="showTab('run-tabs','custom')">Custom</button>
</div>
<!-- SSH tab -->
<div class="tab-content active" data-tab-group="run-tabs" data-tab="ssh">
<div style="display:grid;grid-template-columns:1fr auto auto;gap:0.75rem;align-items:end;margin-top:0.75rem">
<div class="form-group" style="margin-bottom:0">
<label for="ssh-rhosts">Target(s)</label>
<input type="text" id="ssh-rhosts" placeholder="192.168.1.0/24 or 10.0.0.5">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="ssh-rport">Port</label>
<input type="text" id="ssh-rport" value="22" style="width:70px">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="ssh-threads">Threads</label>
<input type="text" id="ssh-threads" value="10" style="width:60px">
</div>
</div>
<div class="tool-actions" style="margin-top:0.75rem">
<button class="btn btn-primary" onclick="runFeaturedModule('ssh')">Run SSH Version Scan</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('ssh-enum')">SSH Enum Users</button>
<button class="btn btn-secondary" onclick="toggleBruteRow()">SSH Brute-Force &darr;</button>
</div>
<div id="ssh-brute-row" style="display:none;margin-top:0.5rem">
<div style="display:grid;grid-template-columns:1fr 1fr auto;gap:0.75rem;align-items:end">
<div class="form-group" style="margin-bottom:0">
<label for="ssh-username">Username</label>
<input type="text" id="ssh-username" placeholder="root">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="ssh-password">Password</label>
<input type="text" id="ssh-password" placeholder="password">
</div>
<button class="btn btn-danger" onclick="runFeaturedModule('ssh-brute')">Run Brute-Force</button>
</div>
</div>
</div>
<!-- Port Scan tab -->
<div class="tab-content" data-tab-group="run-tabs" data-tab="portscan">
<div style="display:grid;grid-template-columns:1fr auto auto;gap:0.75rem;align-items:end;margin-top:0.75rem">
<div class="form-group" style="margin-bottom:0">
<label for="ps-rhosts">Target(s)</label>
<input type="text" id="ps-rhosts" placeholder="192.168.1.0/24 or 10.0.0.5">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="ps-ports">Ports</label>
<input type="text" id="ps-ports" value="1-1024" style="width:100px">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="ps-threads">Threads</label>
<input type="text" id="ps-threads" value="10" style="width:60px">
</div>
</div>
<div class="tool-actions" style="margin-top:0.75rem">
<button class="btn btn-primary" onclick="runFeaturedModule('tcp-scan')">TCP Scan</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('syn-scan')">SYN Scan</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('ack-scan')">ACK Scan</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('udp-scan')">UDP Sweep</button>
</div>
</div>
<!-- OS Detect tab -->
<div class="tab-content" data-tab-group="run-tabs" data-tab="osdetect">
<div style="margin-top:0.75rem">
<div class="form-group">
<label for="os-rhosts">Target(s)</label>
<input type="text" id="os-rhosts" placeholder="192.168.1.0/24 or 10.0.0.5">
</div>
</div>
<div class="tool-actions">
<button class="btn btn-primary" onclick="runFeaturedModule('smb-version')">SMB Version</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('http-header')">HTTP Header</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('ftp-version')">FTP Version</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('telnet-version')">Telnet Version</button>
</div>
</div>
<!-- Vuln Scan tab -->
<div class="tab-content" data-tab-group="run-tabs" data-tab="vuln">
<div style="display:grid;grid-template-columns:1fr auto;gap:0.75rem;align-items:end;margin-top:0.75rem">
<div class="form-group" style="margin-bottom:0">
<label for="vuln-rhosts">Target(s)</label>
<input type="text" id="vuln-rhosts" placeholder="192.168.1.0/24 or 10.0.0.5">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="vuln-threads">Threads</label>
<input type="text" id="vuln-threads" value="5" style="width:60px">
</div>
</div>
<div class="tool-actions" style="margin-top:0.75rem">
<button class="btn btn-danger" onclick="runFeaturedModule('eternalblue-check')">EternalBlue Check</button>
<button class="btn btn-danger" onclick="runFeaturedModule('bluekeep-check')">BlueKeep Check</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('ssl-heartbleed')">Heartbleed Check</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('shellshock-check')">Shellshock Check</button>
</div>
</div>
<!-- SMB tab -->
<div class="tab-content" data-tab-group="run-tabs" data-tab="smb">
<div style="display:grid;grid-template-columns:1fr auto auto;gap:0.75rem;align-items:end;margin-top:0.75rem">
<div class="form-group" style="margin-bottom:0">
<label for="smb-rhosts">Target(s)</label>
<input type="text" id="smb-rhosts" placeholder="192.168.1.0/24 or 10.0.0.5">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="smb-user">Username</label>
<input type="text" id="smb-user" placeholder="admin" style="width:100px">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="smb-pass">Password</label>
<input type="text" id="smb-pass" placeholder="" style="width:100px">
</div>
</div>
<div class="tool-actions" style="margin-top:0.75rem">
<button class="btn btn-primary" onclick="runFeaturedModule('smb-enum-shares')">Enum Shares</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('smb-enum-users')">Enum Users</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('smb-login')">SMB Login</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('smb-pipe-auditor')">Pipe Auditor</button>
</div>
</div>
<!-- HTTP tab -->
<div class="tab-content" data-tab-group="run-tabs" data-tab="http">
<div style="display:grid;grid-template-columns:1fr auto auto;gap:0.75rem;align-items:end;margin-top:0.75rem">
<div class="form-group" style="margin-bottom:0">
<label for="http-rhosts">Target(s)</label>
<input type="text" id="http-rhosts" placeholder="192.168.1.0/24 or example.com">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="http-rport">Port</label>
<input type="text" id="http-rport" value="80" style="width:70px">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="http-threads">Threads</label>
<input type="text" id="http-threads" value="5" style="width:60px">
</div>
</div>
<div style="margin-top:0.5rem">
<div class="form-group" style="margin-bottom:0">
<label for="http-targeturi">Target URI</label>
<input type="text" id="http-targeturi" value="/" placeholder="/">
</div>
</div>
<div class="tool-actions" style="margin-top:0.75rem">
<button class="btn btn-primary" onclick="runFeaturedModule('http-dir-scanner')">Dir Scanner</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('http-title')">HTTP Title</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('http-robots')">robots.txt</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('http-cert')">SSL Cert</button>
</div>
</div>
<!-- Exploit tab -->
<div class="tab-content" data-tab-group="run-tabs" data-tab="exploit">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem;align-items:end;margin-top:0.75rem">
<div class="form-group" style="margin-bottom:0">
<label for="exp-rhosts">Target (RHOSTS)</label>
<input type="text" id="exp-rhosts" placeholder="192.168.1.5">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="exp-lhost">Your IP (LHOST)</label>
<input type="text" id="exp-lhost" placeholder="192.168.1.100">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr auto auto;gap:0.75rem;align-items:end;margin-top:0.5rem">
<div class="form-group" style="margin-bottom:0">
<label for="exp-module">Exploit Module</label>
<input type="text" id="exp-module" placeholder="exploit/windows/smb/ms17_010_eternalblue">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="exp-payload">Payload</label>
<input type="text" id="exp-payload" placeholder="windows/x64/meterpreter/reverse_tcp" style="width:250px">
</div>
<div class="form-group" style="margin-bottom:0">
<label for="exp-lport">LPORT</label>
<input type="text" id="exp-lport" value="4444" style="width:70px">
</div>
</div>
<div class="tool-actions" style="margin-top:0.75rem">
<button class="btn btn-danger" onclick="runFeaturedModule('exploit-run')">Launch Exploit</button>
</div>
<p style="font-size:0.75rem;color:var(--text-muted);margin-top:0.5rem">Exploits run as background jobs. Check Active Sessions for shells.</p>
</div>
<!-- Custom tab -->
<div class="tab-content" data-tab-group="run-tabs" data-tab="custom">
<div style="margin-top:0.75rem">
<div class="form-group">
<label for="custom-module">Module Path</label>
<input type="text" id="custom-module" placeholder="auxiliary/scanner/ssh/ssh_version">
</div>
<div class="form-group">
<label for="custom-options">Options (JSON)</label>
<input type="text" id="custom-options" placeholder='{"RHOSTS":"10.0.0.1","RPORT":22}'>
</div>
</div>
<div class="tool-actions">
<button class="btn btn-primary" onclick="runFeaturedModule('custom')">Run Module</button>
</div>
</div>
<!-- Shared output area -->
<div style="margin-top:0.75rem;display:flex;align-items:center;gap:0.75rem;padding-top:0.5rem;border-top:1px solid var(--border)">
<span id="run-status" style="font-size:0.85rem;color:var(--text-secondary);flex:1"></span>
<button id="run-stop-btn" class="btn btn-sm btn-danger" style="display:none" onclick="stopCurrentModule()">Stop</button>
</div>
<div id="module-output" class="results-stream" style="min-height:140px;margin-top:0.5rem"></div>
</div>
<!-- Module Browser -->
<div class="section">
<h2>Module Browser</h2>
<div class="tab-bar">
<button class="tab active" data-tab-group="msf-browse" data-tab="auxiliary" onclick="showTab('msf-browse','auxiliary');browseMSFModules('auxiliary')">Scanners</button>
<button class="tab" data-tab-group="msf-browse" data-tab="exploit" onclick="showTab('msf-browse','exploit');browseMSFModules('exploit')">Exploits</button>
<button class="tab" data-tab-group="msf-browse" data-tab="post" onclick="showTab('msf-browse','post');browseMSFModules('post')">Post</button>
<button class="tab" data-tab-group="msf-browse" data-tab="payload" onclick="showTab('msf-browse','payload');browseMSFModules('payload')">Payloads</button>
</div>
<input type="hidden" id="msf-page-auxiliary" value="1">
<input type="hidden" id="msf-page-exploit" value="1">
<input type="hidden" id="msf-page-post" value="1">
<input type="hidden" id="msf-page-payload" value="1">
<div class="tab-content active" data-tab-group="msf-browse" data-tab="auxiliary" id="msf-modules-auxiliary">
<div class="empty-state">Click a tab to browse modules.</div>
</div>
<div class="tab-content" data-tab-group="msf-browse" data-tab="exploit" id="msf-modules-exploit"></div>
<div class="tab-content" data-tab-group="msf-browse" data-tab="post" id="msf-modules-post"></div>
<div class="tab-content" data-tab-group="msf-browse" data-tab="payload" id="msf-modules-payload"></div>
</div>
<!-- Sessions & Jobs -->
<div class="section">
<h2>Active Sessions &amp; Jobs</h2>
<div class="tool-actions">
<button class="btn btn-small" onclick="loadMSFSessions()">Refresh Sessions</button>
<button class="btn btn-small" onclick="loadMSFJobs()">Refresh Jobs</button>
</div>
<div id="msf-sessions">
<div class="empty-state">Click "Refresh" to check for active sessions and jobs.</div>
</div>
<div id="msf-jobs" style="margin-top:0.5rem"></div>
</div>
{% if modules %}
<div class="section">
<h2>Offense 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 %}
<!-- Agent Hal -->
<div class="section">
<h2>Agent Hal — Autonomous Mode</h2>
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:0.75rem">Give Hal a security task and watch it execute step by step using MSF and other tools.</p>
<div class="input-row">
<input type="text" id="agent-task" placeholder="e.g. scan 10.0.0.5 for open SSH ports and identify the service version">
<button class="btn btn-primary" onclick="runHalTask()">Run</button>
<button id="agent-stop-btn" class="btn btn-danger" style="display:none" onclick="stopHalTask()">Stop</button>
</div>
<div id="agent-output" class="results-stream" style="min-height:120px"></div>
</div>
<script>
let _currentJobId = null;
let _currentRunId = null;
// Check MSF status on page load
document.addEventListener('DOMContentLoaded', function() { checkMSFStatus(); });
/* ── Featured Module Definitions ───────────────────────────────── */
const _FEATURED = {
// SSH
'ssh': {path: 'auxiliary/scanner/ssh/ssh_version', opts: () => ({RHOSTS: v('ssh-rhosts'), RPORT: parseInt(v('ssh-rport')) || 22, THREADS: parseInt(v('ssh-threads')) || 10})},
'ssh-enum': {path: 'auxiliary/scanner/ssh/ssh_enumusers', opts: () => ({RHOSTS: v('ssh-rhosts'), RPORT: parseInt(v('ssh-rport')) || 22, THREADS: parseInt(v('ssh-threads')) || 10})},
'ssh-brute': {path: 'auxiliary/scanner/ssh/ssh_login', opts: () => ({RHOSTS: v('ssh-rhosts'), RPORT: parseInt(v('ssh-rport')) || 22, USERNAME: v('ssh-username'), PASSWORD: v('ssh-password')})},
// Port Scan
'tcp-scan': {path: 'auxiliary/scanner/portscan/tcp', opts: () => ({RHOSTS: v('ps-rhosts'), PORTS: v('ps-ports') || '1-1024', THREADS: parseInt(v('ps-threads')) || 10})},
'syn-scan': {path: 'auxiliary/scanner/portscan/syn', opts: () => ({RHOSTS: v('ps-rhosts'), PORTS: v('ps-ports') || '1-1024', THREADS: parseInt(v('ps-threads')) || 10})},
'ack-scan': {path: 'auxiliary/scanner/portscan/ack', opts: () => ({RHOSTS: v('ps-rhosts'), PORTS: v('ps-ports') || '1-1024', THREADS: parseInt(v('ps-threads')) || 10})},
'udp-scan': {path: 'auxiliary/scanner/discovery/udp_sweep', opts: () => ({RHOSTS: v('ps-rhosts'), THREADS: parseInt(v('ps-threads')) || 10})},
// OS Detect
'smb-version': {path: 'auxiliary/scanner/smb/smb_version', opts: () => ({RHOSTS: v('os-rhosts')})},
'http-header': {path: 'auxiliary/scanner/http/http_header', opts: () => ({RHOSTS: v('os-rhosts')})},
'ftp-version': {path: 'auxiliary/scanner/ftp/ftp_version', opts: () => ({RHOSTS: v('os-rhosts')})},
'telnet-version':{path: 'auxiliary/scanner/telnet/telnet_version', opts: () => ({RHOSTS: v('os-rhosts')})},
// Vuln Scan
'eternalblue-check': {path: 'auxiliary/scanner/smb/smb_ms17_010', opts: () => ({RHOSTS: v('vuln-rhosts'), THREADS: parseInt(v('vuln-threads')) || 5})},
'bluekeep-check': {path: 'auxiliary/scanner/rdp/cve_2019_0708_bluekeep', opts: () => ({RHOSTS: v('vuln-rhosts'), THREADS: parseInt(v('vuln-threads')) || 5})},
'ssl-heartbleed': {path: 'auxiliary/scanner/ssl/openssl_heartbleed', opts: () => ({RHOSTS: v('vuln-rhosts'), THREADS: parseInt(v('vuln-threads')) || 5})},
'shellshock-check': {path: 'auxiliary/scanner/http/apache_mod_cgi_bash_env', opts: () => ({RHOSTS: v('vuln-rhosts'), THREADS: parseInt(v('vuln-threads')) || 5})},
// SMB
'smb-enum-shares': {path: 'auxiliary/scanner/smb/smb_enumshares', opts: () => ({RHOSTS: v('smb-rhosts'), SMBUser: v('smb-user'), SMBPass: v('smb-pass')})},
'smb-enum-users': {path: 'auxiliary/scanner/smb/smb_enumusers', opts: () => ({RHOSTS: v('smb-rhosts'), SMBUser: v('smb-user'), SMBPass: v('smb-pass')})},
'smb-login': {path: 'auxiliary/scanner/smb/smb_login', opts: () => ({RHOSTS: v('smb-rhosts'), SMBUser: v('smb-user'), SMBPass: v('smb-pass')})},
'smb-pipe-auditor':{path: 'auxiliary/scanner/smb/pipe_auditor', opts: () => ({RHOSTS: v('smb-rhosts'), SMBUser: v('smb-user'), SMBPass: v('smb-pass')})},
// HTTP
'http-dir-scanner': {path: 'auxiliary/scanner/http/dir_scanner', opts: () => ({RHOSTS: v('http-rhosts'), RPORT: parseInt(v('http-rport')) || 80, THREADS: parseInt(v('http-threads')) || 5, PATH: v('http-targeturi') || '/'})},
'http-title': {path: 'auxiliary/scanner/http/title', opts: () => ({RHOSTS: v('http-rhosts'), RPORT: parseInt(v('http-rport')) || 80})},
'http-robots': {path: 'auxiliary/scanner/http/robots_txt', opts: () => ({RHOSTS: v('http-rhosts'), RPORT: parseInt(v('http-rport')) || 80})},
'http-cert': {path: 'auxiliary/scanner/http/cert', opts: () => ({RHOSTS: v('http-rhosts'), RPORT: parseInt(v('http-rport')) || 443})},
// Exploit
'exploit-run': {path: null, opts: () => {
const o = {RHOSTS: v('exp-rhosts'), LHOST: v('exp-lhost'), LPORT: parseInt(v('exp-lport')) || 4444};
const payload = v('exp-payload');
if (payload) o.PAYLOAD = payload;
return o;
}},
// Custom
'custom': {path: null, opts: () => {try{return JSON.parse(v('custom-options') || '{}')}catch(e){return {}}}},
};
function v(id) { const el = document.getElementById(id); return el ? el.value.trim() : ''; }
/* ── Server Control ─────────────────────────────────────────────── */
function checkMSFStatus() {
fetch('/offense/status').then(r => r.json()).then(d => {
const el = document.getElementById('msf-status');
const ver = document.getElementById('msf-version');
const btnConnect = document.getElementById('btn-connect');
const btnDisconnect = document.getElementById('btn-disconnect');
const btnStart = document.getElementById('btn-start-server');
const btnStop = document.getElementById('btn-stop-server');
if (d.connected) {
el.innerHTML = '<span class="status-dot active"></span>Connected';
ver.textContent = d.version ? 'Metasploit ' + d.version : '';
btnConnect.style.display = 'none';
btnDisconnect.style.display = '';
btnStop.style.display = d.server_running ? '' : 'none';
btnStart.style.display = 'none';
} else if (d.server_running) {
el.innerHTML = '<span class="status-dot" style="background:var(--warning)"></span>Server running (not connected)';
ver.textContent = '';
btnConnect.style.display = '';
btnDisconnect.style.display = 'none';
btnStop.style.display = '';
btnStart.style.display = 'none';
} else {
el.innerHTML = '<span class="status-dot inactive"></span>Not running';
ver.textContent = '';
btnConnect.style.display = 'none';
btnDisconnect.style.display = 'none';
btnStop.style.display = 'none';
btnStart.style.display = '';
}
// Populate settings panel
if (d.host) document.getElementById('msf-host').value = d.host;
if (d.port) document.getElementById('msf-port').value = d.port;
if (d.username) document.getElementById('msf-user').value = d.username;
document.getElementById('msf-ssl').checked = d.ssl !== false;
}).catch(() => {
document.getElementById('msf-status').innerHTML = '<span class="status-dot inactive"></span>Error checking status';
});
}
function toggleServerPanel() {
const panel = document.getElementById('server-panel');
panel.style.display = panel.style.display === 'none' ? '' : 'none';
}
function _getServerSettings() {
return {
host: v('msf-host') || '127.0.0.1',
port: parseInt(v('msf-port')) || 55553,
username: v('msf-user') || 'msf',
password: v('msf-pass'),
ssl: document.getElementById('msf-ssl').checked,
};
}
function msfStartServer() {
const settings = _getServerSettings();
if (!settings.password) { alert('Password is required'); return; }
const msg = document.getElementById('server-msg');
msg.textContent = 'Starting server...';
fetch('/offense/server/start', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify(settings)
}).then(r => r.json()).then(d => {
msg.textContent = d.ok ? (d.message || 'Started') : ('Error: ' + (d.error || 'unknown'));
if (d.ok) { document.getElementById('server-panel').style.display = 'none'; checkMSFStatus(); }
}).catch(e => { msg.textContent = 'Error: ' + e.message; });
}
function msfConnectOnly() {
const settings = _getServerSettings();
if (!settings.password) { alert('Password is required'); return; }
const msg = document.getElementById('server-msg');
msg.textContent = 'Connecting...';
fetch('/offense/connect', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({password: settings.password})
}).then(r => r.json()).then(d => {
msg.textContent = d.ok ? 'Connected' : ('Error: ' + (d.error || 'unknown'));
if (d.ok) { document.getElementById('server-panel').style.display = 'none'; checkMSFStatus(); }
}).catch(e => { msg.textContent = 'Error: ' + e.message; });
}
function msfConnect() {
// Quick connect using saved password
fetch('/offense/connect', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({})
}).then(r => r.json()).then(d => {
if (d.ok) { checkMSFStatus(); }
else { toggleServerPanel(); document.getElementById('server-msg').textContent = d.error || 'Connection failed — enter password'; }
});
}
function msfDisconnect() {
fetch('/offense/disconnect', {method: 'POST'}).then(() => checkMSFStatus());
}
function msfStopServer() {
if (!confirm('Stop the MSF RPC server?')) return;
fetch('/offense/server/stop', {method: 'POST'}).then(() => checkMSFStatus());
}
function msfSaveSettings() {
const settings = _getServerSettings();
fetch('/offense/settings', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify(settings)
}).then(r => r.json()).then(d => {
document.getElementById('server-msg').textContent = d.ok ? 'Settings saved' : ('Error: ' + d.error);
});
}
/* ── Module Execution ───────────────────────────────────────────── */
function toggleBruteRow() {
var row = document.getElementById('ssh-brute-row');
row.style.display = row.style.display === 'none' ? '' : 'none';
}
function runFeaturedModule(key) {
const cfg = _FEATURED[key];
if (!cfg) return;
let path;
if (key === 'custom') {
path = v('custom-module');
} else if (key === 'exploit-run') {
path = v('exp-module');
} else {
path = cfg.path;
}
if (!path) { alert('Enter a module path'); return; }
const opts = cfg.opts();
// Remove empty string values
Object.keys(opts).forEach(k => { if (opts[k] === '' || opts[k] === undefined) delete opts[k]; });
if (!opts.RHOSTS && !['custom'].includes(key)) { alert('Enter a target'); return; }
runModule(path, opts);
}
function runModule(module_path, options) {
const out = document.getElementById('module-output');
const status = document.getElementById('run-status');
const stopBtn = document.getElementById('run-stop-btn');
out.innerHTML = '';
status.textContent = 'Starting ' + module_path + '...';
stopBtn.style.display = '';
_currentJobId = null;
fetch('/offense/module/run', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({module_path, options})
}).then(res => {
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = '';
function pump() {
reader.read().then(({done, value}) => {
if (done) { status.textContent = 'Finished.'; stopBtn.style.display = 'none'; return; }
buf += dec.decode(value, {stream: true});
const parts = buf.split('\n\n');
buf = parts.pop();
parts.forEach(part => {
const line = part.replace(/^data:\s*/, '').trim();
if (!line) return;
try {
const d = JSON.parse(line);
if (d.job_id) { _currentJobId = d.job_id; status.textContent = 'Running ' + module_path + '…'; }
if (d.error) { out.innerHTML += '<div class="err">Error: ' + escapeHtml(d.error) + '</div>'; stopBtn.style.display = 'none'; }
if (d.line) {
let cls = '';
if (d.line.includes('[+]')) cls = 'success';
else if (d.line.includes('[-]') || d.line.includes('Error')) cls = 'err';
else if (d.line.includes('[!]')) cls = 'warn';
out.innerHTML += '<div class="' + cls + '">' + escapeHtml(d.line) + '</div>';
out.scrollTop = out.scrollHeight;
}
if (d.done) {
status.textContent = 'Done.';
stopBtn.style.display = 'none';
if (d.open_ports && d.open_ports.length) {
out.innerHTML += '<div class="success" style="margin-top:0.5rem;font-weight:600">Open ports: ' + d.open_ports.map(p => escapeHtml(String(p.port || p))).join(', ') + '</div>';
}
if (d.services && d.services.length) {
let html = '<div style="margin-top:0.5rem;font-weight:600;color:var(--accent)">Services detected:</div>';
d.services.forEach(s => { html += '<div class="success">' + escapeHtml(s.ip + ':' + s.port + ' — ' + s.info) + '</div>'; });
out.innerHTML += html;
}
}
} catch(e) {}
});
pump();
}).catch(e => { status.textContent = 'Error.'; out.innerHTML += '<div class="err">' + escapeHtml(e.message) + '</div>'; stopBtn.style.display = 'none'; });
}
pump();
}).catch(e => { status.textContent = 'Failed.'; out.innerHTML = '<div class="err">' + escapeHtml(e.message) + '</div>'; stopBtn.style.display = 'none'; });
}
function stopCurrentModule() {
if (!_currentJobId) return;
fetch('/offense/module/stop', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({job_id: _currentJobId})});
document.getElementById('run-stop-btn').style.display = 'none';
document.getElementById('run-status').textContent = 'Stopped.';
}
/* ── Sessions & Jobs ────────────────────────────────────────────── */
function loadMSFSessions() {
const el = document.getElementById('msf-sessions');
fetch('/offense/sessions').then(r => r.json()).then(d => {
if (d.error) { el.innerHTML = '<div class="empty-state">' + escapeHtml(d.error) + '</div>'; return; }
const sessions = d.sessions || {};
const keys = Object.keys(sessions);
if (!keys.length) { el.innerHTML = '<div class="empty-state">No active sessions.</div>'; return; }
let html = '<table style="width:100%;font-size:0.85rem"><tr><th>ID</th><th>Type</th><th>Target</th><th>Info</th></tr>';
keys.forEach(sid => {
const s = sessions[sid];
html += '<tr><td>' + escapeHtml(sid) + '</td><td>' + escapeHtml(s.type || '') + '</td><td>' + escapeHtml(s.tunnel_peer || s.target_host || '') + '</td><td>' + escapeHtml(s.info || '') + '</td></tr>';
});
html += '</table>';
el.innerHTML = html;
}).catch(() => { el.innerHTML = '<div class="empty-state">Failed to load sessions.</div>'; });
}
function loadMSFJobs() {
const el = document.getElementById('msf-jobs');
fetch('/offense/jobs').then(r => r.json()).then(d => {
if (d.error) { el.innerHTML = '<div class="empty-state">' + escapeHtml(d.error) + '</div>'; return; }
const jobs = d.jobs || {};
const keys = Object.keys(jobs);
if (!keys.length) { el.innerHTML = '<div class="empty-state">No running jobs.</div>'; return; }
let html = '<table style="width:100%;font-size:0.85rem"><tr><th>ID</th><th>Name</th><th></th></tr>';
keys.forEach(jid => {
html += '<tr><td>' + escapeHtml(jid) + '</td><td>' + escapeHtml(String(jobs[jid])) + '</td><td><button class="btn btn-sm btn-danger" onclick="stopMSFJob(\'' + escapeHtml(jid) + '\')">Stop</button></td></tr>';
});
html += '</table>';
el.innerHTML = html;
}).catch(() => { el.innerHTML = '<div class="empty-state">Failed to load jobs.</div>'; });
}
function stopMSFJob(jobId) {
fetch('/offense/jobs/' + jobId + '/stop', {method:'POST'}).then(() => loadMSFJobs());
}
/* ── Agent Hal ──────────────────────────────────────────────────── */
async function runHalTask() {
const task = v('agent-task');
if (!task) return;
const out = document.getElementById('agent-output');
out.innerHTML = '';
document.getElementById('agent-stop-btn').style.display = '';
const res = await fetch('/api/agent/run', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({task})});
const data = await res.json();
if (data.error) { out.innerHTML = '<div class="err">' + escapeHtml(data.error) + '</div>'; document.getElementById('agent-stop-btn').style.display = 'none'; return; }
_currentRunId = data.run_id;
streamAgentSteps(data.run_id);
}
function streamAgentSteps(run_id) {
const out = document.getElementById('agent-output');
const es = new EventSource('/api/agent/stream/' + run_id);
es.onmessage = function(e) {
try {
const d = JSON.parse(e.data);
if (d.done) { es.close(); document.getElementById('agent-stop-btn').style.display = 'none'; return; }
if (d.error) { out.innerHTML += '<div class="err">[error] ' + escapeHtml(d.error) + '</div>'; es.close(); document.getElementById('agent-stop-btn').style.display = 'none'; return; }
const cls = d.type === 'thought' ? 'dim' : d.type === 'action' ? 'info' : d.type === 'result' ? 'warn' : '';
out.innerHTML += '<div class="' + cls + '">[' + escapeHtml(d.type || '') + '] ' + escapeHtml(d.content || '') + '</div>';
out.scrollTop = out.scrollHeight;
} catch(err) {}
};
}
async function stopHalTask() {
if (_currentRunId) await fetch('/api/agent/stop/' + _currentRunId, {method:'POST'});
document.getElementById('agent-stop-btn').style.display = 'none';
}
</script>
{% endblock %}