mirror of
https://github.com/smittix/intercept.git
synced 2026-05-18 05:44:49 -07:00
Add WiFi reconnaissance features
New features: - Client vendor lookup: Shows manufacturer for WiFi clients using OUI database - 5GHz channel utilization graph: Visual display of 5GHz channel usage - Proximity alerts: Watch list for specific MAC addresses with popup alerts - Rogue AP detection: Alerts when same SSID appears on multiple BSSIDs - Handshake capture status: Pulsing indicator during active capture - Sound toggle already exists via MUTE button UI improvements: - Added ROGUE counter to WiFi stats bar - Added Proximity Alerts section with watch list management - Both 2.4GHz and 5GHz channel graphs update in real-time 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
205
intercept.py
205
intercept.py
@@ -701,6 +701,11 @@ HTML_TEMPLATE = '''
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.3; }
|
||||||
|
}
|
||||||
|
|
||||||
.clear-btn {
|
.clear-btn {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -1674,6 +1679,20 @@ HTML_TEMPLATE = '''
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h3>Proximity Alerts</h3>
|
||||||
|
<div class="info-text" style="margin-bottom: 8px;">
|
||||||
|
Alert when specific MAC addresses appear
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" id="watchMacInput" placeholder="AA:BB:CC:DD:EE:FF">
|
||||||
|
</div>
|
||||||
|
<button class="preset-btn" onclick="addWatchMac()" style="width: 100%; margin-bottom: 8px;">
|
||||||
|
Add to Watch List
|
||||||
|
</button>
|
||||||
|
<div id="watchList" style="max-height: 80px; overflow-y: auto; font-size: 10px; color: var(--text-dim);"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h3>Attack Options</h3>
|
<h3>Attack Options</h3>
|
||||||
<div class="info-text" style="color: var(--accent-red); margin-bottom: 10px;">
|
<div class="info-text" style="color: var(--accent-red); margin-bottom: 10px;">
|
||||||
@@ -1812,7 +1831,8 @@ HTML_TEMPLATE = '''
|
|||||||
<div class="stats" id="wifiStats" style="display: none;">
|
<div class="stats" id="wifiStats" style="display: none;">
|
||||||
<div>APs: <span id="apCount">0</span></div>
|
<div>APs: <span id="apCount">0</span></div>
|
||||||
<div>CLIENTS: <span id="clientCount">0</span></div>
|
<div>CLIENTS: <span id="clientCount">0</span></div>
|
||||||
<div>HANDSHAKES: <span id="handshakeCount">0</span></div>
|
<div>HANDSHAKES: <span id="handshakeCount" style="color: var(--accent-green);">0</span></div>
|
||||||
|
<div style="color: var(--accent-red);">ROGUE: <span id="rogueApCount">0</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats" id="btStats" style="display: none;">
|
<div class="stats" id="btStats" style="display: none;">
|
||||||
<div>DEVICES: <span id="btDeviceCount">0</span></div>
|
<div>DEVICES: <span id="btDeviceCount">0</span></div>
|
||||||
@@ -1847,6 +1867,25 @@ HTML_TEMPLATE = '''
|
|||||||
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">13</span></div>
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">13</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="wifi-visual-panel">
|
||||||
|
<h5>Channel Utilization (5 GHz)</h5>
|
||||||
|
<div class="channel-graph" id="channelGraph5g" style="font-size: 7px;">
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">36</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">40</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">44</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">48</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">52</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">56</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">60</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">64</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">100</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">149</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">153</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">157</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">161</span></div>
|
||||||
|
<div class="channel-bar-wrapper"><div class="channel-bar" style="height: 2px;"></div><span class="channel-label">165</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="wifi-visual-panel">
|
<div class="wifi-visual-panel">
|
||||||
<h5>Security Overview</h5>
|
<h5>Security Overview</h5>
|
||||||
<div class="security-container">
|
<div class="security-container">
|
||||||
@@ -2000,6 +2039,7 @@ HTML_TEMPLATE = '''
|
|||||||
if (mode === 'wifi') {
|
if (mode === 'wifi') {
|
||||||
refreshWifiInterfaces();
|
refreshWifiInterfaces();
|
||||||
initRadar();
|
initRadar();
|
||||||
|
initWatchList();
|
||||||
} else if (mode === 'bluetooth') {
|
} else if (mode === 'bluetooth') {
|
||||||
refreshBtInterfaces();
|
refreshBtInterfaces();
|
||||||
initBtRadar();
|
initBtRadar();
|
||||||
@@ -3092,6 +3132,137 @@ HTML_TEMPLATE = '''
|
|||||||
let apCount = 0;
|
let apCount = 0;
|
||||||
let clientCount = 0;
|
let clientCount = 0;
|
||||||
let handshakeCount = 0;
|
let handshakeCount = 0;
|
||||||
|
let rogueApCount = 0;
|
||||||
|
let ssidToBssids = {}; // Track SSIDs to their BSSIDs for rogue AP detection
|
||||||
|
let watchMacs = JSON.parse(localStorage.getItem('watchMacs') || '[]');
|
||||||
|
let alertedMacs = new Set(); // Prevent duplicate alerts per session
|
||||||
|
|
||||||
|
// 5GHz channel mapping for the graph
|
||||||
|
const channels5g = ['36', '40', '44', '48', '52', '56', '60', '64', '100', '149', '153', '157', '161', '165'];
|
||||||
|
|
||||||
|
// Initialize watch list display
|
||||||
|
function initWatchList() {
|
||||||
|
updateWatchListDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add MAC to watch list
|
||||||
|
function addWatchMac() {
|
||||||
|
const input = document.getElementById('watchMacInput');
|
||||||
|
const mac = input.value.trim().toUpperCase();
|
||||||
|
if (!mac || !/^([0-9A-F]{2}:){5}[0-9A-F]{2}$/.test(mac)) {
|
||||||
|
alert('Please enter a valid MAC address (AA:BB:CC:DD:EE:FF)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!watchMacs.includes(mac)) {
|
||||||
|
watchMacs.push(mac);
|
||||||
|
localStorage.setItem('watchMacs', JSON.stringify(watchMacs));
|
||||||
|
updateWatchListDisplay();
|
||||||
|
}
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove MAC from watch list
|
||||||
|
function removeWatchMac(mac) {
|
||||||
|
watchMacs = watchMacs.filter(m => m !== mac);
|
||||||
|
localStorage.setItem('watchMacs', JSON.stringify(watchMacs));
|
||||||
|
alertedMacs.delete(mac);
|
||||||
|
updateWatchListDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update watch list display
|
||||||
|
function updateWatchListDisplay() {
|
||||||
|
const container = document.getElementById('watchList');
|
||||||
|
if (!container) return;
|
||||||
|
if (watchMacs.length === 0) {
|
||||||
|
container.innerHTML = '<div style="color: #555;">No MACs in watch list</div>';
|
||||||
|
} else {
|
||||||
|
container.innerHTML = watchMacs.map(mac =>
|
||||||
|
`<div style="display: flex; justify-content: space-between; align-items: center; padding: 2px 0;">
|
||||||
|
<span>${mac}</span>
|
||||||
|
<button onclick="removeWatchMac('${mac}')" style="background: none; border: none; color: var(--accent-red); cursor: pointer; font-size: 10px;">✕</button>
|
||||||
|
</div>`
|
||||||
|
).join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if MAC is in watch list and alert
|
||||||
|
function checkWatchList(mac, type) {
|
||||||
|
const upperMac = mac.toUpperCase();
|
||||||
|
if (watchMacs.includes(upperMac) && !alertedMacs.has(upperMac)) {
|
||||||
|
alertedMacs.add(upperMac);
|
||||||
|
// Play alert sound multiple times for urgency
|
||||||
|
playAlert();
|
||||||
|
setTimeout(playAlert, 300);
|
||||||
|
setTimeout(playAlert, 600);
|
||||||
|
// Show prominent alert
|
||||||
|
showProximityAlert(mac, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show proximity alert popup
|
||||||
|
function showProximityAlert(mac, type) {
|
||||||
|
const alertDiv = document.createElement('div');
|
||||||
|
alertDiv.className = 'proximity-alert';
|
||||||
|
alertDiv.innerHTML = `
|
||||||
|
<div style="font-weight: bold; color: var(--accent-red);">⚠ PROXIMITY ALERT</div>
|
||||||
|
<div>Watched ${type} detected:</div>
|
||||||
|
<div style="font-family: monospace; font-size: 14px;">${mac}</div>
|
||||||
|
<button onclick="this.parentElement.remove()" style="margin-top: 8px; padding: 4px 12px; cursor: pointer;">Dismiss</button>
|
||||||
|
`;
|
||||||
|
alertDiv.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #1a1a2e; border: 2px solid var(--accent-red); padding: 20px; border-radius: 8px; z-index: 10000; text-align: center; box-shadow: 0 0 30px rgba(255,0,0,0.5);';
|
||||||
|
document.body.appendChild(alertDiv);
|
||||||
|
// Auto-dismiss after 10 seconds
|
||||||
|
setTimeout(() => alertDiv.remove(), 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for rogue APs (same SSID, different BSSID)
|
||||||
|
function checkRogueAP(ssid, bssid) {
|
||||||
|
if (!ssid || ssid === 'Hidden' || ssid === '[Hidden]') return false;
|
||||||
|
|
||||||
|
if (!ssidToBssids[ssid]) {
|
||||||
|
ssidToBssids[ssid] = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
const isNewBssid = !ssidToBssids[ssid].has(bssid);
|
||||||
|
ssidToBssids[ssid].add(bssid);
|
||||||
|
|
||||||
|
// If we have more than one BSSID for this SSID, it could be rogue (or just multiple APs)
|
||||||
|
if (ssidToBssids[ssid].size > 1 && isNewBssid) {
|
||||||
|
rogueApCount++;
|
||||||
|
document.getElementById('rogueApCount').textContent = rogueApCount;
|
||||||
|
playAlert();
|
||||||
|
showInfo(`⚠ Potential Rogue AP: "${ssid}" seen on multiple BSSIDs (${ssidToBssids[ssid].size} total)`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 5GHz channel graph
|
||||||
|
function updateChannel5gGraph() {
|
||||||
|
const bars = document.querySelectorAll('#channelGraph5g .channel-bar');
|
||||||
|
const labels = document.querySelectorAll('#channelGraph5g .channel-label');
|
||||||
|
|
||||||
|
// Count networks per 5GHz channel
|
||||||
|
const channelCounts = {};
|
||||||
|
channels5g.forEach(ch => channelCounts[ch] = 0);
|
||||||
|
|
||||||
|
Object.values(wifiNetworks).forEach(net => {
|
||||||
|
const ch = net.channel?.toString().trim();
|
||||||
|
if (channels5g.includes(ch)) {
|
||||||
|
channelCounts[ch]++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const maxCount = Math.max(1, ...Object.values(channelCounts));
|
||||||
|
|
||||||
|
bars.forEach((bar, i) => {
|
||||||
|
const ch = channels5g[i];
|
||||||
|
const count = channelCounts[ch] || 0;
|
||||||
|
const height = Math.max(2, (count / maxCount) * 50);
|
||||||
|
bar.style.height = height + 'px';
|
||||||
|
bar.className = 'channel-bar' + (count > 0 ? ' active' : '') + (count > 3 ? ' congested' : '') + (count > 5 ? ' very-congested' : '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh WiFi interfaces
|
// Refresh WiFi interfaces
|
||||||
function refreshWifiInterfaces() {
|
function refreshWifiInterfaces() {
|
||||||
@@ -3291,6 +3462,12 @@ HTML_TEMPLATE = '''
|
|||||||
document.getElementById('apCount').textContent = apCount;
|
document.getElementById('apCount').textContent = apCount;
|
||||||
playAlert();
|
playAlert();
|
||||||
pulseSignal();
|
pulseSignal();
|
||||||
|
|
||||||
|
// Check for rogue AP (same SSID, different BSSID)
|
||||||
|
checkRogueAP(net.essid, net.bssid);
|
||||||
|
|
||||||
|
// Check proximity watch list
|
||||||
|
checkWatchList(net.bssid, 'AP');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update recon display
|
// Update recon display
|
||||||
@@ -3305,6 +3482,10 @@ HTML_TEMPLATE = '''
|
|||||||
|
|
||||||
// Add to output
|
// Add to output
|
||||||
addWifiNetworkCard(net, isNew);
|
addWifiNetworkCard(net, isNew);
|
||||||
|
|
||||||
|
// Update both channel graphs
|
||||||
|
updateChannelGraph();
|
||||||
|
updateChannel5gGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle discovered WiFi client
|
// Handle discovered WiFi client
|
||||||
@@ -3315,14 +3496,19 @@ HTML_TEMPLATE = '''
|
|||||||
if (isNew) {
|
if (isNew) {
|
||||||
clientCount++;
|
clientCount++;
|
||||||
document.getElementById('clientCount').textContent = clientCount;
|
document.getElementById('clientCount').textContent = clientCount;
|
||||||
|
|
||||||
|
// Check proximity watch list
|
||||||
|
checkWatchList(client.mac, 'Client');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track in device intelligence
|
// Track in device intelligence with vendor info
|
||||||
|
const vendorInfo = client.vendor && client.vendor !== 'Unknown' ? ` [${client.vendor}]` : '';
|
||||||
trackDevice({
|
trackDevice({
|
||||||
protocol: 'WiFi-Client',
|
protocol: 'WiFi-Client',
|
||||||
address: client.mac,
|
address: client.mac,
|
||||||
message: client.probes || '[No probes]',
|
message: (client.probes || '[No probes]') + vendorInfo,
|
||||||
bssid: client.bssid
|
bssid: client.bssid,
|
||||||
|
vendor: client.vendor
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3400,8 +3586,12 @@ HTML_TEMPLATE = '''
|
|||||||
}).then(r => r.json())
|
}).then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.status === 'started') {
|
if (data.status === 'started') {
|
||||||
showInfo('Capturing handshakes for ' + bssid + '. File: ' + data.capture_file);
|
showInfo('🎯 Capturing handshakes for ' + bssid + '. File: ' + data.capture_file);
|
||||||
setWifiRunning(true);
|
setWifiRunning(true);
|
||||||
|
// Update handshake indicator to show active capture
|
||||||
|
const hsSpan = document.getElementById('handshakeCount');
|
||||||
|
hsSpan.style.animation = 'pulse 1s infinite';
|
||||||
|
hsSpan.title = 'Capturing: ' + bssid;
|
||||||
} else {
|
} else {
|
||||||
alert('Error: ' + data.message);
|
alert('Error: ' + data.message);
|
||||||
}
|
}
|
||||||
@@ -5113,6 +5303,8 @@ def parse_airodump_csv(csv_path):
|
|||||||
if len(parts) >= 6:
|
if len(parts) >= 6:
|
||||||
station = parts[0]
|
station = parts[0]
|
||||||
if station and ':' in station:
|
if station and ':' in station:
|
||||||
|
# Lookup vendor from OUI database
|
||||||
|
vendor = get_manufacturer(station)
|
||||||
clients[station] = {
|
clients[station] = {
|
||||||
'mac': station,
|
'mac': station,
|
||||||
'first_seen': parts[1],
|
'first_seen': parts[1],
|
||||||
@@ -5120,7 +5312,8 @@ def parse_airodump_csv(csv_path):
|
|||||||
'power': parts[3],
|
'power': parts[3],
|
||||||
'packets': parts[4],
|
'packets': parts[4],
|
||||||
'bssid': parts[5],
|
'bssid': parts[5],
|
||||||
'probes': parts[6] if len(parts) > 6 else ''
|
'probes': parts[6] if len(parts) > 6 else '',
|
||||||
|
'vendor': vendor
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[WiFi] Error parsing CSV: {e}")
|
print(f"[WiFi] Error parsing CSV: {e}")
|
||||||
|
|||||||
Reference in New Issue
Block a user