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>
234 lines
12 KiB
HTML
234 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}IP Capture — AUTARCH{% endblock %}
|
|
{% block content %}
|
|
<h1>IP Capture & Redirect</h1>
|
|
<p style="color:var(--text-secondary);margin-bottom:1.5rem">
|
|
Create stealthy tracking links that capture visitor IP + metadata, then redirect to a legitimate site.
|
|
</p>
|
|
|
|
<!-- Tabs -->
|
|
<div class="tabs" style="display:flex;gap:0;border-bottom:2px solid var(--border);margin-bottom:1.5rem">
|
|
<button class="tab-btn active" onclick="capTab('create',this)">Create & Manage</button>
|
|
<button class="tab-btn" onclick="capTab('captures',this)">Captures</button>
|
|
</div>
|
|
|
|
<!-- ═══════════════════ CREATE TAB ═══════════════════ -->
|
|
<div id="tab-create" class="tab-pane">
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1.25rem">
|
|
<!-- Create link -->
|
|
<div class="card" style="padding:1.25rem">
|
|
<h3 style="margin-bottom:1rem">Create Capture Link</h3>
|
|
<label class="form-label">Target URL (redirect destination)</label>
|
|
<input id="cap-target" class="form-input" placeholder="https://example.com/real-article">
|
|
|
|
<label class="form-label" style="margin-top:0.5rem">Friendly Name</label>
|
|
<input id="cap-name" class="form-input" placeholder="Phishing awareness test #1">
|
|
|
|
<label class="form-label" style="margin-top:0.5rem">Disguise Type</label>
|
|
<select id="cap-disguise" class="form-input">
|
|
<option value="article">Article URL (realistic path)</option>
|
|
<option value="short">Short URL (/c/xxxxx)</option>
|
|
</select>
|
|
|
|
<button class="btn btn-primary" style="margin-top:1rem;width:100%" onclick="createCapLink()">Create Link</button>
|
|
|
|
<!-- Result -->
|
|
<div id="cap-result" style="display:none;margin-top:1rem;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);padding:1rem">
|
|
<div style="font-size:0.82rem;color:var(--text-secondary);margin-bottom:0.5rem">Your tracking links:</div>
|
|
<div style="margin-bottom:0.4rem">
|
|
<label style="font-size:0.72rem;color:var(--text-muted)">SHORT URL</label>
|
|
<div style="display:flex;gap:0.4rem;align-items:center">
|
|
<input id="cap-res-short" class="form-input" readonly style="font-family:monospace;font-size:0.82rem">
|
|
<button class="btn btn-small" onclick="copyUrl('cap-res-short')">Copy</button>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label style="font-size:0.72rem;color:var(--text-muted)">ARTICLE URL</label>
|
|
<div style="display:flex;gap:0.4rem;align-items:center">
|
|
<input id="cap-res-article" class="form-input" readonly style="font-family:monospace;font-size:0.82rem">
|
|
<button class="btn btn-small" onclick="copyUrl('cap-res-article')">Copy</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Active links -->
|
|
<div class="card" style="padding:1.25rem">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem">
|
|
<h3>Active Links</h3>
|
|
<button class="btn btn-small" onclick="loadCapLinks()">Refresh</button>
|
|
</div>
|
|
<div id="cap-links" style="font-size:0.85rem">Loading...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════════════ CAPTURES TAB ═══════════════════ -->
|
|
<div id="tab-captures" class="tab-pane" style="display:none">
|
|
<div class="card" style="padding:1.25rem">
|
|
<div style="display:flex;align-items:center;gap:0.75rem;margin-bottom:1rem">
|
|
<h3>Captures for:</h3>
|
|
<select id="cap-select" class="form-input" style="width:auto;min-width:250px" onchange="loadCaptures()"></select>
|
|
<button class="btn btn-small" onclick="loadCaptures()">Refresh</button>
|
|
<div style="margin-left:auto;display:flex;gap:0.4rem">
|
|
<button class="btn btn-small" onclick="exportCap('json')">Export JSON</button>
|
|
<button class="btn btn-small" onclick="exportCap('csv')">Export CSV</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="cap-stats" style="display:flex;gap:1.5rem;margin-bottom:1rem;font-size:0.9rem"></div>
|
|
|
|
<table style="width:100%;font-size:0.82rem;border-collapse:collapse">
|
|
<thead>
|
|
<tr style="border-bottom:2px solid var(--border);text-align:left">
|
|
<th style="padding:6px">IP</th>
|
|
<th style="padding:6px">Timestamp</th>
|
|
<th style="padding:6px">Location</th>
|
|
<th style="padding:6px">User Agent</th>
|
|
<th style="padding:6px">Language</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="cap-table"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.tab-btn{padding:0.6rem 1.2rem;background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:0.9rem;border-bottom:2px solid transparent;margin-bottom:-2px;transition:all 0.2s}
|
|
.tab-btn:hover{color:var(--text-primary)}
|
|
.tab-btn.active{color:var(--accent);border-bottom-color:var(--accent);font-weight:600}
|
|
.form-label{display:block;font-size:0.78rem;color:var(--text-secondary);margin-bottom:0.25rem;font-weight:600;text-transform:uppercase;letter-spacing:0.04em}
|
|
.form-input{width:100%;padding:0.5rem 0.65rem;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-primary);font-size:0.85rem}
|
|
.form-input:focus{outline:none;border-color:var(--accent)}
|
|
.btn-danger{background:var(--danger);color:#fff}
|
|
.link-card{background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);padding:0.75rem;margin-bottom:0.5rem}
|
|
.link-card:hover{border-color:var(--accent)}
|
|
</style>
|
|
|
|
<script>
|
|
let _currentCapKey = '';
|
|
|
|
function capTab(name, btn) {
|
|
document.querySelectorAll('.tab-pane').forEach(p => p.style.display = 'none');
|
|
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
|
document.getElementById('tab-' + name).style.display = '';
|
|
btn.classList.add('active');
|
|
if (name === 'captures') { loadCapSelect(); loadCaptures(); }
|
|
}
|
|
|
|
function copyUrl(id) {
|
|
const el = document.getElementById(id);
|
|
el.select();
|
|
document.execCommand('copy');
|
|
}
|
|
|
|
// ── Create ──
|
|
function createCapLink() {
|
|
const data = {
|
|
target_url: document.getElementById('cap-target').value,
|
|
name: document.getElementById('cap-name').value,
|
|
disguise: document.getElementById('cap-disguise').value,
|
|
};
|
|
fetch('/ipcapture/links', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(data)})
|
|
.then(r => r.json()).then(d => {
|
|
if (d.ok) {
|
|
const base = window.location.origin;
|
|
document.getElementById('cap-res-short').value = base + d.short_path;
|
|
document.getElementById('cap-res-article').value = base + d.article_path;
|
|
document.getElementById('cap-result').style.display = '';
|
|
loadCapLinks();
|
|
} else {
|
|
alert(d.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
// ── Links ──
|
|
function loadCapLinks() {
|
|
fetch('/ipcapture/links').then(r => r.json()).then(d => {
|
|
const el = document.getElementById('cap-links');
|
|
if (!d.ok || !d.links.length) { el.textContent = 'No links created yet'; return; }
|
|
el.innerHTML = d.links.map(l => {
|
|
const s = l.stats || {};
|
|
return `<div class="link-card">
|
|
<div style="display:flex;justify-content:space-between;align-items:center">
|
|
<strong>${l.name || l.key}</strong>
|
|
<span style="font-size:0.78rem;color:var(--text-muted)">${s.total || 0} captures (${s.unique_ips || 0} unique)</span>
|
|
</div>
|
|
<div style="font-size:0.78rem;color:var(--text-secondary);margin-top:0.3rem">
|
|
Target: <a href="${l.target_url}" target="_blank" style="color:var(--accent)">${l.target_url.substring(0,60)}${l.target_url.length > 60 ? '...' : ''}</a>
|
|
</div>
|
|
<div style="font-size:0.75rem;font-family:monospace;color:var(--text-muted);margin-top:0.2rem">
|
|
${l.short_path} • ${l.article_path || ''}
|
|
</div>
|
|
<div style="margin-top:0.5rem;display:flex;gap:0.4rem">
|
|
<button class="btn btn-small" onclick="_currentCapKey='${l.key}';capTab('captures',document.querySelectorAll('.tab-btn')[1])">View Captures</button>
|
|
<button class="btn btn-small btn-danger" onclick="deleteCapLink('${l.key}')">Delete</button>
|
|
</div>
|
|
</div>`;
|
|
}).join('');
|
|
});
|
|
}
|
|
|
|
function deleteCapLink(key) {
|
|
if (!confirm('Delete this capture link?')) return;
|
|
fetch(`/ipcapture/links/${key}`, {method:'DELETE'}).then(() => loadCapLinks());
|
|
}
|
|
|
|
// ── Captures ──
|
|
function loadCapSelect() {
|
|
fetch('/ipcapture/links').then(r => r.json()).then(d => {
|
|
const sel = document.getElementById('cap-select');
|
|
if (!d.ok || !d.links.length) { sel.innerHTML = '<option>No links</option>'; return; }
|
|
sel.innerHTML = d.links.map(l => `<option value="${l.key}">${l.name || l.key} (${(l.stats||{}).total||0} captures)</option>`).join('');
|
|
if (_currentCapKey) sel.value = _currentCapKey;
|
|
});
|
|
}
|
|
|
|
function loadCaptures() {
|
|
const key = document.getElementById('cap-select').value || _currentCapKey;
|
|
if (!key) return;
|
|
_currentCapKey = key;
|
|
fetch(`/ipcapture/links/${key}`).then(r => r.json()).then(d => {
|
|
if (!d.ok) return;
|
|
const link = d.link;
|
|
const s = link.stats || {};
|
|
|
|
document.getElementById('cap-stats').innerHTML =
|
|
`<span>Total: <strong>${s.total||0}</strong></span>
|
|
<span>Unique IPs: <strong>${s.unique_ips||0}</strong></span>
|
|
<span style="color:var(--text-muted)">First: ${s.first ? new Date(s.first).toLocaleString() : '—'}</span>
|
|
<span style="color:var(--text-muted)">Last: ${s.last ? new Date(s.last).toLocaleString() : '—'}</span>`;
|
|
|
|
const captures = link.captures || [];
|
|
const el = document.getElementById('cap-table');
|
|
if (!captures.length) {
|
|
el.innerHTML = '<tr><td colspan="5" style="padding:10px;color:var(--text-muted)">No captures yet. Share your link and wait for clicks.</td></tr>';
|
|
return;
|
|
}
|
|
el.innerHTML = captures.map(c => {
|
|
const geo = c.geo || {};
|
|
const loc = geo.city ? `${geo.city}, ${geo.country}` : (geo.country || 'Unknown');
|
|
const ua = (c.user_agent || '').substring(0, 60) + ((c.user_agent||'').length > 60 ? '...' : '');
|
|
return `<tr style="border-bottom:1px solid var(--border)">
|
|
<td style="padding:5px;font-family:monospace;font-weight:600">${c.ip}</td>
|
|
<td style="padding:5px;font-size:0.78rem">${new Date(c.timestamp).toLocaleString()}</td>
|
|
<td style="padding:5px">${loc}</td>
|
|
<td style="padding:5px;font-size:0.75rem;max-width:250px;overflow:hidden;text-overflow:ellipsis" title="${c.user_agent||''}">${ua}</td>
|
|
<td style="padding:5px;font-size:0.75rem">${c.accept_language || ''}</td>
|
|
</tr>`;
|
|
}).join('');
|
|
});
|
|
}
|
|
|
|
function exportCap(fmt) {
|
|
const key = _currentCapKey || document.getElementById('cap-select').value;
|
|
if (!key) return;
|
|
window.open(`/ipcapture/links/${key}/export?format=${fmt}`, '_blank');
|
|
}
|
|
|
|
// Init
|
|
document.addEventListener('DOMContentLoaded', loadCapLinks);
|
|
</script>
|
|
{% endblock %}
|