mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
First GSM SPY addition
This commit is contained in:
@@ -274,3 +274,14 @@ MAX_DEAUTH_ALERTS_AGE_SECONDS = 300 # 5 minutes
|
||||
|
||||
# Deauth detector sniff timeout (seconds)
|
||||
DEAUTH_SNIFF_TIMEOUT = 0.5
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# GSM SPY (Cellular Intelligence)
|
||||
# =============================================================================
|
||||
|
||||
# Maximum age for GSM tower/device data in DataStore (seconds)
|
||||
MAX_GSM_AGE_SECONDS = 300 # 5 minutes
|
||||
|
||||
# Timing Advance conversion to meters
|
||||
GSM_TA_METERS_PER_UNIT = 554
|
||||
|
||||
@@ -352,6 +352,134 @@ def init_db() -> None:
|
||||
ON tscm_cases(status, created_at)
|
||||
''')
|
||||
|
||||
# =====================================================================
|
||||
# GSM (Global System for Mobile) Intelligence Tables
|
||||
# =====================================================================
|
||||
|
||||
# gsm_cells - Known cell towers (OpenCellID cache)
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS gsm_cells (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
mcc INTEGER NOT NULL,
|
||||
mnc INTEGER NOT NULL,
|
||||
lac INTEGER NOT NULL,
|
||||
cid INTEGER NOT NULL,
|
||||
lat REAL,
|
||||
lon REAL,
|
||||
azimuth INTEGER,
|
||||
range_meters INTEGER,
|
||||
samples INTEGER,
|
||||
radio TEXT,
|
||||
operator TEXT,
|
||||
first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
metadata TEXT,
|
||||
UNIQUE(mcc, mnc, lac, cid)
|
||||
)
|
||||
''')
|
||||
|
||||
# gsm_rogues - Detected rogue towers / IMSI catchers
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS gsm_rogues (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
arfcn INTEGER NOT NULL,
|
||||
mcc INTEGER,
|
||||
mnc INTEGER,
|
||||
lac INTEGER,
|
||||
cid INTEGER,
|
||||
signal_strength REAL,
|
||||
reason TEXT NOT NULL,
|
||||
threat_level TEXT DEFAULT 'medium',
|
||||
detected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
location_lat REAL,
|
||||
location_lon REAL,
|
||||
acknowledged BOOLEAN DEFAULT 0,
|
||||
notes TEXT,
|
||||
metadata TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
# gsm_signals - 60-day archive of signal observations
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS gsm_signals (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
imsi TEXT,
|
||||
tmsi TEXT,
|
||||
mcc INTEGER,
|
||||
mnc INTEGER,
|
||||
lac INTEGER,
|
||||
cid INTEGER,
|
||||
ta_value INTEGER,
|
||||
signal_strength REAL,
|
||||
arfcn INTEGER,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
metadata TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
# gsm_tmsi_log - 24-hour raw pings for crowd density
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS gsm_tmsi_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
tmsi TEXT NOT NULL,
|
||||
lac INTEGER,
|
||||
cid INTEGER,
|
||||
ta_value INTEGER,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
|
||||
# gsm_velocity_log - 1-hour buffer for movement tracking
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS gsm_velocity_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
device_id TEXT NOT NULL,
|
||||
prev_ta INTEGER,
|
||||
curr_ta INTEGER,
|
||||
prev_cid INTEGER,
|
||||
curr_cid INTEGER,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
estimated_velocity REAL,
|
||||
metadata TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
# GSM indexes for performance
|
||||
conn.execute('''
|
||||
CREATE INDEX IF NOT EXISTS idx_gsm_cells_location
|
||||
ON gsm_cells(lat, lon)
|
||||
''')
|
||||
|
||||
conn.execute('''
|
||||
CREATE INDEX IF NOT EXISTS idx_gsm_cells_identity
|
||||
ON gsm_cells(mcc, mnc, lac, cid)
|
||||
''')
|
||||
|
||||
conn.execute('''
|
||||
CREATE INDEX IF NOT EXISTS idx_gsm_rogues_severity
|
||||
ON gsm_rogues(threat_level, detected_at)
|
||||
''')
|
||||
|
||||
conn.execute('''
|
||||
CREATE INDEX IF NOT EXISTS idx_gsm_signals_cell_time
|
||||
ON gsm_signals(cid, lac, timestamp)
|
||||
''')
|
||||
|
||||
conn.execute('''
|
||||
CREATE INDEX IF NOT EXISTS idx_gsm_signals_device
|
||||
ON gsm_signals(imsi, tmsi, timestamp)
|
||||
''')
|
||||
|
||||
conn.execute('''
|
||||
CREATE INDEX IF NOT EXISTS idx_gsm_tmsi_log_time
|
||||
ON gsm_tmsi_log(timestamp)
|
||||
''')
|
||||
|
||||
conn.execute('''
|
||||
CREATE INDEX IF NOT EXISTS idx_gsm_velocity_log_device
|
||||
ON gsm_velocity_log(device_id, timestamp)
|
||||
''')
|
||||
|
||||
# =====================================================================
|
||||
# DSC (Digital Selective Calling) Tables
|
||||
# =====================================================================
|
||||
@@ -1205,127 +1333,127 @@ def get_all_known_devices(
|
||||
]
|
||||
|
||||
|
||||
def delete_known_device(identifier: str) -> bool:
|
||||
"""Remove a device from the known-good registry."""
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
'DELETE FROM tscm_known_devices WHERE identifier = ?',
|
||||
(identifier.upper(),)
|
||||
)
|
||||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TSCM Schedule Functions
|
||||
# =============================================================================
|
||||
|
||||
def create_tscm_schedule(
|
||||
name: str,
|
||||
cron_expression: str,
|
||||
sweep_type: str = 'standard',
|
||||
baseline_id: int | None = None,
|
||||
zone_name: str | None = None,
|
||||
enabled: bool = True,
|
||||
notify_on_threat: bool = True,
|
||||
notify_email: str | None = None,
|
||||
last_run: str | None = None,
|
||||
next_run: str | None = None,
|
||||
) -> int:
|
||||
"""Create a new TSCM sweep schedule."""
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute('''
|
||||
INSERT INTO tscm_schedules
|
||||
(name, baseline_id, zone_name, cron_expression, sweep_type,
|
||||
enabled, last_run, next_run, notify_on_threat, notify_email)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
name,
|
||||
baseline_id,
|
||||
zone_name,
|
||||
cron_expression,
|
||||
sweep_type,
|
||||
1 if enabled else 0,
|
||||
last_run,
|
||||
next_run,
|
||||
1 if notify_on_threat else 0,
|
||||
notify_email,
|
||||
))
|
||||
return cursor.lastrowid
|
||||
|
||||
|
||||
def get_tscm_schedule(schedule_id: int) -> dict | None:
|
||||
"""Get a TSCM schedule by ID."""
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
'SELECT * FROM tscm_schedules WHERE id = ?',
|
||||
(schedule_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def get_all_tscm_schedules(
|
||||
enabled: bool | None = None,
|
||||
limit: int = 200
|
||||
) -> list[dict]:
|
||||
"""Get all TSCM schedules."""
|
||||
conditions = []
|
||||
params = []
|
||||
|
||||
if enabled is not None:
|
||||
conditions.append('enabled = ?')
|
||||
params.append(1 if enabled else 0)
|
||||
|
||||
where_clause = f'WHERE {" AND ".join(conditions)}' if conditions else ''
|
||||
params.append(limit)
|
||||
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(f'''
|
||||
SELECT * FROM tscm_schedules
|
||||
{where_clause}
|
||||
ORDER BY id DESC
|
||||
LIMIT ?
|
||||
''', params)
|
||||
return [dict(row) for row in cursor]
|
||||
|
||||
|
||||
def update_tscm_schedule(schedule_id: int, **fields) -> bool:
|
||||
"""Update a TSCM schedule."""
|
||||
if not fields:
|
||||
return False
|
||||
|
||||
updates = []
|
||||
params = []
|
||||
|
||||
for key, value in fields.items():
|
||||
updates.append(f'{key} = ?')
|
||||
params.append(value)
|
||||
|
||||
params.append(schedule_id)
|
||||
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
f'UPDATE tscm_schedules SET {", ".join(updates)} WHERE id = ?',
|
||||
params
|
||||
)
|
||||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
def delete_tscm_schedule(schedule_id: int) -> bool:
|
||||
"""Delete a TSCM schedule."""
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
'DELETE FROM tscm_schedules WHERE id = ?',
|
||||
(schedule_id,)
|
||||
)
|
||||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
def is_known_good_device(identifier: str, location: str | None = None) -> dict | None:
|
||||
"""Check if a device is in the known-good registry for a location."""
|
||||
with get_db() as conn:
|
||||
if location:
|
||||
cursor = conn.execute('''
|
||||
def delete_known_device(identifier: str) -> bool:
|
||||
"""Remove a device from the known-good registry."""
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
'DELETE FROM tscm_known_devices WHERE identifier = ?',
|
||||
(identifier.upper(),)
|
||||
)
|
||||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TSCM Schedule Functions
|
||||
# =============================================================================
|
||||
|
||||
def create_tscm_schedule(
|
||||
name: str,
|
||||
cron_expression: str,
|
||||
sweep_type: str = 'standard',
|
||||
baseline_id: int | None = None,
|
||||
zone_name: str | None = None,
|
||||
enabled: bool = True,
|
||||
notify_on_threat: bool = True,
|
||||
notify_email: str | None = None,
|
||||
last_run: str | None = None,
|
||||
next_run: str | None = None,
|
||||
) -> int:
|
||||
"""Create a new TSCM sweep schedule."""
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute('''
|
||||
INSERT INTO tscm_schedules
|
||||
(name, baseline_id, zone_name, cron_expression, sweep_type,
|
||||
enabled, last_run, next_run, notify_on_threat, notify_email)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
name,
|
||||
baseline_id,
|
||||
zone_name,
|
||||
cron_expression,
|
||||
sweep_type,
|
||||
1 if enabled else 0,
|
||||
last_run,
|
||||
next_run,
|
||||
1 if notify_on_threat else 0,
|
||||
notify_email,
|
||||
))
|
||||
return cursor.lastrowid
|
||||
|
||||
|
||||
def get_tscm_schedule(schedule_id: int) -> dict | None:
|
||||
"""Get a TSCM schedule by ID."""
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
'SELECT * FROM tscm_schedules WHERE id = ?',
|
||||
(schedule_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def get_all_tscm_schedules(
|
||||
enabled: bool | None = None,
|
||||
limit: int = 200
|
||||
) -> list[dict]:
|
||||
"""Get all TSCM schedules."""
|
||||
conditions = []
|
||||
params = []
|
||||
|
||||
if enabled is not None:
|
||||
conditions.append('enabled = ?')
|
||||
params.append(1 if enabled else 0)
|
||||
|
||||
where_clause = f'WHERE {" AND ".join(conditions)}' if conditions else ''
|
||||
params.append(limit)
|
||||
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(f'''
|
||||
SELECT * FROM tscm_schedules
|
||||
{where_clause}
|
||||
ORDER BY id DESC
|
||||
LIMIT ?
|
||||
''', params)
|
||||
return [dict(row) for row in cursor]
|
||||
|
||||
|
||||
def update_tscm_schedule(schedule_id: int, **fields) -> bool:
|
||||
"""Update a TSCM schedule."""
|
||||
if not fields:
|
||||
return False
|
||||
|
||||
updates = []
|
||||
params = []
|
||||
|
||||
for key, value in fields.items():
|
||||
updates.append(f'{key} = ?')
|
||||
params.append(value)
|
||||
|
||||
params.append(schedule_id)
|
||||
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
f'UPDATE tscm_schedules SET {", ".join(updates)} WHERE id = ?',
|
||||
params
|
||||
)
|
||||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
def delete_tscm_schedule(schedule_id: int) -> bool:
|
||||
"""Delete a TSCM schedule."""
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
'DELETE FROM tscm_schedules WHERE id = ?',
|
||||
(schedule_id,)
|
||||
)
|
||||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
def is_known_good_device(identifier: str, location: str | None = None) -> dict | None:
|
||||
"""Check if a device is in the known-good registry for a location."""
|
||||
with get_db() as conn:
|
||||
if location:
|
||||
cursor = conn.execute('''
|
||||
SELECT * FROM tscm_known_devices
|
||||
WHERE identifier = ? AND (location = ? OR scope = 'global')
|
||||
''', (identifier.upper(), location))
|
||||
|
||||
@@ -443,6 +443,38 @@ TOOL_DEPENDENCIES = {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'gsm': {
|
||||
'name': 'GSM Intelligence',
|
||||
'tools': {
|
||||
'grgsm_scanner': {
|
||||
'required': True,
|
||||
'description': 'gr-gsm scanner for finding GSM towers',
|
||||
'install': {
|
||||
'apt': 'Build gr-gsm from source: https://github.com/ptrkrysik/gr-gsm',
|
||||
'brew': 'brew install gr-gsm (may require manual build)',
|
||||
'manual': 'https://github.com/ptrkrysik/gr-gsm'
|
||||
}
|
||||
},
|
||||
'grgsm_livemon': {
|
||||
'required': True,
|
||||
'description': 'gr-gsm live monitor for decoding GSM signals',
|
||||
'install': {
|
||||
'apt': 'Included with gr-gsm package',
|
||||
'brew': 'Included with gr-gsm',
|
||||
'manual': 'Included with gr-gsm'
|
||||
}
|
||||
},
|
||||
'tshark': {
|
||||
'required': True,
|
||||
'description': 'Wireshark CLI for parsing GSM packets',
|
||||
'install': {
|
||||
'apt': 'sudo apt-get install tshark',
|
||||
'brew': 'brew install wireshark',
|
||||
'manual': 'https://www.wireshark.org/download.html'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user