Phase 1: setup script and data bootstrapping
- setup.sh: Idempotent bootstrap for OPi Zero 3 / RPi Zero 2W / Debian Platform detection, system packages, Pi optimizations (gpu_mem, swap, sysctl), directory structure, Python venv, bettercap binary install, external tools (Responder, NetExec, Chisel, Ligolo, kerbrute, etc), data database creation, systemd service install, permissions, verification. Supports --reinstall, --jumpbox, --no-tools, --verify-only flags. - scripts/build_data.py: Creates 5 SQLite databases innocuous_macs.db (51 consumer device profiles with OUI/DHCP/TCP fingerprints), oui.db (50 seed OUIs), os_sigs.db (16 p0f-style signatures), dhcp_fingerprints.db (19 option-55 fingerprints), ja3_fingerprints.db (13 hashes) - scripts/build_lkm.sh: LKM rootkit compiler wrapper - scripts/rotate_pcap.sh: tcpdump post-rotation handler with zstd compression, optional AES-256-GCM encryption, disk space monitoring, and auto-purge
This commit is contained in:
Executable
+458
@@ -0,0 +1,458 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build SQLite data databases for BigBrother.
|
||||
|
||||
Creates:
|
||||
- innocuous_macs.db : Consumer device MAC profiles for stealth spoofing
|
||||
- oui.db : IEEE OUI vendor lookup (schema only, populated at first run)
|
||||
- os_sigs.db : Passive OS fingerprint signatures (schema only)
|
||||
- dhcp_fingerprints.db : DHCP option 55 fingerprints (schema only)
|
||||
- ja3_fingerprints.db : JA3 TLS client fingerprints (schema only)
|
||||
|
||||
Called by setup.sh during bootstrap.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def create_innocuous_macs_db(db_path: str) -> None:
|
||||
"""Create innocuous MAC profiles database with ~50 real device profiles.
|
||||
|
||||
Each profile includes OUI, vendor, model, DHCP hostname pattern,
|
||||
DHCP vendor class, TTL, TCP window size, and OS family -- everything
|
||||
needed to impersonate the device at the network fingerprint level.
|
||||
"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
conn.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS profiles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
category TEXT NOT NULL,
|
||||
oui TEXT NOT NULL,
|
||||
vendor TEXT NOT NULL,
|
||||
model TEXT NOT NULL,
|
||||
dhcp_hostname TEXT,
|
||||
dhcp_vendor_class TEXT,
|
||||
ttl INTEGER DEFAULT 64,
|
||||
tcp_window INTEGER DEFAULT 65535,
|
||||
os_family TEXT DEFAULT 'Linux'
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_profiles_category ON profiles(category);
|
||||
CREATE INDEX IF NOT EXISTS idx_profiles_oui ON profiles(oui);
|
||||
""")
|
||||
|
||||
profiles = [
|
||||
# ── Streaming devices ───────────────────────────────────────────
|
||||
("streaming", "FC:65:DE", "Amazon", "Fire TV Stick 4K",
|
||||
"amazon-", "AmazonFireTV", 64, 26883, "Android"),
|
||||
("streaming", "A0:02:DC", "Amazon", "Fire TV Stick Lite",
|
||||
"amazon-", "AmazonFireTV", 64, 26883, "Android"),
|
||||
("streaming", "44:D5:F2", "Amazon", "Fire TV Cube",
|
||||
"amazon-", "AmazonFireTV", 64, 26883, "Android"),
|
||||
("streaming", "D8:31:34", "Roku", "Roku Ultra",
|
||||
"Roku-", "Roku/DVP-12.0", 64, 65535, "Linux"),
|
||||
("streaming", "B0:A7:37", "Roku", "Roku Express",
|
||||
"Roku-", "Roku/DVP-10.0", 64, 65535, "Linux"),
|
||||
("streaming", "54:60:09", "Google", "Chromecast",
|
||||
"Chromecast-", "Google", 64, 65535, "Linux"),
|
||||
("streaming", "F4:F5:D8", "Google", "Chromecast with Google TV",
|
||||
"Chromecast-", "Google", 64, 65535, "Android"),
|
||||
("streaming", "A4:83:E7", "Apple", "Apple TV 4K",
|
||||
"Apple-TV", "AAPLBK", 64, 65535, "tvOS"),
|
||||
("streaming", "40:CB:C0", "Apple", "Apple TV HD",
|
||||
"Apple-TV", "AAPLBK", 64, 65535, "tvOS"),
|
||||
|
||||
# ── Smart TVs ──────────────────────────────────────────────────
|
||||
("smart_tv", "8C:79:F5", "Samsung", "Samsung Smart TV",
|
||||
"Samsung-TV", "Samsung", 128, 65535, "Tizen"),
|
||||
("smart_tv", "BC:14:EF", "Samsung", "Samsung QLED TV",
|
||||
"Samsung-TV", "Samsung", 128, 65535, "Tizen"),
|
||||
("smart_tv", "A8:23:FE", "LG", "LG webOS TV",
|
||||
"LG-TV", "LG", 64, 65535, "webOS"),
|
||||
("smart_tv", "58:FD:B1", "LG", "LG OLED TV",
|
||||
"LG-TV", "LG", 64, 65535, "webOS"),
|
||||
("smart_tv", "D4:38:9C", "Vizio", "Vizio SmartCast TV",
|
||||
"VIZIO-TV", "Vizio", 64, 65535, "Linux"),
|
||||
("smart_tv", "C0:79:82", "TCL", "TCL Roku TV",
|
||||
"TCL-TV", "Roku/DVP", 64, 65535, "Linux"),
|
||||
("smart_tv", "C8:02:10", "Sony", "Sony Bravia Android TV",
|
||||
"Sony-TV", "Sony", 64, 65535, "Android"),
|
||||
|
||||
# ── Smart speakers / assistants ────────────────────────────────
|
||||
("speaker", "48:A6:B8", "Sonos", "Sonos One",
|
||||
"Sonos-", "Sonos", 64, 65535, "Linux"),
|
||||
("speaker", "5C:AA:FD", "Sonos", "Sonos Beam",
|
||||
"Sonos-", "Sonos", 64, 65535, "Linux"),
|
||||
("speaker", "30:FD:38", "Google", "Google Home Mini",
|
||||
"Google-Home-", "Google", 64, 65535, "Linux"),
|
||||
("speaker", "1C:F2:9A", "Google", "Google Nest Hub",
|
||||
"Google-Nest-", "Google", 64, 65535, "Linux"),
|
||||
("speaker", "68:54:FD", "Amazon", "Echo Dot",
|
||||
"amazon-", "AmazonEcho", 64, 26883, "Android"),
|
||||
("speaker", "74:C2:46", "Amazon", "Echo Show",
|
||||
"amazon-", "AmazonEcho", 64, 26883, "Android"),
|
||||
|
||||
# ── Smart home / IoT ──────────────────────────────────────────
|
||||
("iot", "4C:EB:D6", "Amazon", "Ring Doorbell",
|
||||
"Ring-", "Amazon", 64, 65535, "Linux"),
|
||||
("iot", "18:B4:30", "Nest", "Nest Thermostat",
|
||||
"Nest-", "Nest", 64, 65535, "Linux"),
|
||||
("iot", "18:B4:30", "Nest", "Nest Protect",
|
||||
"Nest-", "Nest", 64, 65535, "Linux"),
|
||||
("iot", "00:17:88", "Philips", "Philips Hue Bridge",
|
||||
"Philips-hue", "Philips-hue", 64, 65535, "Linux"),
|
||||
("iot", "D8:0D:17", "TP-Link", "TP-Link Kasa Smart Plug",
|
||||
"TP-Link-", "TP-Link", 64, 65535, "Linux"),
|
||||
("iot", "2C:AA:8E", "Wyze", "Wyze Cam v3",
|
||||
"Wyze-", "Wyze", 64, 65535, "Linux"),
|
||||
("iot", "44:61:32", "Ecobee", "Ecobee Thermostat",
|
||||
"ecobee-", "ecobee", 64, 65535, "Linux"),
|
||||
("iot", "50:14:79", "iRobot", "Roomba i7",
|
||||
"iRobot-", "iRobot", 64, 65535, "Linux"),
|
||||
("iot", "24:E1:24", "iRobot", "Roomba j7",
|
||||
"iRobot-", "iRobot", 64, 65535, "Linux"),
|
||||
("iot", "DC:A6:32", "Raspberry Pi", "IoT Sensor",
|
||||
"raspberrypi", "RaspberryPi", 64, 29200, "Linux"),
|
||||
("iot", "B4:E6:2D", "Raspberry Pi", "IoT Gateway",
|
||||
"raspberrypi", "RaspberryPi", 64, 29200, "Linux"),
|
||||
("iot", "B8:27:EB", "Raspberry Pi", "IoT Controller",
|
||||
"raspberrypi", "RaspberryPi", 64, 29200, "Linux"),
|
||||
|
||||
# ── Printers ──────────────────────────────────────────────────
|
||||
("printer", "3C:D9:2B", "HP", "HP LaserJet Pro",
|
||||
"HP", "Hewlett-Packard", 128, 8192, "HP JetDirect"),
|
||||
("printer", "C8:B5:B7", "HP", "HP OfficeJet Pro",
|
||||
"HP", "Hewlett-Packard", 128, 8192, "HP JetDirect"),
|
||||
("printer", "30:05:5C", "Brother", "Brother HL-L2350DW",
|
||||
"BRN", "Brother", 128, 8192, "Brother"),
|
||||
("printer", "00:80:77", "Brother", "Brother MFC-L2750DW",
|
||||
"BRN", "Brother", 128, 8192, "Brother"),
|
||||
("printer", "00:1B:A9", "Brother", "Brother DCP-L2550DW",
|
||||
"BRN", "Brother", 128, 8192, "Brother"),
|
||||
("printer", "2C:F0:5D", "Epson", "Epson EcoTank ET-4760",
|
||||
"EPSON", "EPSON", 128, 8192, "Epson"),
|
||||
("printer", "64:EB:8C", "Canon", "Canon PIXMA G6020",
|
||||
"Canon", "Canon", 128, 8192, "Canon"),
|
||||
|
||||
# ── Gaming consoles ───────────────────────────────────────────
|
||||
("gaming", "7C:ED:8D", "Microsoft", "Xbox Series X",
|
||||
"Xbox-", "Microsoft", 128, 65535, "Windows"),
|
||||
("gaming", "00:D9:D1", "Sony", "PlayStation 5",
|
||||
"PS5-", "Sony", 64, 65535, "FreeBSD"),
|
||||
("gaming", "98:B6:E9", "Sony", "PlayStation 4",
|
||||
"PS4-", "Sony", 64, 65535, "FreeBSD"),
|
||||
("gaming", "7C:BB:8A", "Nintendo", "Nintendo Switch",
|
||||
"Nintendo-", "Nintendo", 64, 65535, "Nintendo"),
|
||||
|
||||
# ── Network gear (hide as infrastructure) ─────────────────────
|
||||
("network", "B4:FB:E4", "Ubiquiti", "UniFi AP",
|
||||
"UAP-", "Ubiquiti", 64, 65535, "Linux"),
|
||||
("network", "78:8A:20", "Ubiquiti", "USG Gateway",
|
||||
"USG-", "Ubiquiti", 64, 65535, "Linux"),
|
||||
("network", "14:CC:20", "TP-Link", "TP-Link Archer AX50",
|
||||
"TL-", "TP-Link", 64, 65535, "Linux"),
|
||||
("network", "AC:84:C6", "TP-Link", "TP-Link Deco M5",
|
||||
"Deco-", "TP-Link", 64, 65535, "Linux"),
|
||||
("network", "20:A6:CD", "Netgear", "Netgear Orbi",
|
||||
"Orbi-", "Netgear", 64, 65535, "Linux"),
|
||||
("network", "C4:04:15", "Netgear", "Netgear Nighthawk",
|
||||
"NETGEAR-", "Netgear", 64, 65535, "Linux"),
|
||||
]
|
||||
|
||||
conn.executemany("""
|
||||
INSERT INTO profiles (category, oui, vendor, model,
|
||||
dhcp_hostname, dhcp_vendor_class,
|
||||
ttl, tcp_window, os_family)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", profiles)
|
||||
|
||||
conn.commit()
|
||||
count = conn.execute("SELECT COUNT(*) FROM profiles").fetchone()[0]
|
||||
conn.close()
|
||||
print(f" innocuous_macs.db: {count} device profiles")
|
||||
|
||||
|
||||
def create_oui_db(db_path: str) -> None:
|
||||
"""Create OUI database schema (populated from IEEE list at first run)."""
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
conn.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS oui (
|
||||
prefix TEXT PRIMARY KEY,
|
||||
vendor TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_oui_vendor ON oui(vendor);
|
||||
""")
|
||||
|
||||
# Seed with common OUIs used by profiles to ensure basic functionality
|
||||
# before the full IEEE list is downloaded
|
||||
seed_ouis = [
|
||||
("FC:65:DE", "Amazon Technologies Inc."),
|
||||
("A0:02:DC", "Amazon Technologies Inc."),
|
||||
("44:D5:F2", "Amazon Technologies Inc."),
|
||||
("D8:31:34", "Roku, Inc."),
|
||||
("B0:A7:37", "Roku, Inc."),
|
||||
("54:60:09", "Google, Inc."),
|
||||
("F4:F5:D8", "Google, Inc."),
|
||||
("A4:83:E7", "Apple, Inc."),
|
||||
("40:CB:C0", "Apple, Inc."),
|
||||
("8C:79:F5", "Samsung Electronics Co.,Ltd"),
|
||||
("BC:14:EF", "Samsung Electronics Co.,Ltd"),
|
||||
("A8:23:FE", "LG Electronics"),
|
||||
("58:FD:B1", "LG Electronics"),
|
||||
("D4:38:9C", "Vizio, Inc."),
|
||||
("C0:79:82", "TCL King Electrical Appliances"),
|
||||
("C8:02:10", "Sony Interactive Entertainment Inc."),
|
||||
("48:A6:B8", "Sonos, Inc."),
|
||||
("5C:AA:FD", "Sonos, Inc."),
|
||||
("30:FD:38", "Google, Inc."),
|
||||
("1C:F2:9A", "Google, Inc."),
|
||||
("68:54:FD", "Amazon Technologies Inc."),
|
||||
("74:C2:46", "Amazon Technologies Inc."),
|
||||
("4C:EB:D6", "Amazon Technologies Inc."),
|
||||
("18:B4:30", "Nest Labs Inc."),
|
||||
("00:17:88", "Philips Lighting BV"),
|
||||
("D8:0D:17", "TP-Link Technologies Co.,Ltd."),
|
||||
("2C:AA:8E", "Wyze Labs Inc."),
|
||||
("44:61:32", "ecobee inc."),
|
||||
("50:14:79", "iRobot Corporation"),
|
||||
("24:E1:24", "iRobot Corporation"),
|
||||
("DC:A6:32", "Raspberry Pi Trading Ltd"),
|
||||
("B4:E6:2D", "Raspberry Pi Trading Ltd"),
|
||||
("B8:27:EB", "Raspberry Pi Foundation"),
|
||||
("3C:D9:2B", "HP Inc."),
|
||||
("C8:B5:B7", "HP Inc."),
|
||||
("30:05:5C", "Brother Industries, LTD."),
|
||||
("00:80:77", "Brother Industries, LTD."),
|
||||
("00:1B:A9", "Brother Industries, LTD."),
|
||||
("2C:F0:5D", "Seiko Epson Corporation"),
|
||||
("64:EB:8C", "Canon Inc."),
|
||||
("7C:ED:8D", "Microsoft Corporation"),
|
||||
("00:D9:D1", "Sony Interactive Entertainment Inc."),
|
||||
("98:B6:E9", "Sony Interactive Entertainment Inc."),
|
||||
("7C:BB:8A", "Nintendo Co.,Ltd"),
|
||||
("B4:FB:E4", "Ubiquiti Inc"),
|
||||
("78:8A:20", "Ubiquiti Inc"),
|
||||
("14:CC:20", "TP-Link Technologies Co.,Ltd."),
|
||||
("AC:84:C6", "TP-Link Technologies Co.,Ltd."),
|
||||
("20:A6:CD", "NETGEAR"),
|
||||
("C4:04:15", "NETGEAR"),
|
||||
]
|
||||
|
||||
conn.executemany(
|
||||
"INSERT OR IGNORE INTO oui (prefix, vendor) VALUES (?, ?)",
|
||||
seed_ouis
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
count = conn.execute("SELECT COUNT(*) FROM oui").fetchone()[0]
|
||||
conn.close()
|
||||
print(f" oui.db: {count} seed OUI entries (full IEEE list loaded at first run)")
|
||||
|
||||
|
||||
def create_os_sigs_db(db_path: str) -> None:
|
||||
"""Create passive OS fingerprint signature database.
|
||||
|
||||
p0f-style: TCP SYN parameters -> OS identification.
|
||||
"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
conn.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS signatures (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ttl INTEGER NOT NULL,
|
||||
window INTEGER NOT NULL,
|
||||
df INTEGER NOT NULL DEFAULT 1,
|
||||
mss INTEGER DEFAULT 0,
|
||||
os TEXT NOT NULL,
|
||||
version TEXT DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sigs_ttl_window ON signatures(ttl, window);
|
||||
""")
|
||||
|
||||
# Common OS fingerprints (TTL, window, DF, MSS, OS, version)
|
||||
sigs = [
|
||||
# Linux
|
||||
(64, 29200, 1, 1460, "Linux", "3.x-5.x"),
|
||||
(64, 65535, 1, 1460, "Linux", "2.6.x"),
|
||||
(64, 5840, 1, 1460, "Linux", "2.6.x (older)"),
|
||||
(64, 14600, 1, 1460, "Linux", "3.x"),
|
||||
(64, 26883, 1, 1460, "Linux", "Android"),
|
||||
# Windows
|
||||
(128, 65535, 1, 1460, "Windows", "10/11/Server 2016+"),
|
||||
(128, 8192, 1, 1460, "Windows", "7/Server 2008"),
|
||||
(128, 16384, 1, 1460, "Windows", "Vista"),
|
||||
(128, 64240, 1, 1460, "Windows", "10"),
|
||||
# macOS
|
||||
(64, 65535, 1, 1460, "macOS", "10.x-14.x"),
|
||||
# FreeBSD
|
||||
(64, 65535, 1, 1460, "FreeBSD", "12.x-14.x"),
|
||||
# iOS / tvOS
|
||||
(64, 65535, 1, 1460, "iOS", "15.x-17.x"),
|
||||
# Network devices
|
||||
(255, 4128, 1, 536, "Cisco IOS", ""),
|
||||
(64, 16384, 1, 1460, "Juniper JunOS", ""),
|
||||
# Printers
|
||||
(128, 8192, 1, 1460, "HP JetDirect", ""),
|
||||
(128, 8192, 1, 1460, "Brother", ""),
|
||||
]
|
||||
|
||||
conn.executemany("""
|
||||
INSERT INTO signatures (ttl, window, df, mss, os, version)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""", sigs)
|
||||
|
||||
conn.commit()
|
||||
count = conn.execute("SELECT COUNT(*) FROM signatures").fetchone()[0]
|
||||
conn.close()
|
||||
print(f" os_sigs.db: {count} OS fingerprint signatures")
|
||||
|
||||
|
||||
def create_dhcp_fingerprints_db(db_path: str) -> None:
|
||||
"""Create DHCP option 55 fingerprint database.
|
||||
|
||||
Maps DHCP parameter request lists to device/OS identification.
|
||||
Fingerbank-style: the ordered list of requested DHCP options is a
|
||||
surprisingly reliable device identifier.
|
||||
"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
conn.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS fingerprints (
|
||||
fingerprint TEXT PRIMARY KEY,
|
||||
device TEXT NOT NULL,
|
||||
os TEXT DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_fp_device ON fingerprints(device);
|
||||
""")
|
||||
|
||||
# Common DHCP fingerprints (option 55 parameter request lists)
|
||||
fps = [
|
||||
("1,3,6,15,26,28,51,58,59", "Windows", "Windows 10/11"),
|
||||
("1,3,6,15,26,28,51,58,59,43", "Windows", "Windows 10 (domain)"),
|
||||
("1,15,3,6,44,46,47,31,33,121,249,43", "Windows", "Windows 7"),
|
||||
("1,3,6,15,119,252", "macOS", "macOS 10.x-14.x"),
|
||||
("1,121,3,6,15,119,252", "macOS", "macOS 12+"),
|
||||
("1,3,6,15,119,252,95,44,46", "iOS", "iOS 15+"),
|
||||
("1,3,6,28,51,58,59", "Android", "Android 10+"),
|
||||
("1,3,6,15,26,28", "Linux", "Linux (generic)"),
|
||||
("1,28,2,3,15,6,12", "Linux", "Linux (systemd-networkd)"),
|
||||
("1,3,6,12,15,26,28,42", "Roku", "Roku OS"),
|
||||
("1,3,6,12,15,28,42,125", "Amazon", "Fire TV / Echo"),
|
||||
("1,3,6,15,28,33", "Chromecast", "Google Cast"),
|
||||
("1,3,6,12,15,28,42", "Apple TV", "tvOS"),
|
||||
("1,3,6,15,44,46,47", "Sonos", "Sonos"),
|
||||
("1,3,6,12,15,28,42,120,119", "Samsung TV", "Tizen"),
|
||||
("1,3,6,12,15,17,28,40,41,42", "HP Printer", "JetDirect"),
|
||||
("1,3,6,12,15,28,44", "Brother Printer", "Brother"),
|
||||
("1,3,6,12,15", "Philips Hue", "Hue Bridge"),
|
||||
("1,3,6,15,28", "IoT Generic", "Embedded Linux"),
|
||||
]
|
||||
|
||||
conn.executemany(
|
||||
"INSERT OR IGNORE INTO fingerprints (fingerprint, device, os) VALUES (?, ?, ?)",
|
||||
fps
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
count = conn.execute("SELECT COUNT(*) FROM fingerprints").fetchone()[0]
|
||||
conn.close()
|
||||
print(f" dhcp_fingerprints.db: {count} DHCP fingerprints")
|
||||
|
||||
|
||||
def create_ja3_fingerprints_db(db_path: str) -> None:
|
||||
"""Create JA3 TLS client fingerprint database.
|
||||
|
||||
JA3 hashes uniquely identify TLS client implementations.
|
||||
Used by ja3_spoofer to match outbound traffic to common browsers.
|
||||
"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
conn.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS fingerprints (
|
||||
ja3_hash TEXT PRIMARY KEY,
|
||||
client TEXT NOT NULL,
|
||||
version TEXT DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ja3_client ON fingerprints(client);
|
||||
""")
|
||||
|
||||
# Common JA3 hashes -- these rotate with browser versions but
|
||||
# provide a baseline for spoofing. Updated at runtime via
|
||||
# ja3er.com or similar feeds.
|
||||
ja3s = [
|
||||
("cd08e31494f9531f560d64c695473da9", "Chrome", "120+"),
|
||||
("b32309a26951912be7dba376398abc3b", "Chrome", "100-119"),
|
||||
("eb1d94daa7e0344597e756a1fb6e7054", "Firefox", "120+"),
|
||||
("e4bb02b26cf670ba1d2e4942440a1cfc", "Firefox", "100-119"),
|
||||
("773906b0efdefa24a7f2b8eb6985bf37", "Safari", "17+"),
|
||||
("2ce0b4f7f6e119e26091e74e5c635cca", "Safari", "16"),
|
||||
("56c70e24355b14b540300c0c15971b8a", "Edge", "120+"),
|
||||
("555af378b96073da365e42db3052cf7a", "Edge", "100-119"),
|
||||
("3b5074b1b5d032e5620f69f9f700ff0e", "curl", "7.x"),
|
||||
("456523fc94726331a4d5a2e1d40b2cd7", "Python requests", "2.x"),
|
||||
("e7d705a3286e19ea42f587b344ee6865", "Wget", "1.x"),
|
||||
("6734f37431670b3ab4292b8f60f29984", "Java", "11+"),
|
||||
("e35c1eda3a26177f1a3f0aebdc2bd319", "Go", "1.19+"),
|
||||
]
|
||||
|
||||
conn.executemany(
|
||||
"INSERT OR IGNORE INTO fingerprints (ja3_hash, client, version) VALUES (?, ?, ?)",
|
||||
ja3s
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
count = conn.execute("SELECT COUNT(*) FROM fingerprints").fetchone()[0]
|
||||
conn.close()
|
||||
print(f" ja3_fingerprints.db: {count} JA3 fingerprints")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Build BigBrother data databases")
|
||||
parser.add_argument(
|
||||
"--output-dir", "-o",
|
||||
default=os.path.join(os.path.dirname(os.path.dirname(__file__)), "data"),
|
||||
help="Output directory for database files"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--force", "-f",
|
||||
action="store_true",
|
||||
help="Overwrite existing databases"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
output_dir = Path(args.output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print(f"Building data databases in {output_dir}/")
|
||||
|
||||
databases = [
|
||||
("innocuous_macs.db", create_innocuous_macs_db),
|
||||
("oui.db", create_oui_db),
|
||||
("os_sigs.db", create_os_sigs_db),
|
||||
("dhcp_fingerprints.db", create_dhcp_fingerprints_db),
|
||||
("ja3_fingerprints.db", create_ja3_fingerprints_db),
|
||||
]
|
||||
|
||||
for db_name, create_func in databases:
|
||||
db_path = str(output_dir / db_name)
|
||||
if os.path.exists(db_path) and not args.force:
|
||||
print(f" {db_name}: already exists (use --force to rebuild)")
|
||||
continue
|
||||
if os.path.exists(db_path):
|
||||
os.remove(db_path)
|
||||
create_func(db_path)
|
||||
|
||||
print("Done.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the BigBrother LKM rootkit module (Debian host only)
|
||||
# Requires kernel headers: apt install linux-headers-$(uname -r)
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
LKM_DIR="$(cd "$(dirname "$0")/../templates/lkm" && pwd)"
|
||||
|
||||
if [[ ! -d "$LKM_DIR" ]]; then
|
||||
echo -e "${RED}[-]${NC} LKM source directory not found: $LKM_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$LKM_DIR/Makefile" ]]; then
|
||||
echo -e "${RED}[-]${NC} No Makefile found in $LKM_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for kernel headers
|
||||
KVER=$(uname -r)
|
||||
if [[ ! -d "/lib/modules/${KVER}/build" ]]; then
|
||||
echo -e "${RED}[-]${NC} Kernel headers not found for ${KVER}"
|
||||
echo -e "${YELLOW}[!]${NC} Install with: apt install linux-headers-${KVER}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for build tools
|
||||
for tool in make gcc; do
|
||||
if ! command -v "$tool" &>/dev/null; then
|
||||
echo -e "${RED}[-]${NC} Required tool not found: $tool"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN}[+]${NC} Building LKM rootkit for kernel ${KVER}..."
|
||||
cd "$LKM_DIR"
|
||||
make clean 2>/dev/null || true
|
||||
make
|
||||
|
||||
if [[ -f "$LKM_DIR/bb_hide.ko" ]]; then
|
||||
echo -e "${GREEN}[+]${NC} LKM built: $LKM_DIR/bb_hide.ko"
|
||||
echo -e "${GREEN}[+]${NC} Size: $(stat -c '%s' "$LKM_DIR/bb_hide.ko") bytes"
|
||||
echo ""
|
||||
echo -e "${YELLOW}[!]${NC} Load with: insmod $LKM_DIR/bb_hide.ko"
|
||||
echo -e "${YELLOW}[!]${NC} Verify: lsmod | grep bb_hide"
|
||||
echo -e "${YELLOW}[!]${NC} Unload: rmmod bb_hide"
|
||||
else
|
||||
echo -e "${RED}[-]${NC} Build failed -- bb_hide.ko not found"
|
||||
exit 1
|
||||
fi
|
||||
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
# Post-rotation handler for tcpdump -z
|
||||
# Compresses rotated PCAP with zstd, optionally encrypts with AES-256-GCM
|
||||
#
|
||||
# Called by tcpdump like: tcpdump -z /opt/.cache/bb/scripts/rotate_pcap.sh
|
||||
# tcpdump passes the completed PCAP filename as $1
|
||||
#
|
||||
# Environment variables:
|
||||
# ZSTD_LEVEL - Compression level (default: 19, jumpbox: 3)
|
||||
# BB_ENCRYPT - Set to "1" to encrypt after compression
|
||||
# BB_KEY_FILE - Path to encryption key file (required if BB_ENCRYPT=1)
|
||||
# BB_VENV - Path to Python venv (for encryption via crypto module)
|
||||
# BB_MAX_PCAP_PCT - Max disk usage percent before purge (default: 85)
|
||||
set -euo pipefail
|
||||
|
||||
PCAP="$1"
|
||||
ZSTD_LEVEL="${ZSTD_LEVEL:-19}"
|
||||
BB_ENCRYPT="${BB_ENCRYPT:-0}"
|
||||
BB_KEY_FILE="${BB_KEY_FILE:-}"
|
||||
BB_VENV="${BB_VENV:-/opt/.cache/bb/.venv}"
|
||||
BB_MAX_PCAP_PCT="${BB_MAX_PCAP_PCT:-85}"
|
||||
PCAP_DIR="$(dirname "$PCAP")"
|
||||
|
||||
# Logging (silent by default, tcpdump -z doesn't have a tty)
|
||||
log() {
|
||||
logger -t "bb-pcap-rotate" "$*" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Verify input file exists and is non-empty
|
||||
if [[ ! -f "$PCAP" ]]; then
|
||||
log "ERROR: PCAP file not found: $PCAP"
|
||||
exit 0 # Exit 0 so tcpdump doesn't complain
|
||||
fi
|
||||
|
||||
if [[ ! -s "$PCAP" ]]; then
|
||||
log "WARNING: Empty PCAP file, removing: $PCAP"
|
||||
rm -f "$PCAP"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PCAP_SIZE=$(stat -c '%s' "$PCAP" 2>/dev/null || echo 0)
|
||||
log "Rotating PCAP: $PCAP ($PCAP_SIZE bytes, zstd -${ZSTD_LEVEL})"
|
||||
|
||||
# ── Disk space check ───────────────────────────────────────────────────────
|
||||
check_disk_space() {
|
||||
local usage
|
||||
usage=$(df --output=pcent "$PCAP_DIR" 2>/dev/null | tail -1 | tr -d '% ')
|
||||
if [[ -n "$usage" ]] && [[ "$usage" -ge "$BB_MAX_PCAP_PCT" ]]; then
|
||||
log "WARNING: Disk usage ${usage}% >= ${BB_MAX_PCAP_PCT}%, purging oldest PCAPs"
|
||||
# Delete oldest compressed PCAPs first, then oldest raw
|
||||
local oldest
|
||||
oldest=$(find "$PCAP_DIR" -name '*.pcap.zst*' -type f -printf '%T+ %p\n' 2>/dev/null \
|
||||
| sort | head -5 | awk '{print $2}')
|
||||
for f in $oldest; do
|
||||
log "Purging: $f"
|
||||
rm -f "$f"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
check_disk_space
|
||||
|
||||
# ── Compress with zstd ─────────────────────────────────────────────────────
|
||||
COMPRESSED="${PCAP}.zst"
|
||||
|
||||
if command -v zstd &>/dev/null; then
|
||||
zstd "-${ZSTD_LEVEL}" --rm -q "$PCAP" -o "$COMPRESSED" 2>/dev/null
|
||||
if [[ -f "$COMPRESSED" ]]; then
|
||||
COMP_SIZE=$(stat -c '%s' "$COMPRESSED" 2>/dev/null || echo 0)
|
||||
log "Compressed: ${PCAP_SIZE} -> ${COMP_SIZE} bytes (zstd -${ZSTD_LEVEL})"
|
||||
else
|
||||
log "ERROR: zstd compression failed for $PCAP"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
log "ERROR: zstd not found, leaving PCAP uncompressed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Encrypt (optional) ────────────────────────────────────────────────────
|
||||
if [[ "$BB_ENCRYPT" == "1" ]] && [[ -n "$BB_KEY_FILE" ]] && [[ -f "$BB_KEY_FILE" ]]; then
|
||||
ENCRYPTED="${COMPRESSED}.enc"
|
||||
|
||||
if [[ -f "${BB_VENV}/bin/python3" ]]; then
|
||||
"${BB_VENV}/bin/python3" -c "
|
||||
import sys
|
||||
sys.path.insert(0, '$(dirname "$(dirname "$0")")')
|
||||
from utils.crypto import encrypt_file
|
||||
encrypt_file('${COMPRESSED}', '${ENCRYPTED}',
|
||||
open('${BB_KEY_FILE}', 'rb').read().strip())
|
||||
" 2>/dev/null
|
||||
|
||||
if [[ -f "$ENCRYPTED" ]]; then
|
||||
rm -f "$COMPRESSED"
|
||||
ENC_SIZE=$(stat -c '%s' "$ENCRYPTED" 2>/dev/null || echo 0)
|
||||
log "Encrypted: ${COMPRESSED} -> ${ENCRYPTED} (${ENC_SIZE} bytes)"
|
||||
else
|
||||
log "WARNING: Encryption failed, keeping compressed file"
|
||||
fi
|
||||
else
|
||||
log "WARNING: Python venv not found at ${BB_VENV}, skipping encryption"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Restrictive permissions ────────────────────────────────────────────────
|
||||
chmod 600 "${PCAP_DIR}"/*.zst* 2>/dev/null || true
|
||||
|
||||
log "PCAP rotation complete"
|
||||
@@ -0,0 +1,827 @@
|
||||
#!/usr/bin/env bash
|
||||
# BigBrother setup -- Idempotent bootstrap for network surveillance implant
|
||||
# Targets: Orange Pi Zero 3 (primary), RPi Zero 2W (jumpbox), generic Debian
|
||||
set -euo pipefail
|
||||
|
||||
# ── Colors ──────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ── Globals ─────────────────────────────────────────────────────────────────
|
||||
INSTALL_DIR="/opt/.cache/bb"
|
||||
TOOLS_DIR="/opt/tools"
|
||||
VENV_DIR="${INSTALL_DIR}/.venv"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
LOG_FILE="/tmp/bb_setup_$(date +%Y%m%d_%H%M%S).log"
|
||||
|
||||
# Flags
|
||||
FLAG_REINSTALL=0
|
||||
FLAG_JUMPBOX=0
|
||||
FLAG_NO_TOOLS=0
|
||||
FLAG_VERIFY_ONLY=0
|
||||
|
||||
# ── Output helpers ──────────────────────────────────────────────────────────
|
||||
info() { echo -e "${GREEN}[+]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
error() { echo -e "${RED}[-]${NC} $*"; }
|
||||
step() { echo -e "${CYAN}[*]${NC} ${BOLD}$*${NC}"; }
|
||||
debug() { echo -e "${BLUE}[.]${NC} $*"; }
|
||||
|
||||
die() {
|
||||
error "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
log_cmd() {
|
||||
# Run command, log output, show status
|
||||
"$@" >> "$LOG_FILE" 2>&1 || { error "Command failed: $* (see $LOG_FILE)"; return 1; }
|
||||
}
|
||||
|
||||
# ── Argument parsing ────────────────────────────────────────────────────────
|
||||
usage() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --reinstall Force reinstall all components"
|
||||
echo " --jumpbox Minimal install for RPi Zero 2W jumpbox mode"
|
||||
echo " --no-tools Skip GitHub tool installation"
|
||||
echo " --verify-only Check installation without changes"
|
||||
echo " -h, --help Show this help"
|
||||
exit 0
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--reinstall) FLAG_REINSTALL=1; shift ;;
|
||||
--jumpbox) FLAG_JUMPBOX=1; shift ;;
|
||||
--no-tools) FLAG_NO_TOOLS=1; shift ;;
|
||||
--verify-only) FLAG_VERIFY_ONLY=1; shift ;;
|
||||
-h|--help) usage ;;
|
||||
*) die "Unknown option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Root check ──────────────────────────────────────────────────────────────
|
||||
[[ $EUID -eq 0 ]] || die "Must be run as root"
|
||||
|
||||
# ── Platform detection ──────────────────────────────────────────────────────
|
||||
detect_platform() {
|
||||
if [ -f /proc/device-tree/model ]; then
|
||||
local model
|
||||
model=$(tr -d '\0' < /proc/device-tree/model)
|
||||
if echo "$model" | grep -qi "orange pi zero3"; then
|
||||
echo "opi_zero3"
|
||||
elif echo "$model" | grep -qi "orange pi zero 3"; then
|
||||
echo "opi_zero3"
|
||||
elif echo "$model" | grep -qi "raspberry pi zero 2"; then
|
||||
echo "pi_zero"
|
||||
elif echo "$model" | grep -qi "raspberry pi 4"; then
|
||||
echo "pi4"
|
||||
else
|
||||
echo "generic"
|
||||
fi
|
||||
else
|
||||
echo "generic"
|
||||
fi
|
||||
}
|
||||
|
||||
detect_arch() {
|
||||
local arch
|
||||
arch=$(dpkg --print-architecture 2>/dev/null || uname -m)
|
||||
case "$arch" in
|
||||
arm64|aarch64) echo "arm64" ;;
|
||||
amd64|x86_64) echo "amd64" ;;
|
||||
armhf|armv7l) echo "armhf" ;;
|
||||
*) echo "$arch" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
PLATFORM=$(detect_platform)
|
||||
ARCH=$(detect_arch)
|
||||
|
||||
step "Platform: ${PLATFORM} (${ARCH})"
|
||||
echo " Install dir: ${INSTALL_DIR}"
|
||||
echo " Tools dir: ${TOOLS_DIR}"
|
||||
echo " Log file: ${LOG_FILE}"
|
||||
echo ""
|
||||
|
||||
# Force jumpbox mode on Pi Zero
|
||||
if [[ "$PLATFORM" == "pi_zero" ]]; then
|
||||
FLAG_JUMPBOX=1
|
||||
info "Pi Zero 2W detected -- forcing jumpbox mode"
|
||||
fi
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# VERIFY-ONLY MODE
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
if [[ $FLAG_VERIFY_ONLY -eq 1 ]]; then
|
||||
step "Running verification checks..."
|
||||
ERRORS=0
|
||||
|
||||
verify_bin() {
|
||||
if command -v "$1" &>/dev/null; then
|
||||
info " $1 ... found ($(command -v "$1"))"
|
||||
else
|
||||
error " $1 ... MISSING"
|
||||
((ERRORS++))
|
||||
fi
|
||||
}
|
||||
|
||||
verify_file() {
|
||||
if [[ -f "$1" ]]; then
|
||||
info " $1 ... exists"
|
||||
else
|
||||
error " $1 ... MISSING"
|
||||
((ERRORS++))
|
||||
fi
|
||||
}
|
||||
|
||||
verify_dir() {
|
||||
if [[ -d "$1" ]]; then
|
||||
info " $1 ... exists"
|
||||
else
|
||||
error " $1 ... MISSING"
|
||||
((ERRORS++))
|
||||
fi
|
||||
}
|
||||
|
||||
verify_db() {
|
||||
if [[ -f "$1" ]] && sqlite3 "$1" "SELECT 1;" &>/dev/null; then
|
||||
local tables
|
||||
tables=$(sqlite3 "$1" ".tables" 2>/dev/null)
|
||||
info " $1 ... valid (tables: $tables)"
|
||||
else
|
||||
error " $1 ... MISSING or invalid"
|
||||
((ERRORS++))
|
||||
fi
|
||||
}
|
||||
|
||||
echo ""
|
||||
step "System binaries"
|
||||
for bin in python3 tcpdump tshark nmap bettercap macchanger aircrack-ng \
|
||||
zstd cryptsetup tmux jq sqlite3 curl wget git; do
|
||||
verify_bin "$bin"
|
||||
done
|
||||
|
||||
echo ""
|
||||
step "Directory structure"
|
||||
for dir in "${INSTALL_DIR}" "${INSTALL_DIR}/core" "${INSTALL_DIR}/modules" \
|
||||
"${INSTALL_DIR}/utils" "${INSTALL_DIR}/config" "${INSTALL_DIR}/data" \
|
||||
"${INSTALL_DIR}/storage" "${INSTALL_DIR}/services" "${INSTALL_DIR}/scripts"; do
|
||||
verify_dir "$dir"
|
||||
done
|
||||
|
||||
echo ""
|
||||
step "Python venv"
|
||||
verify_dir "${VENV_DIR}"
|
||||
if [[ -f "${VENV_DIR}/bin/python3" ]]; then
|
||||
info " Python venv functional"
|
||||
if "${VENV_DIR}/bin/python3" -c "import scapy, click, rich, yaml, cryptography, requests, dpkt, zstandard, dnslib, jinja2" 2>/dev/null; then
|
||||
info " Core Python imports OK"
|
||||
else
|
||||
error " Some Python imports FAILED"
|
||||
((ERRORS++))
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
step "Data databases"
|
||||
verify_db "${INSTALL_DIR}/data/innocuous_macs.db"
|
||||
verify_db "${INSTALL_DIR}/data/oui.db"
|
||||
verify_db "${INSTALL_DIR}/data/os_sigs.db"
|
||||
verify_db "${INSTALL_DIR}/data/dhcp_fingerprints.db"
|
||||
verify_db "${INSTALL_DIR}/data/ja3_fingerprints.db"
|
||||
|
||||
if [[ $FLAG_NO_TOOLS -eq 0 ]]; then
|
||||
echo ""
|
||||
step "External tools"
|
||||
verify_dir "${TOOLS_DIR}/Responder"
|
||||
verify_dir "${TOOLS_DIR}/enum4linux-ng"
|
||||
verify_dir "${TOOLS_DIR}/smbmap"
|
||||
verify_bin chisel
|
||||
verify_bin ligolo-agent
|
||||
verify_bin kerbrute
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [[ $ERRORS -eq 0 ]]; then
|
||||
info "All verification checks passed"
|
||||
else
|
||||
error "${ERRORS} verification check(s) failed"
|
||||
fi
|
||||
exit $ERRORS
|
||||
fi
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 1. SYSTEM PACKAGES
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
step "Installing system packages..."
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Core package list
|
||||
PACKAGES=(
|
||||
build-essential python3-dev python3-venv python3-pip python3-setuptools
|
||||
libpcap-dev libssl-dev libffi-dev libsqlite3-dev
|
||||
tcpdump tshark nmap masscan
|
||||
net-tools iproute2 bridge-utils wireless-tools wpasupplicant
|
||||
aircrack-ng macchanger
|
||||
iptables nftables ebtables arptables
|
||||
cryptsetup
|
||||
zstd
|
||||
tmux screen
|
||||
curl wget git jq sqlite3
|
||||
dnsutils hostapd dnsmasq
|
||||
wireguard-tools
|
||||
autossh socat stunnel4
|
||||
iodine
|
||||
ppp usb-modeswitch
|
||||
)
|
||||
|
||||
# Only install bluetooth packages on platforms that support it
|
||||
if [[ "$PLATFORM" != "pi_zero" ]]; then
|
||||
PACKAGES+=(bluez libbluetooth-dev)
|
||||
fi
|
||||
|
||||
# Check if proxychains is available (package name varies)
|
||||
if apt-cache show proxychains4 &>/dev/null 2>&1; then
|
||||
PACKAGES+=(proxychains4)
|
||||
elif apt-cache show proxychains-ng &>/dev/null 2>&1; then
|
||||
PACKAGES+=(proxychains-ng)
|
||||
fi
|
||||
|
||||
# Check if sshuttle is available as system package
|
||||
if apt-cache show sshuttle &>/dev/null 2>&1; then
|
||||
PACKAGES+=(sshuttle)
|
||||
fi
|
||||
|
||||
info "Updating package cache..."
|
||||
log_cmd apt-get update -qq
|
||||
|
||||
info "Installing ${#PACKAGES[@]} packages..."
|
||||
log_cmd apt-get install -y -qq "${PACKAGES[@]}"
|
||||
|
||||
info "System packages installed"
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 2. PI-SPECIFIC OPTIMIZATIONS
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
if [[ "$PLATFORM" == "opi_zero3" || "$PLATFORM" == "pi_zero" || "$PLATFORM" == "pi4" ]]; then
|
||||
step "Applying Pi/SBC optimizations..."
|
||||
|
||||
# RPi-specific: reduce GPU memory
|
||||
if [[ "$PLATFORM" == "pi_zero" || "$PLATFORM" == "pi4" ]]; then
|
||||
if [[ -f /boot/config.txt ]]; then
|
||||
if ! grep -q "^gpu_mem=16" /boot/config.txt; then
|
||||
echo "gpu_mem=16" >> /boot/config.txt
|
||||
info "Set gpu_mem=16 in /boot/config.txt"
|
||||
fi
|
||||
elif [[ -f /boot/firmware/config.txt ]]; then
|
||||
if ! grep -q "^gpu_mem=16" /boot/firmware/config.txt; then
|
||||
echo "gpu_mem=16" >> /boot/firmware/config.txt
|
||||
info "Set gpu_mem=16 in /boot/firmware/config.txt"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Disable unnecessary services
|
||||
for svc in bluetooth avahi-daemon triggerhappy ModemManager; do
|
||||
if systemctl is-enabled "$svc" &>/dev/null 2>&1; then
|
||||
systemctl disable --now "$svc" 2>/dev/null || true
|
||||
info "Disabled $svc"
|
||||
fi
|
||||
done
|
||||
|
||||
# Disable swap
|
||||
swapoff -a 2>/dev/null || true
|
||||
systemctl disable dphys-swapfile 2>/dev/null || true
|
||||
systemctl disable zram-config 2>/dev/null || true
|
||||
# Remove swap entries from fstab
|
||||
if grep -q "swap" /etc/fstab 2>/dev/null; then
|
||||
sed -i '/swap/s/^/#/' /etc/fstab
|
||||
info "Commented out swap entries in /etc/fstab"
|
||||
fi
|
||||
info "Swap disabled"
|
||||
|
||||
# Kernel parameters
|
||||
SYSCTL_CONF="/etc/sysctl.d/99-bigbrother.conf"
|
||||
cat > "$SYSCTL_CONF" << 'SYSCTL'
|
||||
# BigBrother network parameters
|
||||
net.ipv4.ip_forward=1
|
||||
net.ipv6.conf.all.forwarding=1
|
||||
net.ipv4.conf.all.send_redirects=0
|
||||
net.ipv4.conf.default.send_redirects=0
|
||||
net.ipv6.conf.all.disable_ipv6=1
|
||||
net.ipv6.conf.default.disable_ipv6=1
|
||||
|
||||
# Performance tuning for packet capture
|
||||
net.core.rmem_max=16777216
|
||||
net.core.rmem_default=1048576
|
||||
net.core.netdev_max_backlog=5000
|
||||
SYSCTL
|
||||
sysctl --system >> "$LOG_FILE" 2>&1 || true
|
||||
info "Kernel parameters applied"
|
||||
fi
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 3. DIRECTORY STRUCTURE
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
step "Creating directory structure..."
|
||||
|
||||
mkdir -p "${INSTALL_DIR}"/{core,modules/{passive,active,stealth,intel,connectivity},utils}
|
||||
mkdir -p "${INSTALL_DIR}"/config/{caplets,mitmproxy_addons,templates}
|
||||
mkdir -p "${INSTALL_DIR}"/data/{bpf_filters,wordlists,ids_rules}
|
||||
mkdir -p "${INSTALL_DIR}"/storage/{pcaps,logs,baseline,intel,credentials,config,creds,dns_logs,sni_logs,audit,tool_logs}
|
||||
mkdir -p "${INSTALL_DIR}"/{services,scripts/operator,templates/{captive_portals,dns_zones,hostapd,responder,lkm},tests}
|
||||
|
||||
chmod 700 "${INSTALL_DIR}/storage"
|
||||
chmod 700 "${INSTALL_DIR}/storage"/*
|
||||
|
||||
# Tools directory
|
||||
mkdir -p "${TOOLS_DIR}"
|
||||
|
||||
info "Directory structure created"
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 4. PYTHON VIRTUAL ENVIRONMENT
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
step "Setting up Python virtual environment..."
|
||||
|
||||
if [[ ! -d "${VENV_DIR}" ]] || [[ $FLAG_REINSTALL -eq 1 ]]; then
|
||||
rm -rf "${VENV_DIR}"
|
||||
python3 -m venv "${VENV_DIR}"
|
||||
info "Created venv at ${VENV_DIR}"
|
||||
else
|
||||
info "Venv already exists at ${VENV_DIR}"
|
||||
fi
|
||||
|
||||
# Upgrade pip
|
||||
"${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel >> "$LOG_FILE" 2>&1
|
||||
|
||||
# Install requirements from source tree if available, else from install dir
|
||||
REQ_FILE=""
|
||||
if [[ -f "${SCRIPT_DIR}/requirements.txt" ]]; then
|
||||
REQ_FILE="${SCRIPT_DIR}/requirements.txt"
|
||||
elif [[ -f "${INSTALL_DIR}/requirements.txt" ]]; then
|
||||
REQ_FILE="${INSTALL_DIR}/requirements.txt"
|
||||
fi
|
||||
|
||||
if [[ -n "$REQ_FILE" ]]; then
|
||||
info "Installing Python requirements from ${REQ_FILE}..."
|
||||
"${VENV_DIR}/bin/pip" install -r "$REQ_FILE" >> "$LOG_FILE" 2>&1
|
||||
fi
|
||||
|
||||
# Additional pip packages (tools installed into venv)
|
||||
VENV_PIP_PACKAGES=(
|
||||
impacket
|
||||
certipy-ad
|
||||
bloodhound
|
||||
pyjwt
|
||||
graphviz
|
||||
numpy
|
||||
)
|
||||
|
||||
# Jumpbox mode: skip heavy packages
|
||||
if [[ $FLAG_JUMPBOX -eq 0 ]]; then
|
||||
VENV_PIP_PACKAGES+=(
|
||||
mitmproxy
|
||||
mitm6
|
||||
coercer
|
||||
pypykatz
|
||||
ldapdomaindump
|
||||
)
|
||||
fi
|
||||
|
||||
info "Installing additional Python packages..."
|
||||
for pkg in "${VENV_PIP_PACKAGES[@]}"; do
|
||||
if "${VENV_DIR}/bin/pip" show "$pkg" &>/dev/null && [[ $FLAG_REINSTALL -eq 0 ]]; then
|
||||
debug " $pkg already installed"
|
||||
else
|
||||
info " Installing $pkg..."
|
||||
"${VENV_DIR}/bin/pip" install "$pkg" >> "$LOG_FILE" 2>&1 || warn " Failed to install $pkg (non-fatal)"
|
||||
fi
|
||||
done
|
||||
|
||||
info "Python environment ready"
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 5. INSTALL BETTERCAP
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
step "Installing bettercap..."
|
||||
|
||||
install_bettercap() {
|
||||
if command -v bettercap &>/dev/null && [[ $FLAG_REINSTALL -eq 0 ]]; then
|
||||
info "bettercap already installed: $(bettercap -version 2>/dev/null || echo 'unknown version')"
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "Downloading bettercap binary..."
|
||||
|
||||
local bc_arch
|
||||
case "$ARCH" in
|
||||
arm64) bc_arch="arm64" ;;
|
||||
amd64) bc_arch="amd64" ;;
|
||||
armhf) bc_arch="armhf" ;;
|
||||
*) warn "Unknown arch $ARCH for bettercap, trying amd64"; bc_arch="amd64" ;;
|
||||
esac
|
||||
|
||||
# Get latest release URL from GitHub
|
||||
local release_url
|
||||
release_url=$(curl -fsSL "https://api.github.com/repos/bettercap/bettercap/releases/latest" \
|
||||
| jq -r ".assets[] | select(.name | test(\"linux_${bc_arch}\")) | .browser_download_url" \
|
||||
| head -1)
|
||||
|
||||
if [[ -z "$release_url" || "$release_url" == "null" ]]; then
|
||||
# Fallback: try to install from apt
|
||||
if apt-cache show bettercap &>/dev/null 2>&1; then
|
||||
log_cmd apt-get install -y -qq bettercap
|
||||
info "bettercap installed from apt"
|
||||
return 0
|
||||
fi
|
||||
warn "Could not find bettercap release for ${bc_arch} -- manual install required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local tmp_dir
|
||||
tmp_dir=$(mktemp -d)
|
||||
trap "rm -rf '$tmp_dir'" RETURN
|
||||
|
||||
curl -fsSL "$release_url" -o "${tmp_dir}/bettercap.zip"
|
||||
|
||||
# Extract
|
||||
if command -v unzip &>/dev/null; then
|
||||
unzip -o "${tmp_dir}/bettercap.zip" -d "${tmp_dir}/bc" >> "$LOG_FILE" 2>&1
|
||||
else
|
||||
apt-get install -y -qq unzip >> "$LOG_FILE" 2>&1
|
||||
unzip -o "${tmp_dir}/bettercap.zip" -d "${tmp_dir}/bc" >> "$LOG_FILE" 2>&1
|
||||
fi
|
||||
|
||||
# Find and install the binary
|
||||
local bc_bin
|
||||
bc_bin=$(find "${tmp_dir}/bc" -name "bettercap" -type f | head -1)
|
||||
if [[ -z "$bc_bin" ]]; then
|
||||
warn "Could not find bettercap binary in archive"
|
||||
return 1
|
||||
fi
|
||||
|
||||
cp "$bc_bin" /usr/local/bin/bettercap
|
||||
chmod 755 /usr/local/bin/bettercap
|
||||
setcap cap_net_raw,cap_net_admin=eip /usr/local/bin/bettercap 2>/dev/null || true
|
||||
|
||||
info "bettercap installed to /usr/local/bin/bettercap"
|
||||
trap - RETURN
|
||||
rm -rf "$tmp_dir"
|
||||
}
|
||||
|
||||
if [[ $FLAG_JUMPBOX -eq 0 ]]; then
|
||||
install_bettercap || warn "bettercap installation failed (non-fatal)"
|
||||
else
|
||||
info "Skipping bettercap (jumpbox mode)"
|
||||
fi
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 6. INSTALL TOOLS FROM GITHUB
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
if [[ $FLAG_NO_TOOLS -eq 0 ]]; then
|
||||
step "Installing external tools..."
|
||||
|
||||
clone_or_pull() {
|
||||
local repo_url="$1"
|
||||
local dest="$2"
|
||||
local name
|
||||
name=$(basename "$dest")
|
||||
|
||||
if [[ -d "$dest/.git" ]] && [[ $FLAG_REINSTALL -eq 0 ]]; then
|
||||
debug " $name already cloned, pulling updates..."
|
||||
git -C "$dest" pull --ff-only >> "$LOG_FILE" 2>&1 || true
|
||||
else
|
||||
rm -rf "$dest"
|
||||
info " Cloning $name..."
|
||||
git clone --depth 1 "$repo_url" "$dest" >> "$LOG_FILE" 2>&1 || {
|
||||
warn " Failed to clone $name"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
download_release_binary() {
|
||||
local repo="$1" # e.g. "jpillora/chisel"
|
||||
local bin_name="$2" # e.g. "chisel"
|
||||
local dest="$3" # e.g. "/usr/local/bin/chisel"
|
||||
local arch_pattern="$4" # e.g. "linux_arm64"
|
||||
|
||||
if [[ -f "$dest" ]] && [[ $FLAG_REINSTALL -eq 0 ]]; then
|
||||
debug " $bin_name already installed at $dest"
|
||||
return 0
|
||||
fi
|
||||
|
||||
info " Downloading $bin_name..."
|
||||
|
||||
local release_url
|
||||
release_url=$(curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" \
|
||||
| jq -r ".assets[] | select(.name | test(\"${arch_pattern}\")) | .browser_download_url" \
|
||||
| head -1)
|
||||
|
||||
if [[ -z "$release_url" || "$release_url" == "null" ]]; then
|
||||
warn " Could not find release for $bin_name (${arch_pattern})"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local tmp_file
|
||||
tmp_file=$(mktemp)
|
||||
|
||||
curl -fsSL "$release_url" -o "$tmp_file"
|
||||
|
||||
# Detect archive type and extract
|
||||
local file_type
|
||||
file_type=$(file -b "$tmp_file")
|
||||
|
||||
if echo "$file_type" | grep -qi "gzip"; then
|
||||
if echo "$release_url" | grep -qi "\.tar\.gz"; then
|
||||
local tmp_dir
|
||||
tmp_dir=$(mktemp -d)
|
||||
tar xzf "$tmp_file" -C "$tmp_dir" 2>/dev/null
|
||||
local found_bin
|
||||
found_bin=$(find "$tmp_dir" -name "$bin_name" -type f | head -1)
|
||||
if [[ -n "$found_bin" ]]; then
|
||||
cp "$found_bin" "$dest"
|
||||
else
|
||||
# Try the first executable file
|
||||
found_bin=$(find "$tmp_dir" -type f -executable | head -1)
|
||||
if [[ -n "$found_bin" ]]; then
|
||||
cp "$found_bin" "$dest"
|
||||
else
|
||||
warn " Could not find $bin_name in archive"
|
||||
rm -rf "$tmp_dir" "$tmp_file"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
rm -rf "$tmp_dir"
|
||||
else
|
||||
gunzip -c "$tmp_file" > "$dest" 2>/dev/null || cp "$tmp_file" "$dest"
|
||||
fi
|
||||
elif echo "$file_type" | grep -qi "zip"; then
|
||||
local tmp_dir
|
||||
tmp_dir=$(mktemp -d)
|
||||
unzip -o "$tmp_file" -d "$tmp_dir" >> "$LOG_FILE" 2>&1
|
||||
local found_bin
|
||||
found_bin=$(find "$tmp_dir" -name "$bin_name" -type f | head -1)
|
||||
if [[ -n "$found_bin" ]]; then
|
||||
cp "$found_bin" "$dest"
|
||||
fi
|
||||
rm -rf "$tmp_dir"
|
||||
else
|
||||
# Assume raw binary
|
||||
cp "$tmp_file" "$dest"
|
||||
fi
|
||||
|
||||
chmod 755 "$dest"
|
||||
rm -f "$tmp_file"
|
||||
info " $bin_name installed to $dest"
|
||||
}
|
||||
|
||||
# Determine arch pattern for downloads
|
||||
case "$ARCH" in
|
||||
arm64) DL_ARCH="linux.*arm64\|linux.*aarch64" ;;
|
||||
amd64) DL_ARCH="linux.*amd64\|linux.*x86_64" ;;
|
||||
armhf) DL_ARCH="linux.*arm\|linux.*armv" ;;
|
||||
*) DL_ARCH="linux.*amd64" ;;
|
||||
esac
|
||||
|
||||
# ── Git clone tools ─────────────────────────────────────────────────
|
||||
clone_or_pull "https://github.com/lgandx/Responder" "${TOOLS_DIR}/Responder"
|
||||
clone_or_pull "https://github.com/cddmp/enum4linux-ng" "${TOOLS_DIR}/enum4linux-ng"
|
||||
clone_or_pull "https://github.com/ShawnDEvans/smbmap" "${TOOLS_DIR}/smbmap"
|
||||
|
||||
# ── Binary release tools ────────────────────────────────────────────
|
||||
# Chisel
|
||||
case "$ARCH" in
|
||||
arm64) download_release_binary "jpillora/chisel" "chisel" "/usr/local/bin/chisel" "linux_arm64" ;;
|
||||
amd64) download_release_binary "jpillora/chisel" "chisel" "/usr/local/bin/chisel" "linux_amd64" ;;
|
||||
*) warn " Chisel: unsupported arch $ARCH" ;;
|
||||
esac
|
||||
|
||||
# Ligolo-ng (agent binary)
|
||||
case "$ARCH" in
|
||||
arm64) download_release_binary "nicocha30/ligolo-ng" "ligolo-agent" "/usr/local/bin/ligolo-agent" "agent.*linux_arm64" ;;
|
||||
amd64) download_release_binary "nicocha30/ligolo-ng" "ligolo-agent" "/usr/local/bin/ligolo-agent" "agent.*linux_amd64" ;;
|
||||
*) warn " Ligolo-ng: unsupported arch $ARCH" ;;
|
||||
esac
|
||||
|
||||
# Kerbrute
|
||||
case "$ARCH" in
|
||||
arm64) download_release_binary "ropnop/kerbrute" "kerbrute" "/usr/local/bin/kerbrute" "linux_arm64" ;;
|
||||
amd64) download_release_binary "ropnop/kerbrute" "kerbrute" "/usr/local/bin/kerbrute" "linux_amd64" ;;
|
||||
*) warn " Kerbrute: unsupported arch $ARCH" ;;
|
||||
esac
|
||||
|
||||
# ── NetExec (pip or git) ────────────────────────────────────────────
|
||||
if ! "${VENV_DIR}/bin/pip" show netexec &>/dev/null || [[ $FLAG_REINSTALL -eq 1 ]]; then
|
||||
info " Installing NetExec via pip..."
|
||||
"${VENV_DIR}/bin/pip" install netexec >> "$LOG_FILE" 2>&1 || {
|
||||
warn " NetExec pip install failed, trying git clone..."
|
||||
clone_or_pull "https://github.com/Pennyw0rth/NetExec" "${TOOLS_DIR}/NetExec"
|
||||
}
|
||||
else
|
||||
debug " NetExec already installed"
|
||||
fi
|
||||
|
||||
# ── Evil-WinRM (gem or skip) ────────────────────────────────────────
|
||||
if command -v gem &>/dev/null; then
|
||||
if ! command -v evil-winrm &>/dev/null || [[ $FLAG_REINSTALL -eq 1 ]]; then
|
||||
info " Installing evil-winrm..."
|
||||
gem install evil-winrm >> "$LOG_FILE" 2>&1 || warn " evil-winrm install failed"
|
||||
fi
|
||||
else
|
||||
debug " Ruby not available, skipping evil-winrm"
|
||||
fi
|
||||
|
||||
# ── OPi Zero 3 / Debian host only tools ─────────────────────────────
|
||||
if [[ $FLAG_JUMPBOX -eq 0 ]]; then
|
||||
# ROADtools (Azure/Entra recon)
|
||||
if ! "${VENV_DIR}/bin/pip" show roadlib &>/dev/null || [[ $FLAG_REINSTALL -eq 1 ]]; then
|
||||
info " Installing ROADtools..."
|
||||
"${VENV_DIR}/bin/pip" install roadlib roadrecon >> "$LOG_FILE" 2>&1 || warn " ROADtools install failed"
|
||||
fi
|
||||
|
||||
# PetitPotam
|
||||
clone_or_pull "https://github.com/topotam/PetitPotam" "${TOOLS_DIR}/PetitPotam"
|
||||
fi
|
||||
|
||||
info "External tools installation complete"
|
||||
else
|
||||
info "Skipping external tool installation (--no-tools)"
|
||||
fi
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 7. CREATE DATA DATABASES
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
step "Building data databases..."
|
||||
|
||||
BUILD_DATA="${SCRIPT_DIR}/scripts/build_data.py"
|
||||
if [[ ! -f "$BUILD_DATA" ]]; then
|
||||
BUILD_DATA="${INSTALL_DIR}/scripts/build_data.py"
|
||||
fi
|
||||
|
||||
if [[ -f "$BUILD_DATA" ]]; then
|
||||
"${VENV_DIR}/bin/python3" "$BUILD_DATA" --output-dir "${INSTALL_DIR}/data" >> "$LOG_FILE" 2>&1
|
||||
info "Data databases created"
|
||||
else
|
||||
warn "build_data.py not found -- databases will need to be created manually"
|
||||
fi
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 8. INSTALL SYSTEMD SERVICES
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
step "Installing systemd services..."
|
||||
|
||||
install_service() {
|
||||
local svc_file="$1"
|
||||
local svc_name
|
||||
svc_name=$(basename "$svc_file")
|
||||
|
||||
if [[ -f "$svc_file" ]]; then
|
||||
cp "$svc_file" "/etc/systemd/system/${svc_name}"
|
||||
info " Installed $svc_name (not enabled)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Look for service files in source tree first, then install dir
|
||||
SVC_DIR="${SCRIPT_DIR}/services"
|
||||
if [[ ! -d "$SVC_DIR" ]]; then
|
||||
SVC_DIR="${INSTALL_DIR}/services"
|
||||
fi
|
||||
|
||||
if [[ -d "$SVC_DIR" ]]; then
|
||||
for svc in "${SVC_DIR}"/*.service "${SVC_DIR}"/*.timer; do
|
||||
[[ -f "$svc" ]] && install_service "$svc"
|
||||
done
|
||||
systemctl daemon-reload
|
||||
info "Systemd services installed (none enabled by default)"
|
||||
else
|
||||
debug "No service files found in $SVC_DIR"
|
||||
fi
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 9. COPY PROJECT FILES
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
step "Deploying project files..."
|
||||
|
||||
if [[ "$SCRIPT_DIR" != "$INSTALL_DIR" ]]; then
|
||||
# Copy from source tree to install directory
|
||||
# rsync preferred for idempotency; fall back to cp
|
||||
if command -v rsync &>/dev/null; then
|
||||
rsync -a --exclude='.git' --exclude='.venv' --exclude='storage/' \
|
||||
--exclude='__pycache__' --exclude='*.pyc' \
|
||||
"${SCRIPT_DIR}/" "${INSTALL_DIR}/"
|
||||
info "Project files synced to ${INSTALL_DIR}"
|
||||
else
|
||||
cp -a "${SCRIPT_DIR}"/*.py "${INSTALL_DIR}/" 2>/dev/null || true
|
||||
for subdir in core modules utils config data services scripts templates tests; do
|
||||
if [[ -d "${SCRIPT_DIR}/${subdir}" ]]; then
|
||||
cp -a "${SCRIPT_DIR}/${subdir}" "${INSTALL_DIR}/"
|
||||
fi
|
||||
done
|
||||
info "Project files copied to ${INSTALL_DIR}"
|
||||
fi
|
||||
else
|
||||
info "Already running from install directory"
|
||||
fi
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 10. SET PERMISSIONS
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
step "Setting permissions..."
|
||||
|
||||
# Storage: restrictive
|
||||
chmod 700 "${INSTALL_DIR}/storage"
|
||||
find "${INSTALL_DIR}/storage" -mindepth 1 -type d -exec chmod 700 {} \; 2>/dev/null || true
|
||||
|
||||
# Config files: restrictive
|
||||
find "${INSTALL_DIR}/config" -type f -exec chmod 600 {} \; 2>/dev/null || true
|
||||
|
||||
# Scripts: executable
|
||||
find "${INSTALL_DIR}/scripts" -name "*.sh" -exec chmod 755 {} \; 2>/dev/null || true
|
||||
find "${INSTALL_DIR}/scripts" -name "*.py" -exec chmod 755 {} \; 2>/dev/null || true
|
||||
|
||||
# Python source: read-only (owner rw)
|
||||
find "${INSTALL_DIR}" -name "*.py" -not -path "*/.venv/*" -exec chmod 644 {} \; 2>/dev/null || true
|
||||
|
||||
# Main CLI executable
|
||||
[[ -f "${INSTALL_DIR}/bigbrother.py" ]] && chmod 755 "${INSTALL_DIR}/bigbrother.py"
|
||||
|
||||
# Installed binaries
|
||||
for bin in /usr/local/bin/bettercap /usr/local/bin/chisel /usr/local/bin/ligolo-agent /usr/local/bin/kerbrute; do
|
||||
[[ -f "$bin" ]] && chmod 755 "$bin"
|
||||
done
|
||||
|
||||
info "Permissions set"
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# 11. FINAL VERIFICATION
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
step "Running final verification..."
|
||||
|
||||
VERIFY_PASS=0
|
||||
VERIFY_FAIL=0
|
||||
|
||||
check() {
|
||||
if "$@" &>/dev/null 2>&1; then
|
||||
((VERIFY_PASS++))
|
||||
else
|
||||
warn " FAIL: $*"
|
||||
((VERIFY_FAIL++))
|
||||
fi
|
||||
}
|
||||
|
||||
# Core binaries
|
||||
check command -v python3
|
||||
check command -v tcpdump
|
||||
check command -v nmap
|
||||
check command -v sqlite3
|
||||
check command -v macchanger
|
||||
check command -v zstd
|
||||
check command -v tmux
|
||||
|
||||
# Python venv
|
||||
check test -f "${VENV_DIR}/bin/python3"
|
||||
check "${VENV_DIR}/bin/python3" -c "import scapy"
|
||||
check "${VENV_DIR}/bin/python3" -c "import click"
|
||||
check "${VENV_DIR}/bin/python3" -c "import rich"
|
||||
check "${VENV_DIR}/bin/python3" -c "import cryptography"
|
||||
check "${VENV_DIR}/bin/python3" -c "import yaml"
|
||||
|
||||
# Directories
|
||||
check test -d "${INSTALL_DIR}/core"
|
||||
check test -d "${INSTALL_DIR}/modules"
|
||||
check test -d "${INSTALL_DIR}/storage"
|
||||
check test -d "${INSTALL_DIR}/data"
|
||||
|
||||
# Databases
|
||||
for db in innocuous_macs.db oui.db os_sigs.db dhcp_fingerprints.db ja3_fingerprints.db; do
|
||||
check test -f "${INSTALL_DIR}/data/${db}"
|
||||
done
|
||||
|
||||
echo ""
|
||||
info "Verification: ${VERIFY_PASS} passed, ${VERIFY_FAIL} failed"
|
||||
|
||||
if [[ $VERIFY_FAIL -gt 0 ]]; then
|
||||
warn "Some checks failed -- review $LOG_FILE for details"
|
||||
else
|
||||
info "Setup complete"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
step "BigBrother deployment summary"
|
||||
echo " Platform: ${PLATFORM} (${ARCH})"
|
||||
echo " Install dir: ${INSTALL_DIR}"
|
||||
echo " Venv: ${VENV_DIR}"
|
||||
echo " Mode: $(if [[ $FLAG_JUMPBOX -eq 1 ]]; then echo 'jumpbox (minimal)'; else echo 'full'; fi)"
|
||||
echo " Log: ${LOG_FILE}"
|
||||
echo ""
|
||||
info "Run '${INSTALL_DIR}/bigbrother.py' to start"
|
||||
Reference in New Issue
Block a user