90c26ae24d
- Set HOME/GOPATH/GOMODCACHE before go install kerbrute on arm64 to fix 'module cache not found' abort that prevented --enable-service from running - Enable bigbrother-capture, bigbrother-watchdog, and bettercap.service in --enable-service block (previously only bigbrother-core was enabled) - Add services/bettercap.service: runs passive_recon.cap with net.recon + net.sniff + events.stream instead of idle api.rest only - bettercap.service depends on bigbrother-core to ensure engine is up first Fixes DevTrack #311 and #312
878 lines
34 KiB
Bash
Executable File
878 lines
34 KiB
Bash
Executable File
#!/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
|
|
FLAG_ENABLE_SVC=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 " --enable-service Enable and start bigbrother-core.service after setup"
|
|
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 ;;
|
|
--enable-service) FLAG_ENABLE_SVC=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
|
|
macchanger
|
|
iptables nftables ebtables arptables
|
|
cryptsetup
|
|
zstd
|
|
tmux screen
|
|
curl wget git jq sqlite3 file
|
|
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
|
|
|
|
# aircrack-ng not available on all Ubuntu arm64 mirrors — add only if a real candidate exists
|
|
if apt-cache policy aircrack-ng 2>/dev/null | grep -q "Candidate: [^(none)]"; then
|
|
PACKAGES+=(aircrack-ng)
|
|
else
|
|
warn "aircrack-ng has no install candidate -- skipping (install manually if needed)"
|
|
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 (no arm64 binary — build from source)
|
|
case "$ARCH" in
|
|
arm64)
|
|
export HOME=/root GOPATH=/root/go GOMODCACHE=/root/go/pkg/mod
|
|
mkdir -p /root/go
|
|
if command -v go &>/dev/null; then
|
|
log_cmd go install github.com/ropnop/kerbrute@latest
|
|
cp "${GOPATH}/bin/kerbrute" /usr/local/bin/kerbrute 2>/dev/null || true
|
|
else
|
|
GOTAR=$(mktemp -d)
|
|
GOVERSION="1.22.3"
|
|
curl -fsSL "https://go.dev/dl/go${GOVERSION}.linux-arm64.tar.gz" -o "$GOTAR/go.tar.gz"
|
|
tar -C /usr/local -xzf "$GOTAR/go.tar.gz"
|
|
export PATH=$PATH:/usr/local/go/bin
|
|
log_cmd /usr/local/go/bin/go install github.com/ropnop/kerbrute@latest
|
|
cp "${GOPATH}/bin/kerbrute" /usr/local/bin/kerbrute 2>/dev/null || true
|
|
rm -rf "$GOTAR"
|
|
fi
|
|
;;
|
|
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
|
|
BUILD_DATA_FLAGS="--output-dir ${INSTALL_DIR}/data"
|
|
[[ $FLAG_REINSTALL -eq 1 ]] && BUILD_DATA_FLAGS="$BUILD_DATA_FLAGS --force"
|
|
"${VENV_DIR}/bin/python3" "$BUILD_DATA" $BUILD_DATA_FLAGS >> "$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++)) || true
|
|
else
|
|
warn " FAIL: $*"
|
|
((VERIFY_FAIL++)) || true
|
|
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"
|
|
|
|
# ════════════════════════════════════════════════════════════════════════════
|
|
# 12. ENABLE AND START SERVICE
|
|
# ════════════════════════════════════════════════════════════════════════════
|
|
if [[ $FLAG_ENABLE_SVC -eq 1 ]]; then
|
|
step "Enabling and starting BigBrother services..."
|
|
for svc in bigbrother-core.service bigbrother-capture.service bigbrother-watchdog.service bettercap.service; do
|
|
if systemctl enable "$svc" 2>/dev/null; then
|
|
info " $svc enabled"
|
|
else
|
|
warn " Failed to enable $svc (may not be installed)"
|
|
fi
|
|
done
|
|
systemctl daemon-reload
|
|
if systemctl start bigbrother-core.service 2>/dev/null; then
|
|
info "bigbrother-core.service started"
|
|
sleep 3
|
|
systemctl status bigbrother-core.service --no-pager -l 2>/dev/null || true
|
|
else
|
|
warn "Failed to start bigbrother-core.service — check journalctl -u bigbrother-core"
|
|
fi
|
|
fi
|