901 lines
42 KiB
HTML
901 lines
42 KiB
HTML
|
|
{% extends "base.html" %}
|
||
|
|
{% block title %}AUTARCH — Starlink Hacking{% endblock %}
|
||
|
|
|
||
|
|
{% block content %}
|
||
|
|
<div class="page-header">
|
||
|
|
<h1 style="color:#ef4444">Starlink Terminal Security Analysis</h1>
|
||
|
|
<p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
|
||
|
|
gRPC exploitation, firmware analysis, network attacks, and RF analysis for Starlink user terminals.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Tab Bar -->
|
||
|
|
<div class="tab-bar">
|
||
|
|
<button class="tab active" data-tab-group="sl" data-tab="discovery" onclick="showTab('sl','discovery')">Discovery & Status</button>
|
||
|
|
<button class="tab" data-tab-group="sl" data-tab="grpc" onclick="showTab('sl','grpc')">gRPC Control</button>
|
||
|
|
<button class="tab" data-tab-group="sl" data-tab="network" onclick="showTab('sl','network')">Network Attack</button>
|
||
|
|
<button class="tab" data-tab-group="sl" data-tab="rffirmware" onclick="showTab('sl','rffirmware')">RF & Firmware</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ==================== DISCOVERY & STATUS TAB ==================== -->
|
||
|
|
<div class="tab-content active" data-tab-group="sl" data-tab="discovery">
|
||
|
|
|
||
|
|
<!-- Discover Dish -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Discover Dish</h2>
|
||
|
|
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Dish IP</label>
|
||
|
|
<input type="text" id="sl-dish-ip" class="form-control" value="192.168.100.1" style="width:200px">
|
||
|
|
</div>
|
||
|
|
<button id="btn-sl-discover" class="btn btn-primary" onclick="slDiscover()">Discover</button>
|
||
|
|
<button class="btn" onclick="slModuleStatus()" title="Module status">Status</button>
|
||
|
|
</div>
|
||
|
|
<div id="sl-discover-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Dish Status -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Dish Status</h2>
|
||
|
|
<div style="margin-bottom:8px">
|
||
|
|
<button id="btn-sl-status" class="btn btn-primary" onclick="slGetStatus()">Get Status</button>
|
||
|
|
<button class="btn" onclick="slGetInfo()">Device Info</button>
|
||
|
|
</div>
|
||
|
|
<div id="sl-status-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Network Info -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Network & WiFi Clients</h2>
|
||
|
|
<button id="btn-sl-network" class="btn btn-primary" onclick="slGetNetwork()">Get Network Info</button>
|
||
|
|
<div id="sl-network-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Port Scan -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Port Scan</h2>
|
||
|
|
<button id="btn-sl-portscan" class="btn btn-primary" onclick="slPortScan()">Scan Dish Ports</button>
|
||
|
|
<span id="sl-portscan-status" style="margin-left:12px;font-size:0.85rem;color:var(--text-secondary)"></span>
|
||
|
|
<div id="sl-portscan-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ==================== gRPC CONTROL TAB ==================== -->
|
||
|
|
<div class="tab-content" data-tab-group="sl" data-tab="grpc">
|
||
|
|
|
||
|
|
<!-- Enumerate Methods -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Enumerate gRPC Methods</h2>
|
||
|
|
<button id="btn-sl-grpc-enum" class="btn btn-primary" onclick="slGrpcEnum()">Enumerate Methods</button>
|
||
|
|
<div id="sl-grpc-enum-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Quick Actions -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Quick Actions</h2>
|
||
|
|
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:10px">
|
||
|
|
Send commands to the Starlink dish via gRPC. Use with caution on production hardware.
|
||
|
|
</p>
|
||
|
|
<div style="display:flex;gap:10px;flex-wrap:wrap">
|
||
|
|
<button class="btn" style="border-color:#f97316;color:#f97316" onclick="slGrpcStow()">Stow Dish</button>
|
||
|
|
<button class="btn" style="border-color:#22c55e;color:#22c55e" onclick="slGrpcUnstow()">Unstow Dish</button>
|
||
|
|
<button class="btn" style="border-color:#ef4444;color:#ef4444" onclick="slGrpcReboot()">Reboot Dish</button>
|
||
|
|
<button class="btn" style="border-color:#ef4444;color:#ef4444;opacity:0.7" onclick="slGrpcFactoryReset()">Factory Reset</button>
|
||
|
|
</div>
|
||
|
|
<div id="sl-grpc-action-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Custom gRPC Call -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Custom gRPC Call</h2>
|
||
|
|
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;max-width:800px">
|
||
|
|
<div class="form-group" style="flex:1;min-width:200px;margin:0">
|
||
|
|
<label>Method</label>
|
||
|
|
<input type="text" id="sl-grpc-method" class="form-control" placeholder="e.g. get_status">
|
||
|
|
</div>
|
||
|
|
<button class="btn btn-primary" onclick="slGrpcCall()">Execute</button>
|
||
|
|
</div>
|
||
|
|
<div class="form-group" style="margin-top:8px;max-width:800px">
|
||
|
|
<label>Parameters (JSON)</label>
|
||
|
|
<textarea id="sl-grpc-params" class="form-control" rows="3" placeholder='{"getStatus": {}}'
|
||
|
|
style="font-family:monospace;font-size:0.85rem"></textarea>
|
||
|
|
</div>
|
||
|
|
<div id="sl-grpc-call-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ==================== NETWORK ATTACK TAB ==================== -->
|
||
|
|
<div class="tab-content" data-tab-group="sl" data-tab="network">
|
||
|
|
|
||
|
|
<!-- Traffic Interception -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Traffic Interception (ARP Spoof)</h2>
|
||
|
|
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:10px">
|
||
|
|
ARP spoofing between dish and gateway to intercept traffic. Requires arpspoof or ettercap.
|
||
|
|
</p>
|
||
|
|
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Gateway IP</label>
|
||
|
|
<input type="text" id="sl-intercept-gateway" class="form-control" value="192.168.1.1" style="width:180px">
|
||
|
|
</div>
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Interface (optional)</label>
|
||
|
|
<input type="text" id="sl-intercept-iface" class="form-control" placeholder="auto" style="width:130px">
|
||
|
|
</div>
|
||
|
|
<button class="btn btn-primary" onclick="slInterceptStart()">Start Intercept</button>
|
||
|
|
<button class="btn" style="border-color:#ef4444;color:#ef4444" onclick="slInterceptStop()">Stop</button>
|
||
|
|
</div>
|
||
|
|
<div id="sl-intercept-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- DNS Spoofing -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>DNS Spoofing</h2>
|
||
|
|
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:10px">
|
||
|
|
Redirect DNS queries for a domain to a controlled IP. Requires dnsspoof or ettercap.
|
||
|
|
</p>
|
||
|
|
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Domain</label>
|
||
|
|
<input type="text" id="sl-dns-domain" class="form-control" placeholder="example.com" style="width:200px">
|
||
|
|
</div>
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Spoof IP</label>
|
||
|
|
<input type="text" id="sl-dns-ip" class="form-control" placeholder="10.0.0.1" style="width:160px">
|
||
|
|
</div>
|
||
|
|
<button class="btn btn-primary" onclick="slDnsStart()">Start DNS Spoof</button>
|
||
|
|
<button class="btn" style="border-color:#ef4444;color:#ef4444" onclick="slDnsStop()">Stop</button>
|
||
|
|
</div>
|
||
|
|
<div id="sl-dns-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Client Deauth -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>WiFi Client Deauthentication</h2>
|
||
|
|
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:10px">
|
||
|
|
Send deauth packets to disconnect WiFi clients from the Starlink router. Requires aircrack-ng or mdk4.
|
||
|
|
</p>
|
||
|
|
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Target MAC (empty=broadcast)</label>
|
||
|
|
<input type="text" id="sl-deauth-mac" class="form-control" placeholder="AA:BB:CC:DD:EE:FF" style="width:200px">
|
||
|
|
</div>
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Wireless Interface</label>
|
||
|
|
<input type="text" id="sl-deauth-iface" class="form-control" placeholder="wlan0mon" style="width:140px">
|
||
|
|
</div>
|
||
|
|
<button class="btn btn-primary" onclick="slDeauth()">Send Deauth</button>
|
||
|
|
</div>
|
||
|
|
<div id="sl-deauth-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- MITM -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>MITM WiFi Clients</h2>
|
||
|
|
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Interface (optional)</label>
|
||
|
|
<input type="text" id="sl-mitm-iface" class="form-control" placeholder="auto" style="width:140px">
|
||
|
|
</div>
|
||
|
|
<button class="btn btn-primary" onclick="slMitm()">Start MITM</button>
|
||
|
|
</div>
|
||
|
|
<div id="sl-mitm-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ==================== RF & FIRMWARE TAB ==================== -->
|
||
|
|
<div class="tab-content" data-tab-group="sl" data-tab="rffirmware">
|
||
|
|
|
||
|
|
<!-- Firmware Version Check -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Firmware Version Check</h2>
|
||
|
|
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:10px">
|
||
|
|
Check the running firmware version against known vulnerable versions.
|
||
|
|
</p>
|
||
|
|
<button id="btn-sl-fw-check" class="btn btn-primary" onclick="slFirmwareCheck()">Check Firmware</button>
|
||
|
|
<div id="sl-fw-check-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Firmware Upload & Analyze -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Firmware Analysis</h2>
|
||
|
|
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:10px">
|
||
|
|
Upload a firmware image for signature scanning, entropy analysis, and string extraction.
|
||
|
|
</p>
|
||
|
|
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Firmware File</label>
|
||
|
|
<input type="file" id="sl-fw-file" class="form-control" style="max-width:350px">
|
||
|
|
</div>
|
||
|
|
<button class="btn btn-primary" onclick="slFirmwareAnalyze()">Analyze</button>
|
||
|
|
</div>
|
||
|
|
<div id="sl-fw-analyze-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Debug Interfaces -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Debug Interface Scan</h2>
|
||
|
|
<button class="btn btn-primary" onclick="slDebugInterfaces()">Scan Debug Interfaces</button>
|
||
|
|
<div id="sl-debug-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- RF Downlink Analysis -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>RF Downlink Analysis</h2>
|
||
|
|
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:10px">
|
||
|
|
Ku-band downlink analysis (10.7-12.7 GHz). Requires SDR hardware (HackRF or RTL-SDR with LNB).
|
||
|
|
</p>
|
||
|
|
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Device</label>
|
||
|
|
<select id="sl-rf-device" class="form-control" style="width:130px">
|
||
|
|
<option value="hackrf">HackRF</option>
|
||
|
|
<option value="rtl">RTL-SDR</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
<div class="form-group" style="margin:0">
|
||
|
|
<label>Duration (s)</label>
|
||
|
|
<input type="number" id="sl-rf-duration" class="form-control" value="30" min="5" max="300" style="width:100px">
|
||
|
|
</div>
|
||
|
|
<button class="btn btn-primary" onclick="slRfDownlink()">Analyze Downlink</button>
|
||
|
|
</div>
|
||
|
|
<div id="sl-rf-downlink-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Jamming Detection -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Jamming Detection</h2>
|
||
|
|
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:10px">
|
||
|
|
Check for signal jamming indicators by analyzing dish diagnostics (drop rate, latency, throughput, SNR).
|
||
|
|
</p>
|
||
|
|
<button class="btn btn-primary" onclick="slDetectJamming()">Detect Jamming</button>
|
||
|
|
<div id="sl-jamming-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Known CVEs -->
|
||
|
|
<div class="section">
|
||
|
|
<h2>Known Vulnerabilities</h2>
|
||
|
|
<button class="btn btn-primary" onclick="slLoadCves()">Load CVE Database</button>
|
||
|
|
<button class="btn" onclick="slExportResults()" style="margin-left:8px">Export All Results</button>
|
||
|
|
<div id="sl-cve-result" style="margin-top:12px"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ==================== JAVASCRIPT ==================== -->
|
||
|
|
<script>
|
||
|
|
/* ── helpers (from app.js) ── */
|
||
|
|
/* postJSON, fetchJSON, esc, setLoading, showTab are globally available */
|
||
|
|
|
||
|
|
function slShowResult(elId, html) {
|
||
|
|
document.getElementById(elId).innerHTML = html;
|
||
|
|
}
|
||
|
|
|
||
|
|
function slCardHtml(title, rows) {
|
||
|
|
let h = '<div style="background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:14px;margin-bottom:12px">';
|
||
|
|
if (title) h += '<h3 style="margin:0 0 8px 0;font-size:0.95rem;color:#f97316">' + esc(title) + '</h3>';
|
||
|
|
for (const [k, v] of rows) {
|
||
|
|
h += '<div style="display:flex;gap:8px;margin-bottom:3px;font-size:0.85rem">';
|
||
|
|
h += '<span style="color:var(--text-secondary);min-width:160px">' + esc(k) + '</span>';
|
||
|
|
h += '<span style="color:var(--text-primary);word-break:break-all">' + esc(String(v)) + '</span>';
|
||
|
|
h += '</div>';
|
||
|
|
}
|
||
|
|
h += '</div>';
|
||
|
|
return h;
|
||
|
|
}
|
||
|
|
|
||
|
|
function slTableHtml(headers, rows) {
|
||
|
|
let h = '<table style="width:100%;border-collapse:collapse;font-size:0.85rem;margin-top:8px">';
|
||
|
|
h += '<thead><tr>';
|
||
|
|
for (const hdr of headers) h += '<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--border);color:var(--text-secondary)">' + esc(hdr) + '</th>';
|
||
|
|
h += '</tr></thead><tbody>';
|
||
|
|
for (const row of rows) {
|
||
|
|
h += '<tr>';
|
||
|
|
for (const cell of row) h += '<td style="padding:5px 8px;border-bottom:1px solid rgba(51,54,80,0.4)">' + esc(String(cell)) + '</td>';
|
||
|
|
h += '</tr>';
|
||
|
|
}
|
||
|
|
h += '</tbody></table>';
|
||
|
|
return h;
|
||
|
|
}
|
||
|
|
|
||
|
|
function slAlert(msg, type) {
|
||
|
|
const color = type === 'error' ? '#ef4444' : type === 'warning' ? '#f97316' : '#22c55e';
|
||
|
|
return '<div style="padding:8px 12px;border-left:3px solid ' + color + ';background:var(--bg-card);border-radius:var(--radius);font-size:0.85rem;margin-bottom:8px;color:' + color + '">' + esc(msg) + '</div>';
|
||
|
|
}
|
||
|
|
|
||
|
|
function slJsonBlock(data) {
|
||
|
|
return '<pre style="background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);padding:12px;font-size:0.8rem;overflow-x:auto;max-height:400px;color:var(--text-primary)">' + esc(JSON.stringify(data, null, 2)) + '</pre>';
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ── Discovery & Status ── */
|
||
|
|
|
||
|
|
async function slDiscover() {
|
||
|
|
const ip = document.getElementById('sl-dish-ip').value.trim();
|
||
|
|
setLoading('btn-sl-discover', true);
|
||
|
|
slShowResult('sl-discover-result', '<span style="color:var(--text-muted)">Discovering...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/discover', {ip: ip || undefined});
|
||
|
|
let h = '';
|
||
|
|
if (r.found) {
|
||
|
|
h += slAlert('Dish found at ' + (r.target || ip), 'success');
|
||
|
|
h += slCardHtml('Discovery Results', [
|
||
|
|
['Target IP', r.target],
|
||
|
|
['gRPC Port (9200)', r.grpc_available ? 'OPEN' : 'closed'],
|
||
|
|
['HTTP Port (80)', r.http_available ? 'OPEN' : 'closed'],
|
||
|
|
['Firmware', r.firmware || 'unknown'],
|
||
|
|
['Hardware', r.hardware || 'unknown'],
|
||
|
|
['Extra Open Ports', (r.details?.extra_open_ports || []).join(', ') || 'none'],
|
||
|
|
]);
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Dish not found', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-discover-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-discover-result', slAlert('Request failed: ' + e.message, 'error'));
|
||
|
|
}
|
||
|
|
setLoading('btn-sl-discover', false);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slGetStatus() {
|
||
|
|
setLoading('btn-sl-status', true);
|
||
|
|
slShowResult('sl-status-result', '<span style="color:var(--text-muted)">Loading...</span>');
|
||
|
|
try {
|
||
|
|
const r = await fetchJSON('/starlink-hack/dish-status');
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
h += slCardHtml('Dish Status', [
|
||
|
|
['State', r.device_state],
|
||
|
|
['Uptime', r.uptime_human],
|
||
|
|
['Firmware', r.software_version],
|
||
|
|
['Hardware', r.hardware_version],
|
||
|
|
['Alerts', r.alert_count + ' active' + (r.alerts?.length ? ': ' + r.alerts.join(', ') : '')],
|
||
|
|
['Obstruction', (r.obstruction?.fraction_obstructed || 0) + '%'],
|
||
|
|
['Downlink', (r.downlink_throughput_bps || 0).toLocaleString() + ' bps'],
|
||
|
|
['Uplink', (r.uplink_throughput_bps || 0).toLocaleString() + ' bps'],
|
||
|
|
['Latency', (r.pop_ping_latency_ms || 0) + ' ms'],
|
||
|
|
['Drop Rate', ((r.pop_ping_drop_rate || 0) * 100).toFixed(1) + '%'],
|
||
|
|
['ETH Speed', (r.eth_speed_mbps || 0) + ' Mbps'],
|
||
|
|
['SNR OK', r.snr_above_noise_floor ? 'Yes' : 'No'],
|
||
|
|
['Stowed', r.stowed ? 'Yes' : 'No'],
|
||
|
|
]);
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Failed to get status', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-status-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-status-result', slAlert('Request failed: ' + e.message, 'error'));
|
||
|
|
}
|
||
|
|
setLoading('btn-sl-status', false);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slGetInfo() {
|
||
|
|
slShowResult('sl-status-result', '<span style="color:var(--text-muted)">Loading device info...</span>');
|
||
|
|
try {
|
||
|
|
const r = await fetchJSON('/starlink-hack/dish-info');
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
h += slCardHtml('Device Info', [
|
||
|
|
['Device ID', r.device_id],
|
||
|
|
['Hardware Version', r.hardware_version],
|
||
|
|
['Software Version', r.software_version],
|
||
|
|
['Country Code', r.country_code],
|
||
|
|
['Boot Count', r.bootcount || 'N/A'],
|
||
|
|
['Is Dev', r.is_dev ? 'Yes' : 'No'],
|
||
|
|
['Board Rev', r.board_rev || 'N/A'],
|
||
|
|
['Anti-Rollback', r.anti_rollback_version || 'N/A'],
|
||
|
|
]);
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Failed to get info', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-status-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-status-result', slAlert('Request failed: ' + e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slGetNetwork() {
|
||
|
|
setLoading('btn-sl-network', true);
|
||
|
|
slShowResult('sl-network-result', '<span style="color:var(--text-muted)">Loading...</span>');
|
||
|
|
try {
|
||
|
|
const r = await fetchJSON('/starlink-hack/network');
|
||
|
|
let h = '';
|
||
|
|
const cfg = r.wifi_config || {};
|
||
|
|
h += slCardHtml('WiFi Configuration', [
|
||
|
|
['SSID', cfg.ssid || 'unknown'],
|
||
|
|
['Security', cfg.security || 'unknown'],
|
||
|
|
['Band', cfg.band || 'unknown'],
|
||
|
|
['Channel', cfg.channel || 'auto'],
|
||
|
|
]);
|
||
|
|
const clients = r.wifi_clients || [];
|
||
|
|
if (clients.length) {
|
||
|
|
const rows = clients.map(c => [c.name || 'unknown', c.mac || '?', c.ip || '?', (c.signal_strength != null ? c.signal_strength + ' dBm' : 'N/A'), c.band || '?']);
|
||
|
|
h += '<h3 style="font-size:0.95rem;color:#f97316;margin:12px 0 4px 0">WiFi Clients (' + clients.length + ')</h3>';
|
||
|
|
h += slTableHtml(['Name', 'MAC', 'IP', 'Signal', 'Band'], rows);
|
||
|
|
} else {
|
||
|
|
h += '<p style="font-size:0.85rem;color:var(--text-muted)">No WiFi clients found.</p>';
|
||
|
|
}
|
||
|
|
slShowResult('sl-network-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-network-result', slAlert('Request failed: ' + e.message, 'error'));
|
||
|
|
}
|
||
|
|
setLoading('btn-sl-network', false);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slPortScan() {
|
||
|
|
setLoading('btn-sl-portscan', true);
|
||
|
|
document.getElementById('sl-portscan-status').textContent = 'Scanning (this may take a while)...';
|
||
|
|
slShowResult('sl-portscan-result', '');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/scan-ports', {target: document.getElementById('sl-dish-ip').value.trim() || undefined});
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
const ports = r.ports || [];
|
||
|
|
document.getElementById('sl-portscan-status').textContent = ports.length + ' open ports found (' + (r.scanner || 'unknown') + ')';
|
||
|
|
if (ports.length) {
|
||
|
|
h += slTableHtml(['Port', 'Protocol', 'State', 'Service'], ports.map(p => [p.port, p.protocol, p.state, p.service]));
|
||
|
|
} else {
|
||
|
|
h += '<p style="font-size:0.85rem;color:var(--text-muted)">No open ports found.</p>';
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
document.getElementById('sl-portscan-status').textContent = '';
|
||
|
|
h += slAlert(r.error || 'Scan failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-portscan-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
document.getElementById('sl-portscan-status').textContent = '';
|
||
|
|
slShowResult('sl-portscan-result', slAlert('Request failed: ' + e.message, 'error'));
|
||
|
|
}
|
||
|
|
setLoading('btn-sl-portscan', false);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slModuleStatus() {
|
||
|
|
try {
|
||
|
|
const r = await fetchJSON('/starlink-hack/status');
|
||
|
|
let h = slCardHtml('Module Status', [
|
||
|
|
['Module', r.module + ' v' + r.version],
|
||
|
|
['Category', r.category],
|
||
|
|
['Dish IP', r.dish_ip],
|
||
|
|
['Results Count', r.results_count],
|
||
|
|
['Intercept Running', r.intercept_running ? 'Yes' : 'No'],
|
||
|
|
['Data Dir', r.data_dir],
|
||
|
|
]);
|
||
|
|
const tools = r.tools || {};
|
||
|
|
const toolRows = Object.entries(tools).map(([k, v]) => [k, v ? 'Available' : 'Missing']);
|
||
|
|
h += '<h3 style="font-size:0.95rem;color:#f97316;margin:12px 0 4px 0">Tool Availability</h3>';
|
||
|
|
h += slTableHtml(['Tool', 'Status'], toolRows);
|
||
|
|
slShowResult('sl-discover-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-discover-result', slAlert('Request failed: ' + e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ── gRPC Control ── */
|
||
|
|
|
||
|
|
async function slGrpcEnum() {
|
||
|
|
setLoading('btn-sl-grpc-enum', true);
|
||
|
|
slShowResult('sl-grpc-enum-result', '<span style="color:var(--text-muted)">Enumerating...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/grpc/enumerate', {});
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
const src = r.source ? ' (source: ' + r.source + ')' : '';
|
||
|
|
h += '<p style="font-size:0.85rem;color:var(--text-secondary)">' + (r.method_count || 0) + ' methods found' + esc(src) + '</p>';
|
||
|
|
if (r.note) h += '<p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:8px">' + esc(r.note) + '</p>';
|
||
|
|
const methods = r.methods || [];
|
||
|
|
if (methods.length) {
|
||
|
|
const rows = methods.map(m => {
|
||
|
|
if (typeof m === 'object') return [m.method || m.name || '?', m.service || '', m.description || m.desc || ''];
|
||
|
|
return [m, '', ''];
|
||
|
|
});
|
||
|
|
h += slTableHtml(['Method', 'Service', 'Description'], rows);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Enumeration failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-grpc-enum-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-grpc-enum-result', slAlert('Request failed: ' + e.message, 'error'));
|
||
|
|
}
|
||
|
|
setLoading('btn-sl-grpc-enum', false);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slGrpcStow() {
|
||
|
|
if (!confirm('Stow the dish? This will park the antenna.')) return;
|
||
|
|
slShowResult('sl-grpc-action-result', '<span style="color:var(--text-muted)">Sending stow...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/grpc/stow', {});
|
||
|
|
slShowResult('sl-grpc-action-result', r.ok ? slAlert(r.message || 'Stow sent', 'success') : slAlert(r.error || 'Failed', 'error'));
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-grpc-action-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slGrpcUnstow() {
|
||
|
|
slShowResult('sl-grpc-action-result', '<span style="color:var(--text-muted)">Sending unstow...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/grpc/unstow', {});
|
||
|
|
slShowResult('sl-grpc-action-result', r.ok ? slAlert(r.message || 'Unstow sent', 'success') : slAlert(r.error || 'Failed', 'error'));
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-grpc-action-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slGrpcReboot() {
|
||
|
|
if (!confirm('Reboot the Starlink dish? It will be offline for ~2 minutes.')) return;
|
||
|
|
slShowResult('sl-grpc-action-result', '<span style="color:var(--text-muted)">Sending reboot...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/grpc/reboot', {});
|
||
|
|
slShowResult('sl-grpc-action-result', r.ok ? slAlert(r.message || 'Reboot sent', 'success') : slAlert(r.error || 'Failed', 'error'));
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-grpc-action-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slGrpcFactoryReset() {
|
||
|
|
if (!confirm('FACTORY RESET the Starlink dish? This will ERASE ALL settings!')) return;
|
||
|
|
if (!confirm('Are you ABSOLUTELY sure? This is irreversible.')) return;
|
||
|
|
slShowResult('sl-grpc-action-result', '<span style="color:var(--text-muted)">Sending factory reset...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/grpc/factory-reset', {confirm: true});
|
||
|
|
slShowResult('sl-grpc-action-result', r.ok ? slAlert(r.message || 'Factory reset sent', 'warning') : slAlert(r.error || 'Failed', 'error'));
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-grpc-action-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slGrpcCall() {
|
||
|
|
const method = document.getElementById('sl-grpc-method').value.trim();
|
||
|
|
const paramsStr = document.getElementById('sl-grpc-params').value.trim();
|
||
|
|
if (!method) { slShowResult('sl-grpc-call-result', slAlert('Method name is required', 'error')); return; }
|
||
|
|
let params = null;
|
||
|
|
if (paramsStr) {
|
||
|
|
try { params = JSON.parse(paramsStr); } catch (e) { slShowResult('sl-grpc-call-result', slAlert('Invalid JSON: ' + e.message, 'error')); return; }
|
||
|
|
}
|
||
|
|
slShowResult('sl-grpc-call-result', '<span style="color:var(--text-muted)">Calling ' + esc(method) + '...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/grpc/call', {method, params});
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
h += slAlert('Call successful', 'success');
|
||
|
|
h += slJsonBlock(r.response || {});
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Call failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-grpc-call-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-grpc-call-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ── Network Attack ── */
|
||
|
|
|
||
|
|
async function slInterceptStart() {
|
||
|
|
const gateway = document.getElementById('sl-intercept-gateway').value.trim();
|
||
|
|
const iface = document.getElementById('sl-intercept-iface').value.trim();
|
||
|
|
slShowResult('sl-intercept-result', '<span style="color:var(--text-muted)">Starting interception...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/network/intercept', {target_ip: gateway || undefined, interface: iface || undefined});
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
h += slAlert('Interception running (PID: ' + (r.pid || '?') + ', Tool: ' + (r.tool || '?') + ')', 'success');
|
||
|
|
if (r.capture_file) h += '<p style="font-size:0.85rem;color:var(--text-secondary)">Capture: ' + esc(r.capture_file) + '</p>';
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-intercept-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-intercept-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slInterceptStop() {
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/network/intercept/stop', {});
|
||
|
|
slShowResult('sl-intercept-result', slAlert(r.message || 'Stopped', 'success'));
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-intercept-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slDnsStart() {
|
||
|
|
const domain = document.getElementById('sl-dns-domain').value.trim();
|
||
|
|
const ip = document.getElementById('sl-dns-ip').value.trim();
|
||
|
|
if (!domain || !ip) { slShowResult('sl-dns-result', slAlert('Domain and IP are required', 'error')); return; }
|
||
|
|
slShowResult('sl-dns-result', '<span style="color:var(--text-muted)">Starting DNS spoof...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/network/dns-spoof', {domain, ip});
|
||
|
|
slShowResult('sl-dns-result', r.ok ? slAlert('DNS spoofing active: ' + domain + ' -> ' + ip, 'success') : slAlert(r.error || 'Failed', 'error'));
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-dns-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slDnsStop() {
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/network/dns-spoof/stop', {});
|
||
|
|
slShowResult('sl-dns-result', slAlert(r.message || 'Stopped', 'success'));
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-dns-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slDeauth() {
|
||
|
|
const mac = document.getElementById('sl-deauth-mac').value.trim();
|
||
|
|
const iface = document.getElementById('sl-deauth-iface').value.trim();
|
||
|
|
if (!iface) { slShowResult('sl-deauth-result', slAlert('Wireless interface is required', 'error')); return; }
|
||
|
|
slShowResult('sl-deauth-result', '<span style="color:var(--text-muted)">Sending deauth...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/network/deauth', {target_mac: mac || undefined, interface: iface});
|
||
|
|
slShowResult('sl-deauth-result', r.ok ? slAlert('Deauth sent to ' + (r.target || 'broadcast'), 'success') : slAlert(r.error || 'Failed', 'error'));
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-deauth-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slMitm() {
|
||
|
|
const iface = document.getElementById('sl-mitm-iface').value.trim();
|
||
|
|
slShowResult('sl-mitm-result', '<span style="color:var(--text-muted)">Starting MITM...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/network/mitm', {interface: iface || undefined});
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
h += slAlert('MITM running (Tool: ' + (r.tool || '?') + ', PID: ' + (r.pid || '?') + ')', 'success');
|
||
|
|
if (r.note) h += '<p style="font-size:0.85rem;color:var(--text-secondary)">' + esc(r.note) + '</p>';
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-mitm-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-mitm-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ── RF & Firmware ── */
|
||
|
|
|
||
|
|
async function slFirmwareCheck() {
|
||
|
|
setLoading('btn-sl-fw-check', true);
|
||
|
|
slShowResult('sl-fw-check-result', '<span style="color:var(--text-muted)">Checking firmware...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/firmware/check', {});
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
h += slCardHtml('Firmware Check', [
|
||
|
|
['Software Version', r.software_version || 'unknown'],
|
||
|
|
['Hardware Version', r.hardware_version || 'unknown'],
|
||
|
|
['Version Age', r.version_age || 'unknown'],
|
||
|
|
['Vulnerable', r.vulnerable ? 'YES' : 'No'],
|
||
|
|
]);
|
||
|
|
if (r.vulnerable) {
|
||
|
|
h += '<h3 style="color:#ef4444;font-size:0.95rem;margin:8px 0 4px 0">Matched Vulnerabilities</h3>';
|
||
|
|
const vulnRows = (r.vulnerabilities || []).map(v => [v.cve, v.title, v.severity, v.cvss]);
|
||
|
|
h += slTableHtml(['CVE', 'Title', 'Severity', 'CVSS'], vulnRows);
|
||
|
|
}
|
||
|
|
if (r.hardware_note) h += '<p style="font-size:0.8rem;color:var(--text-muted);margin-top:8px">' + esc(r.hardware_note) + '</p>';
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Check failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-fw-check-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-fw-check-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
setLoading('btn-sl-fw-check', false);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slFirmwareAnalyze() {
|
||
|
|
const fileInput = document.getElementById('sl-fw-file');
|
||
|
|
if (!fileInput.files.length) { slShowResult('sl-fw-analyze-result', slAlert('Select a firmware file', 'error')); return; }
|
||
|
|
slShowResult('sl-fw-analyze-result', '<span style="color:var(--text-muted)">Analyzing firmware (may take a while)...</span>');
|
||
|
|
const form = new FormData();
|
||
|
|
form.append('file', fileInput.files[0]);
|
||
|
|
try {
|
||
|
|
const resp = await fetch('/starlink-hack/firmware/analyze', {method: 'POST', body: form});
|
||
|
|
const r = await resp.json();
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
const fi = r.file_info || {};
|
||
|
|
h += slCardHtml('File Info', [
|
||
|
|
['Name', fi.name || '?'],
|
||
|
|
['Size', fi.size_human || '?'],
|
||
|
|
['Signatures Found', r.signature_count || 0],
|
||
|
|
['Interesting Strings', r.interesting_strings_count || 0],
|
||
|
|
]);
|
||
|
|
const ent = r.entropy_analysis || {};
|
||
|
|
if (ent.average !== undefined) {
|
||
|
|
h += slCardHtml('Entropy Analysis', [
|
||
|
|
['Average Entropy', ent.average],
|
||
|
|
['Max Entropy', ent.max],
|
||
|
|
['Min Entropy', ent.min],
|
||
|
|
['High Entropy Blocks', ent.high_entropy_blocks],
|
||
|
|
['Likely Encrypted', ent.likely_encrypted ? 'Yes' : 'No'],
|
||
|
|
['Likely Compressed', ent.likely_compressed ? 'Yes' : 'No'],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
const sigs = r.signatures || [];
|
||
|
|
if (sigs.length) {
|
||
|
|
h += '<h3 style="font-size:0.95rem;color:#f97316;margin:12px 0 4px 0">Signatures (' + sigs.length + ')</h3>';
|
||
|
|
h += slTableHtml(['Offset', 'Description'], sigs.slice(0, 50).map(s => [s.hex_offset || ('0x' + (s.offset || 0).toString(16)), s.description || '?']));
|
||
|
|
}
|
||
|
|
const strs = r.strings_of_interest || [];
|
||
|
|
if (strs.length) {
|
||
|
|
h += '<h3 style="font-size:0.95rem;color:#f97316;margin:12px 0 4px 0">Interesting Strings (' + strs.length + ')</h3>';
|
||
|
|
h += '<div style="background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);padding:10px;max-height:300px;overflow-y:auto;font-family:monospace;font-size:0.8rem">';
|
||
|
|
for (const s of strs.slice(0, 100)) h += '<div>' + esc(s) + '</div>';
|
||
|
|
h += '</div>';
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Analysis failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-fw-analyze-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-fw-analyze-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slDebugInterfaces() {
|
||
|
|
slShowResult('sl-debug-result', '<span style="color:var(--text-muted)">Scanning...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/firmware/debug', {});
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
const ports = r.serial_ports || [];
|
||
|
|
h += slCardHtml('Debug Interface Scan', [
|
||
|
|
['Serial Ports Found', ports.length],
|
||
|
|
['JTAG Adapter Detected', r.jtag_detected ? 'Yes' : 'No'],
|
||
|
|
['OpenOCD Available', r.openocd_available ? 'Yes' : 'No'],
|
||
|
|
]);
|
||
|
|
if (ports.length) {
|
||
|
|
h += slTableHtml(['Device', 'Description', 'VID:PID', 'JTAG?'],
|
||
|
|
ports.map(p => [p.device, p.description, p.vid_pid || 'N/A', p.possible_jtag ? 'POSSIBLE' : '-']));
|
||
|
|
}
|
||
|
|
const inst = r.instructions || {};
|
||
|
|
if (inst.uart) {
|
||
|
|
h += slCardHtml('UART Instructions', [
|
||
|
|
['Settings', inst.uart.settings],
|
||
|
|
['Location', inst.uart.location],
|
||
|
|
['Tools', inst.uart.tools_needed],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Scan failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-debug-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-debug-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slRfDownlink() {
|
||
|
|
const device = document.getElementById('sl-rf-device').value;
|
||
|
|
const duration = parseInt(document.getElementById('sl-rf-duration').value) || 30;
|
||
|
|
slShowResult('sl-rf-downlink-result', '<span style="color:var(--text-muted)">Analyzing downlink (this may take ' + duration + 's)...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/rf/downlink', {device, duration});
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
h += slCardHtml('Downlink Analysis', [
|
||
|
|
['Band', r.band || 'Ku-band'],
|
||
|
|
['Freq Range', r.freq_range || '10.7-12.7 GHz'],
|
||
|
|
['Source', r.source || r.tool || 'unknown'],
|
||
|
|
['Capture File', r.capture_file || 'N/A'],
|
||
|
|
['Sweep Points', r.sweep_points || 'N/A'],
|
||
|
|
]);
|
||
|
|
if (r.dish_diagnostics) {
|
||
|
|
h += slCardHtml('Dish Diagnostics (Fallback)', [
|
||
|
|
['Downlink Throughput', (r.dish_diagnostics.downlink_throughput_bps || 0).toLocaleString() + ' bps'],
|
||
|
|
['Latency', (r.dish_diagnostics.pop_ping_latency_ms || 0) + ' ms'],
|
||
|
|
['Obstruction', (r.dish_diagnostics.obstruction_pct || 0) + '%'],
|
||
|
|
['SNR OK', r.dish_diagnostics.snr_above_noise_floor ? 'Yes' : 'No'],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
if (r.note) h += '<p style="font-size:0.8rem;color:var(--text-muted);margin-top:4px">' + esc(r.note) + '</p>';
|
||
|
|
if (r.alternatives) {
|
||
|
|
h += '<p style="font-size:0.8rem;color:var(--text-muted);margin-top:4px"><strong>LNB method:</strong> ' + esc(r.alternatives.lnb_method) + '</p>';
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Analysis failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-rf-downlink-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-rf-downlink-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slDetectJamming() {
|
||
|
|
slShowResult('sl-jamming-result', '<span style="color:var(--text-muted)">Analyzing jamming indicators...</span>');
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/rf/jamming', {});
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
if (r.jamming_detected) {
|
||
|
|
h += slAlert('JAMMING INDICATORS DETECTED (Confidence: ' + (r.confidence || 'unknown') + ')', 'error');
|
||
|
|
} else {
|
||
|
|
h += slAlert('No jamming indicators detected (Confidence: ' + (r.confidence || 'none') + ')', 'success');
|
||
|
|
}
|
||
|
|
const indicators = r.indicators || [];
|
||
|
|
if (indicators.length) {
|
||
|
|
const rows = indicators.map(i => {
|
||
|
|
const sevColor = i.severity === 'high' ? '#ef4444' : i.severity === 'medium' ? '#f97316' : '#eab308';
|
||
|
|
return ['<span style="color:' + sevColor + '">' + esc(i.severity.toUpperCase()) + '</span>', esc(i.type), esc(i.detail)];
|
||
|
|
});
|
||
|
|
h += '<table style="width:100%;border-collapse:collapse;font-size:0.85rem;margin-top:8px"><thead><tr>';
|
||
|
|
h += '<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--border);color:var(--text-secondary)">Severity</th>';
|
||
|
|
h += '<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--border);color:var(--text-secondary)">Type</th>';
|
||
|
|
h += '<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--border);color:var(--text-secondary)">Detail</th>';
|
||
|
|
h += '</tr></thead><tbody>';
|
||
|
|
for (const row of rows) {
|
||
|
|
h += '<tr><td style="padding:5px 8px;border-bottom:1px solid rgba(51,54,80,0.4)">' + row[0] + '</td>';
|
||
|
|
h += '<td style="padding:5px 8px;border-bottom:1px solid rgba(51,54,80,0.4)">' + row[1] + '</td>';
|
||
|
|
h += '<td style="padding:5px 8px;border-bottom:1px solid rgba(51,54,80,0.4)">' + row[2] + '</td></tr>';
|
||
|
|
}
|
||
|
|
h += '</tbody></table>';
|
||
|
|
}
|
||
|
|
if (r.recommendation) h += '<p style="font-size:0.8rem;color:var(--text-muted);margin-top:8px">' + esc(r.recommendation) + '</p>';
|
||
|
|
} else {
|
||
|
|
h += slAlert(r.error || 'Detection failed', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-jamming-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-jamming-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slLoadCves() {
|
||
|
|
slShowResult('sl-cve-result', '<span style="color:var(--text-muted)">Loading CVE database...</span>');
|
||
|
|
try {
|
||
|
|
const r = await fetchJSON('/starlink-hack/cves');
|
||
|
|
let h = '';
|
||
|
|
if (r.ok) {
|
||
|
|
if (r.current_firmware) {
|
||
|
|
h += '<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:8px">Current firmware: <strong>' + esc(r.current_firmware) + '</strong>';
|
||
|
|
if (r.applicable_count > 0) {
|
||
|
|
h += ' — <span style="color:#ef4444">' + r.applicable_count + ' applicable CVE(s)</span>';
|
||
|
|
}
|
||
|
|
h += '</p>';
|
||
|
|
}
|
||
|
|
const cves = r.cves || [];
|
||
|
|
h += '<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:8px">Total known CVEs: ' + cves.length + '</p>';
|
||
|
|
for (const cve of cves) {
|
||
|
|
const sevColor = cve.severity === 'Critical' ? '#ef4444' : cve.severity === 'High' ? '#f97316' : cve.severity === 'Medium' ? '#eab308' : '#22c55e';
|
||
|
|
h += '<div style="background:var(--bg-card);border:1px solid var(--border);border-left:3px solid ' + sevColor + ';border-radius:var(--radius);padding:12px;margin-bottom:10px">';
|
||
|
|
h += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">';
|
||
|
|
h += '<strong style="color:' + sevColor + '">' + esc(cve.cve) + '</strong>';
|
||
|
|
h += '<span style="font-size:0.8rem;color:' + sevColor + '">' + esc(cve.severity) + ' (CVSS ' + cve.cvss + ')</span>';
|
||
|
|
h += '</div>';
|
||
|
|
h += '<div style="font-size:0.9rem;font-weight:600;margin-bottom:4px">' + esc(cve.title) + '</div>';
|
||
|
|
h += '<div style="font-size:0.82rem;color:var(--text-secondary);margin-bottom:4px">' + esc(cve.description) + '</div>';
|
||
|
|
h += '<div style="font-size:0.8rem;color:var(--text-muted)"><strong>Affected:</strong> ' + esc(cve.affected) + '</div>';
|
||
|
|
h += '<div style="font-size:0.8rem;color:var(--text-muted)"><strong>Technique:</strong> ' + esc(cve.technique) + '</div>';
|
||
|
|
if (cve.references && cve.references.length) {
|
||
|
|
h += '<div style="font-size:0.8rem;margin-top:4px">';
|
||
|
|
for (const ref of cve.references) h += '<a href="' + esc(ref) + '" target="_blank" rel="noopener" style="display:block;color:var(--accent);font-size:0.78rem">' + esc(ref) + '</a>';
|
||
|
|
h += '</div>';
|
||
|
|
}
|
||
|
|
h += '</div>';
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
h += slAlert('Failed to load CVEs', 'error');
|
||
|
|
}
|
||
|
|
slShowResult('sl-cve-result', h);
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-cve-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function slExportResults() {
|
||
|
|
try {
|
||
|
|
const r = await postJSON('/starlink-hack/export', {});
|
||
|
|
if (r.ok) {
|
||
|
|
slShowResult('sl-cve-result', slAlert('Results exported to: ' + (r.path || '?') + ' (' + (r.size_human || '?') + ', ' + (r.entries || 0) + ' entries)', 'success'));
|
||
|
|
} else {
|
||
|
|
slShowResult('sl-cve-result', slAlert(r.error || 'Export failed', 'error'));
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
slShowResult('sl-cve-result', slAlert(e.message, 'error'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
{% endblock %}
|