mirror of
https://github.com/smittix/intercept.git
synced 2026-07-08 09:38:12 -07:00
Fix WiFi client updates, rogue AP detection, and channel recommendation
Backend: - Send client updates when probes or signal change significantly - Previously only new clients were reported, updates were ignored Frontend: - Add client cards to device list (was only showing networks) - Fix rogue AP detection to check OUI - excludes legitimate mesh systems - Improve channel recommendation with detailed usage breakdown - Show per-channel interference counts for 2.4GHz - Show unused channel count for 5GHz Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -359,6 +359,20 @@ def stream_airodump_output(process, csv_path):
|
|||||||
'action': 'new',
|
'action': 'new',
|
||||||
**client
|
**client
|
||||||
})
|
})
|
||||||
|
else:
|
||||||
|
# Send update if probes changed or signal changed significantly
|
||||||
|
old_client = app_module.wifi_clients[mac]
|
||||||
|
old_probes = old_client.get('probes', '')
|
||||||
|
new_probes = client.get('probes', '')
|
||||||
|
old_power = int(old_client.get('power', -100) or -100)
|
||||||
|
new_power = int(client.get('power', -100) or -100)
|
||||||
|
|
||||||
|
if new_probes != old_probes or abs(new_power - old_power) >= 5:
|
||||||
|
app_module.wifi_queue.put({
|
||||||
|
'type': 'client',
|
||||||
|
'action': 'update',
|
||||||
|
**client
|
||||||
|
})
|
||||||
|
|
||||||
app_module.wifi_networks = networks
|
app_module.wifi_networks = networks
|
||||||
app_module.wifi_clients = clients
|
app_module.wifi_clients = clients
|
||||||
|
|||||||
+121
-13
@@ -3922,6 +3922,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for rogue APs (same SSID, different BSSID)
|
// Check for rogue APs (same SSID, different BSSID)
|
||||||
|
// Extract OUI (manufacturer ID) from MAC address
|
||||||
|
function getOui(mac) {
|
||||||
|
if (!mac) return '';
|
||||||
|
return mac.toUpperCase().substring(0, 8); // First 3 octets: "AA:BB:CC"
|
||||||
|
}
|
||||||
|
|
||||||
function checkRogueAP(ssid, bssid, channel, signal) {
|
function checkRogueAP(ssid, bssid, channel, signal) {
|
||||||
if (!ssid || ssid === 'Hidden' || ssid === '[Hidden]') return false;
|
if (!ssid || ssid === 'Hidden' || ssid === '[Hidden]') return false;
|
||||||
|
|
||||||
@@ -3941,6 +3947,7 @@
|
|||||||
bssid: bssid,
|
bssid: bssid,
|
||||||
channel: channel || '?',
|
channel: channel || '?',
|
||||||
signal: signal || '?',
|
signal: signal || '?',
|
||||||
|
oui: getOui(bssid),
|
||||||
firstSeen: new Date().toLocaleTimeString()
|
firstSeen: new Date().toLocaleTimeString()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -3948,8 +3955,19 @@
|
|||||||
const isNewBssid = !ssidToBssids[ssid].has(bssid);
|
const isNewBssid = !ssidToBssids[ssid].has(bssid);
|
||||||
ssidToBssids[ssid].add(bssid);
|
ssidToBssids[ssid].add(bssid);
|
||||||
|
|
||||||
// If we have more than one BSSID for this SSID, it could be rogue (or just multiple APs)
|
// Only flag as rogue if multiple BSSIDs AND different manufacturers (OUIs)
|
||||||
|
// This prevents false positives from mesh WiFi systems and enterprise networks
|
||||||
if (ssidToBssids[ssid].size > 1 && isNewBssid) {
|
if (ssidToBssids[ssid].size > 1 && isNewBssid) {
|
||||||
|
// Check if all BSSIDs have the same OUI (manufacturer)
|
||||||
|
const ouis = new Set(rogueApDetails[ssid].map(e => e.oui));
|
||||||
|
|
||||||
|
// If all BSSIDs have the same OUI, it's likely a mesh system - not rogue
|
||||||
|
if (ouis.size === 1) {
|
||||||
|
// Same manufacturer - probably mesh system, not rogue
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Different manufacturers detected - this is suspicious!
|
||||||
rogueApCount++;
|
rogueApCount++;
|
||||||
document.getElementById('rogueApCount').textContent = rogueApCount;
|
document.getElementById('rogueApCount').textContent = rogueApCount;
|
||||||
playAlert();
|
playAlert();
|
||||||
@@ -3959,8 +3977,8 @@
|
|||||||
|
|
||||||
// Get the BSSIDs to show in alert
|
// Get the BSSIDs to show in alert
|
||||||
const bssidList = rogueApDetails[ssid].map(e => e.bssid).join(', ');
|
const bssidList = rogueApDetails[ssid].map(e => e.bssid).join(', ');
|
||||||
showInfo(`⚠ Rogue AP: "${ssid}" has ${ssidToBssids[ssid].size} BSSIDs: ${bssidList}`);
|
showInfo(`⚠ Rogue AP: "${ssid}" has ${ouis.size} different vendors: ${bssidList}`);
|
||||||
showNotification('⚠️ Rogue AP Detected!', `"${ssid}" on multiple BSSIDs`);
|
showNotification('⚠️ Rogue AP Detected!', `"${ssid}" has different vendor BSSIDs`);
|
||||||
|
|
||||||
// Update all network cards with this SSID to show rogue indicator
|
// Update all network cards with this SSID to show rogue indicator
|
||||||
ssidToBssids[ssid].forEach(rogueBssid => {
|
ssidToBssids[ssid].forEach(rogueBssid => {
|
||||||
@@ -4246,13 +4264,18 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Find best 2.4 GHz channel (1, 6, or 11 preferred)
|
// Count total networks for context
|
||||||
|
const totalNetworks = Object.keys(wifiNetworks).length;
|
||||||
|
|
||||||
|
// Find best 2.4 GHz channel (1, 6, or 11 preferred - non-overlapping)
|
||||||
const preferred24 = [1, 6, 11];
|
const preferred24 = [1, 6, 11];
|
||||||
let best24 = 1;
|
let best24 = 1;
|
||||||
let minCount24 = Infinity;
|
let minCount24 = Infinity;
|
||||||
|
let channelUsage24 = [];
|
||||||
preferred24.forEach(ch => {
|
preferred24.forEach(ch => {
|
||||||
if (channelCounts24[ch] < minCount24) {
|
channelUsage24.push({ channel: ch, count: channelCounts24[ch] || 0 });
|
||||||
minCount24 = channelCounts24[ch];
|
if ((channelCounts24[ch] || 0) < minCount24) {
|
||||||
|
minCount24 = channelCounts24[ch] || 0;
|
||||||
best24 = ch;
|
best24 = ch;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -4260,21 +4283,33 @@
|
|||||||
// Find best 5 GHz channel
|
// Find best 5 GHz channel
|
||||||
let best5 = '36';
|
let best5 = '36';
|
||||||
let minCount5 = Infinity;
|
let minCount5 = Infinity;
|
||||||
|
let used5g = 0;
|
||||||
channels5g.forEach(ch => {
|
channels5g.forEach(ch => {
|
||||||
if (channelCounts5[ch] < minCount5) {
|
const count = channelCounts5[ch] || 0;
|
||||||
minCount5 = channelCounts5[ch];
|
if (count > 0) used5g++;
|
||||||
|
if (count < minCount5) {
|
||||||
|
minCount5 = count;
|
||||||
best5 = ch;
|
best5 = ch;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update UI
|
// Update UI with more context
|
||||||
document.getElementById('rec24Channel').textContent = best24;
|
document.getElementById('rec24Channel').textContent = best24;
|
||||||
document.getElementById('rec24Reason').textContent =
|
if (totalNetworks === 0) {
|
||||||
minCount24 === 0 ? '(unused)' : `(${Math.round(minCount24)} networks nearby)`;
|
document.getElementById('rec24Reason').textContent = '(no networks detected)';
|
||||||
|
} else {
|
||||||
|
const usage = channelUsage24.map(c => `CH${c.channel}:${Math.round(c.count)}`).join(', ');
|
||||||
|
document.getElementById('rec24Reason').textContent =
|
||||||
|
minCount24 === 0 ? '(clear)' : `(${Math.round(minCount24)} interference) [${usage}]`;
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('rec5Channel').textContent = best5;
|
document.getElementById('rec5Channel').textContent = best5;
|
||||||
document.getElementById('rec5Reason').textContent =
|
if (totalNetworks === 0) {
|
||||||
minCount5 === 0 ? '(unused)' : `(${minCount5} networks)`;
|
document.getElementById('rec5Reason').textContent = '(no networks detected)';
|
||||||
|
} else {
|
||||||
|
document.getElementById('rec5Reason').textContent =
|
||||||
|
minCount5 === 0 ? `(clear, ${channels5g.length - used5g} unused)` : `(${minCount5} networks)`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Device Correlation (WiFi <-> Bluetooth)
|
// Device Correlation (WiFi <-> Bluetooth)
|
||||||
@@ -4864,6 +4899,9 @@
|
|||||||
if (client.probes && client.probes.trim()) {
|
if (client.probes && client.probes.trim()) {
|
||||||
scheduleProbeAnalysisUpdate();
|
scheduleProbeAnalysisUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add client card to device list
|
||||||
|
addWifiClientCard(client, isNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throttled probe analysis (called less frequently)
|
// Throttled probe analysis (called less frequently)
|
||||||
@@ -5156,6 +5194,76 @@
|
|||||||
if (autoScroll) output.scrollTop = 0;
|
if (autoScroll) output.scrollTop = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add WiFi client card to device list
|
||||||
|
function addWifiClientCard(client, isNew) {
|
||||||
|
const deviceList = document.getElementById('wifiDeviceListContent');
|
||||||
|
if (!deviceList) return;
|
||||||
|
|
||||||
|
// Remove placeholder if present
|
||||||
|
const placeholder = deviceList.querySelector('div[style*="text-align: center"]');
|
||||||
|
if (placeholder && placeholder.textContent.includes('Start scanning')) {
|
||||||
|
placeholder.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if card already exists
|
||||||
|
let card = document.getElementById('client_' + client.mac.replace(/:/g, ''));
|
||||||
|
|
||||||
|
if (!card) {
|
||||||
|
card = document.createElement('div');
|
||||||
|
card.id = 'client_' + client.mac.replace(/:/g, '');
|
||||||
|
card.className = 'sensor-card wifi-client-card';
|
||||||
|
card.style.borderLeftColor = 'var(--accent-purple)';
|
||||||
|
card.style.cursor = 'pointer';
|
||||||
|
card.onclick = () => selectWifiDevice(client.mac, 'client');
|
||||||
|
deviceList.appendChild(card); // Clients go after networks
|
||||||
|
|
||||||
|
// Update device count
|
||||||
|
const countEl = document.getElementById('wifiDeviceListCount');
|
||||||
|
if (countEl) countEl.textContent = Object.keys(wifiNetworks).length + Object.keys(wifiClients).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle signal strength
|
||||||
|
let signalStrength = parseInt(client.power);
|
||||||
|
if (isNaN(signalStrength) || signalStrength === -1) {
|
||||||
|
signalStrength = null;
|
||||||
|
}
|
||||||
|
const signalBars = signalStrength !== null ? Math.max(0, Math.min(5, Math.floor((signalStrength + 100) / 15))) : 0;
|
||||||
|
const signalDisplay = signalStrength !== null ? `${signalStrength} dBm` : 'N/A';
|
||||||
|
|
||||||
|
// Get connected AP info
|
||||||
|
const connectedAP = client.bssid && wifiNetworks[client.bssid];
|
||||||
|
const apName = connectedAP ? (connectedAP.essid || '[Hidden]') : (client.bssid || 'Not Associated');
|
||||||
|
|
||||||
|
// Format probes
|
||||||
|
const probes = client.probes ? client.probes.split(',').map(p => p.trim()).filter(p => p) : [];
|
||||||
|
const probesDisplay = probes.length > 0 ? probes.slice(0, 3).join(', ') + (probes.length > 3 ? ` +${probes.length - 3}` : '') : 'None';
|
||||||
|
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="header" style="display: flex; justify-content: space-between; margin-bottom: 8px;">
|
||||||
|
<span class="device-name" style="color: var(--accent-purple);">📱 ${escapeHtml(client.vendor || 'Client')}</span>
|
||||||
|
<span style="font-size: 10px; color: var(--text-dim);">CLIENT</span>
|
||||||
|
</div>
|
||||||
|
<div class="sensor-data">
|
||||||
|
<div class="data-item">
|
||||||
|
<div class="data-label">MAC</div>
|
||||||
|
<div class="data-value" style="font-size: 11px;">${escapeHtml(client.mac)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="data-item">
|
||||||
|
<div class="data-label">Connected To</div>
|
||||||
|
<div class="data-value" style="color: var(--accent-cyan);">${escapeHtml(apName)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="data-item">
|
||||||
|
<div class="data-label">Signal</div>
|
||||||
|
<div class="data-value">${signalDisplay} ${'█'.repeat(signalBars)}${'░'.repeat(5-signalBars)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="data-item">
|
||||||
|
<div class="data-label">Probes</div>
|
||||||
|
<div class="data-value" style="font-size: 10px;">${escapeHtml(probesDisplay)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
// Target a network for attack
|
// Target a network for attack
|
||||||
function targetNetwork(bssid, channel) {
|
function targetNetwork(bssid, channel) {
|
||||||
document.getElementById('targetBssid').value = bssid;
|
document.getElementById('targetBssid').value = bssid;
|
||||||
|
|||||||
Reference in New Issue
Block a user