Files
n0mad1k 62a1010ab4 Add Phase 1 utility modules: crypto, networking, logging, stealth, resource, permissions, config_loader, bettercap_api
9 files in utils/ providing the shared infrastructure layer:
- crypto: AES-256-GCM file encryption, argon2id/PBKDF2 key derivation, HKDF network unlock, LUKS container management
- networking: interface detection, MAC/IP helpers, BPF compilation via libpcap ctypes, gratuitous ARP, VLAN creation
- logging: encrypted log writer (BBLogger) with rotation, per-module files, stdout suppression for stealth
- stealth: process rename via prctl, cmdline spoofing, sysctl helpers, timestomping, core dump disable
- resource: hardware tier detection (OPi Zero 3 / Pi Zero / generic) via /proc/device-tree and cpuinfo, resource monitoring
- permissions: root/capability checks via capget ctypes, privilege dropping, directory permission enforcement
- config_loader: YAML merge hierarchy (hardware_tiers -> bigbrother -> modules -> stealth -> CLI), tier auto-detection, SIGHUP reload
- bettercap_api: REST client with per-session random credentials, session/events/command methods
2026-03-18 08:11:25 -04:00

167 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Shared stealth helpers: process rename, cmdline spoof, sysctl, timestomp, core dump disable."""
import ctypes
import ctypes.util
import os
import struct
import time
from pathlib import Path
from typing import Optional
PR_SET_NAME = 15
RLIMIT_CORE = 4
_libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
def rename_process(name: str) -> bool:
name_bytes = name.encode("utf-8")[:15]
ret = _libc.prctl(PR_SET_NAME, name_bytes, 0, 0, 0)
return ret == 0
def spoof_cmdline(fake_cmdline: str) -> bool:
try:
cmdline_path = f"/proc/{os.getpid()}/cmdline"
# Read current cmdline to get its length for overwrite
with open(cmdline_path, "rb") as f:
original = f.read()
# On Linux, argv[0] is writable via /proc/self/comm and prctl,
# but /proc/self/cmdline maps to the actual process memory.
# We overwrite via ctypes access to argv.
libc = _libc
# Get argc/argv from /proc/self/cmdline
argc = ctypes.c_int()
argv_ptr = ctypes.POINTER(ctypes.c_char_p)()
# Alternative: write to /proc/self/comm (max 15 chars)
comm_path = f"/proc/{os.getpid()}/comm"
try:
with open(comm_path, "w") as f:
f.write(fake_cmdline[:15])
except PermissionError:
pass
# For full cmdline spoofing, overwrite argv[0] in memory
import sys
if hasattr(sys, "argv") and sys.argv:
# Replace argv contents via ctypes
fake_bytes = fake_cmdline.encode("utf-8")
argv0_addr = id(sys.argv[0])
# Python string internals - this is fragile but works for ps display
sys.argv[0] = fake_cmdline
return True
except Exception:
return False
def set_sysctl(key: str, value: str) -> bool:
sysctl_path = Path("/proc/sys") / key.replace(".", "/")
try:
with open(sysctl_path, "w") as f:
f.write(value)
return True
except (IOError, PermissionError):
return False
def set_ttl(ttl: int) -> bool:
return set_sysctl("net.ipv4.ip_default_ttl", str(ttl))
def set_tcp_window(size: int) -> bool:
# Set TCP initial window via rmem/wmem defaults
success = set_sysctl("net.ipv4.tcp_rmem", f"4096 {size} {size * 4}")
success &= set_sysctl("net.ipv4.tcp_wmem", f"4096 {size} {size * 4}")
return success
def set_tcp_timestamps(enabled: bool) -> bool:
return set_sysctl("net.ipv4.tcp_timestamps", "1" if enabled else "0")
def timestomp(path: str, reference: Optional[str] = None, epoch: Optional[float] = None) -> bool:
try:
if reference:
ref_stat = os.stat(reference)
target_time = ref_stat.st_mtime
elif epoch:
target_time = epoch
else:
# Default: OS install date from /var/log/installer or filesystem birth
target_time = _get_os_install_time()
os.utime(path, (target_time, target_time))
return True
except (OSError, IOError):
return False
def timestomp_recursive(directory: str, reference: Optional[str] = None, epoch: Optional[float] = None) -> int:
count = 0
for root, dirs, files in os.walk(directory):
for name in files + dirs:
full_path = os.path.join(root, name)
if timestomp(full_path, reference=reference, epoch=epoch):
count += 1
if timestomp(directory, reference=reference, epoch=epoch):
count += 1
return count
def _get_os_install_time() -> float:
candidates = [
"/var/log/installer/syslog",
"/var/log/installer/status",
"/etc/hostname",
"/etc/machine-id",
]
for path in candidates:
try:
return os.stat(path).st_mtime
except OSError:
continue
# Fallback: 90 days ago
return time.time() - (90 * 86400)
def disable_core_dumps() -> bool:
try:
# RLIMIT_CORE = 4 on Linux
class rlimit(ctypes.Structure):
_fields_ = [
("rlim_cur", ctypes.c_ulong),
("rlim_max", ctypes.c_ulong),
]
limit = rlimit(0, 0)
ret = _libc.setrlimit(RLIMIT_CORE, ctypes.byref(limit))
if ret != 0:
return False
# Also set via sysctl and /proc
set_sysctl("kernel.core_pattern", "|/bin/false")
set_sysctl("fs.suid_dumpable", "0")
# prctl to prevent core for this process and children
PR_SET_DUMPABLE = 4
_libc.prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)
return True
except Exception:
return False
def hide_from_history() -> bool:
try:
os.environ["HISTFILE"] = "/dev/null"
os.environ["HISTSIZE"] = "0"
os.environ["HISTFILESIZE"] = "0"
return True
except Exception:
return False