Autarch/web/templates/offense.html
DigiJ ffe47c51b5 Initial public release — AUTARCH v1.0.0
Full security platform with web dashboard, 16 Flask blueprints, 26 modules,
autonomous AI agent, WebUSB hardware support, and Archon Android companion app.

Includes Hash Toolkit, debug console, anti-stalkerware shield, Metasploit/RouterSploit
integration, WireGuard VPN, OSINT reconnaissance, and multi-backend LLM support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 03:57:32 -08:00

324 lines
16 KiB
HTML

{% extends "base.html" %}
{% block title %}Offense - AUTARCH{% endblock %}
{% block content %}
<div class="page-header">
<h1>Offense</h1>
</div>
<!-- MSF Status -->
<div class="section">
<h2>Metasploit Status</h2>
<div class="status-indicator" id="msf-status">
<span class="status-dot inactive"></span>Checking...
</div>
<div id="msf-info" style="font-size:0.85rem;color:var(--text-secondary);margin-top:4px"></div>
<p style="font-size:0.8rem;color:var(--text-muted);margin-top:8px">Module execution is CLI-only for safety. The web UI provides search, browsing, and status.</p>
</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>
<!-- 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 -->
<div class="section">
<h2>Active Sessions</h2>
<div class="tool-actions">
<button class="btn btn-small" onclick="loadMSFSessions()">Refresh</button>
</div>
<div id="msf-sessions">
<div class="empty-state">Click "Refresh" to check for active sessions.</div>
</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 %}
<!-- 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="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="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')">Run TCP Scan</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('syn-scan')">Run SYN Scan</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')">Run SMB Version</button>
<button class="btn btn-secondary" onclick="runFeaturedModule('http-header')">Run HTTP Header</button>
</div>
</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>
<!-- 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(); });
const _FEATURED = {
'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-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')})},
'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})},
'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')})},
'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() : ''; }
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;
const path = key === 'custom' ? v('custom-module') : cfg.path;
if (!path) { alert('Enter a module path'); return; }
const opts = cfg.opts();
if (!opts.RHOSTS && key !== 'custom') { alert('Enter a target in RHOSTS'); 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...';
stopBtn.style.display = '';
_currentJobId = null;
const es = new EventSource('/offense/module/run?' + new URLSearchParams({_body: JSON.stringify({module_path, options})}));
// Use fetch + ReadableStream (EventSource doesn't support POST)
es.close();
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…'; }
if (d.error) { out.innerHTML += '<div class="err">Error: ' + escapeHtml(d.error) + '</div>'; stopBtn.style.display = 'none'; }
if (d.line) { out.innerHTML += '<div>' + 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">Open ports: ' + escapeHtml(d.open_ports.join(', ')) + '</div>';
if (d.findings && d.findings.length) out.innerHTML += '<div class="success">Findings: ' + escapeHtml(JSON.stringify(d.findings)) + '</div>';
}
} 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.';
}
// 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 %}