mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Update TSCM with improved WiFi scanning, new scoring UI, and tracker detection
WiFi Scanning: - Add 'iw' scan method as primary (sometimes works without root) - Auto-detect wireless interface from /sys/class/net - Better error logging for permission issues - Fall back to iwlist if iw fails UI Updates: - Replace Critical/High/Medium/Low cards with new scoring model - Now shows: High Interest (6+), Needs Review (3-5), Informational (0-2) - Add Correlations count card - Update counts based on device classification scores Tracker Detection: - Add detection for Apple AirTag (by OUI and name) - Add detection for Tile trackers - Add detection for Samsung SmartTag - Add detection for ESP32/ESP8266 devices (Espressif chipset) - Add generic chipset vendor detection - New indicator types with appropriate scoring weights Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
141
routes/tscm.py
141
routes/tscm.py
@@ -647,40 +647,117 @@ def _scan_wifi_networks(interface: str) -> list[dict]:
|
||||
logger.warning(f"macOS WiFi scan failed: {e}")
|
||||
|
||||
else:
|
||||
# Linux: Try iwlist scan
|
||||
iface = interface or 'wlan0'
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['iwlist', iface, 'scan'],
|
||||
capture_output=True, text=True, timeout=30
|
||||
)
|
||||
current_network = {}
|
||||
for line in result.stdout.split('\n'):
|
||||
line = line.strip()
|
||||
if 'Cell' in line and 'Address:' in line:
|
||||
# Linux: Try multiple scan methods
|
||||
import shutil
|
||||
|
||||
# Detect wireless interface if not specified
|
||||
if not interface:
|
||||
try:
|
||||
import glob
|
||||
wireless_paths = glob.glob('/sys/class/net/*/wireless')
|
||||
if wireless_paths:
|
||||
iface = wireless_paths[0].split('/')[4]
|
||||
else:
|
||||
iface = 'wlan0'
|
||||
except Exception:
|
||||
iface = 'wlan0'
|
||||
else:
|
||||
iface = interface
|
||||
|
||||
logger.info(f"WiFi scan using interface: {iface}")
|
||||
|
||||
# Method 1: Try iw scan (sometimes works without root)
|
||||
if shutil.which('iw'):
|
||||
try:
|
||||
logger.info("Trying 'iw' scan...")
|
||||
result = subprocess.run(
|
||||
['iw', 'dev', iface, 'scan'],
|
||||
capture_output=True, text=True, timeout=30
|
||||
)
|
||||
if result.returncode == 0 and 'BSS' in result.stdout:
|
||||
# Parse iw output
|
||||
current_bss = None
|
||||
for line in result.stdout.split('\n'):
|
||||
if line.startswith('BSS '):
|
||||
if current_bss and current_bss.get('bssid'):
|
||||
networks.append(current_bss)
|
||||
# Extract BSSID from "BSS xx:xx:xx:xx:xx:xx(on wlan0)"
|
||||
bssid_match = re.search(r'BSS ([0-9a-fA-F:]{17})', line)
|
||||
if bssid_match:
|
||||
current_bss = {'bssid': bssid_match.group(1).upper(), 'essid': '[Hidden]'}
|
||||
elif current_bss:
|
||||
line = line.strip()
|
||||
if line.startswith('SSID:'):
|
||||
ssid = line[5:].strip()
|
||||
current_bss['essid'] = ssid or '[Hidden]'
|
||||
elif line.startswith('signal:'):
|
||||
sig_match = re.search(r'(-?\d+)', line)
|
||||
if sig_match:
|
||||
current_bss['power'] = sig_match.group(1)
|
||||
elif line.startswith('freq:'):
|
||||
freq = line[5:].strip()
|
||||
# Convert frequency to channel
|
||||
try:
|
||||
freq_mhz = int(freq)
|
||||
if freq_mhz < 3000:
|
||||
channel = (freq_mhz - 2407) // 5
|
||||
else:
|
||||
channel = (freq_mhz - 5000) // 5
|
||||
current_bss['channel'] = str(channel)
|
||||
except ValueError:
|
||||
pass
|
||||
elif 'WPA' in line or 'RSN' in line:
|
||||
current_bss['privacy'] = 'WPA2' if 'RSN' in line else 'WPA'
|
||||
if current_bss and current_bss.get('bssid'):
|
||||
networks.append(current_bss)
|
||||
logger.info(f"iw scan found {len(networks)} networks")
|
||||
elif 'Operation not permitted' in result.stderr or result.returncode != 0:
|
||||
logger.warning(f"iw scan requires root: {result.stderr[:100]}")
|
||||
except (subprocess.TimeoutExpired, subprocess.SubprocessError) as e:
|
||||
logger.warning(f"iw scan failed: {e}")
|
||||
|
||||
# Method 2: Try iwlist scan if iw didn't work
|
||||
if not networks and shutil.which('iwlist'):
|
||||
try:
|
||||
logger.info("Trying 'iwlist' scan...")
|
||||
result = subprocess.run(
|
||||
['iwlist', iface, 'scan'],
|
||||
capture_output=True, text=True, timeout=30
|
||||
)
|
||||
if 'Operation not permitted' in result.stderr:
|
||||
logger.warning("iwlist scan requires root privileges")
|
||||
else:
|
||||
current_network = {}
|
||||
for line in result.stdout.split('\n'):
|
||||
line = line.strip()
|
||||
if 'Cell' in line and 'Address:' in line:
|
||||
if current_network.get('bssid'):
|
||||
networks.append(current_network)
|
||||
bssid = line.split('Address:')[1].strip()
|
||||
current_network = {'bssid': bssid.upper(), 'essid': '[Hidden]'}
|
||||
elif 'ESSID:' in line:
|
||||
essid = line.split('ESSID:')[1].strip().strip('"')
|
||||
current_network['essid'] = essid or '[Hidden]'
|
||||
elif 'Channel:' in line:
|
||||
channel = line.split('Channel:')[1].strip()
|
||||
current_network['channel'] = channel
|
||||
elif 'Signal level=' in line:
|
||||
match = re.search(r'Signal level[=:]?\s*(-?\d+)', line)
|
||||
if match:
|
||||
current_network['power'] = match.group(1)
|
||||
elif 'Encryption key:' in line:
|
||||
encrypted = 'on' in line.lower()
|
||||
current_network['encrypted'] = encrypted
|
||||
elif 'WPA' in line or 'WPA2' in line:
|
||||
current_network['privacy'] = 'WPA2' if 'WPA2' in line else 'WPA'
|
||||
if current_network.get('bssid'):
|
||||
networks.append(current_network)
|
||||
bssid = line.split('Address:')[1].strip()
|
||||
current_network = {'bssid': bssid.upper(), 'essid': '[Hidden]'}
|
||||
elif 'ESSID:' in line:
|
||||
essid = line.split('ESSID:')[1].strip().strip('"')
|
||||
current_network['essid'] = essid or '[Hidden]'
|
||||
elif 'Channel:' in line:
|
||||
channel = line.split('Channel:')[1].strip()
|
||||
current_network['channel'] = channel
|
||||
elif 'Signal level=' in line:
|
||||
match = re.search(r'Signal level[=:]?\s*(-?\d+)', line)
|
||||
if match:
|
||||
current_network['power'] = match.group(1)
|
||||
elif 'Encryption key:' in line:
|
||||
encrypted = 'on' in line.lower()
|
||||
current_network['encrypted'] = encrypted
|
||||
elif 'WPA' in line or 'WPA2' in line:
|
||||
current_network['privacy'] = 'WPA2' if 'WPA2' in line else 'WPA'
|
||||
if current_network.get('bssid'):
|
||||
networks.append(current_network)
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired, subprocess.SubprocessError) as e:
|
||||
logger.warning(f"Linux WiFi scan failed: {e}")
|
||||
logger.info(f"iwlist scan found {len(networks)} networks")
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired, subprocess.SubprocessError) as e:
|
||||
logger.warning(f"iwlist scan failed: {e}")
|
||||
|
||||
if not networks:
|
||||
logger.warning("WiFi scanning requires root privileges. Run with sudo for WiFi scanning.")
|
||||
|
||||
return networks
|
||||
|
||||
|
||||
Reference in New Issue
Block a user