Sicherheits-Audit — Juni 2026 — Runde 4
Code-Security-Review
NetMon Roots v1.1.15
Viertes automatisiertes Multi-Agenten-Security-Review auf Basis von
Claude Sonnet 4.6. Schwerpunkt dieser Runde: alle seit Runde 3
geänderten Module — darunter der vollständig überarbeitete
vpn_controller.py, der neue Mountpoint-Guard in
hdd_controller.py, die CWD-Bereinigung in
linux_help_controller.py, das Bug-Fix-Paket aus
lock.py, command.py, dns_control_service.py
und startup_guard_service.py sowie die Aufspaltung des
Security-Monitoring-Controllers in _core.py /
_scanning.py. Insgesamt 40+ geänderte Dateien,
20 davon mit sicherheitsrelevantem Fokus analysiert.
Umfang & Methodik
Die Analyse folgte demselben vierstufigen Prozess wie Runde 3.
Schwerpunkt: alle Module, die seit Runde 3 neu hinzugekommen oder
inhaltlich geändert wurden — insbesondere das vollständig
refaktorierte VPN-Modul, der neue HDD-Mountpoint-Guard, die Bereinigung
des linux_help-Controllers und das zentrale Bug-Fix-Paket aus Commit
363d64f5. Zusätzlich wurden alle aus Runde 3 als
offen gemeldeten Hardening-Empfehlungen auf Umsetzungsstand geprüft.
Phase 1 — Reconnaissance
Lesen aller 40+ geänderten Module; Fokus auf externe
Subprocess-Aufrufe mit Nutzerdaten, sudo-Einbettung,
URL-Handling, Dateioperationen mit unkontrollierten Pfaden,
eval/exec, unsichere Deserialisierung
und Privilegienübergänge.
Phase 2 — Kandidaten-Analyse
Datenflussketten von externen Quellen (WireGuard-Konfig-Dateien,
API-Antworten, Nutzerdialogen, Dateisystem) bis zu sensitiven
Operationen (sudo install, wg-quick up,
subprocess.Popen) vollständig nachverfolgt.
Phase 3 — False-Positive-Filter
K1 in einem unabhängigen Agenten re-analysiert: vollständiger
Quelltext von vpn_controller.py gelesen, exakter Code der
Import-Funktion lokalisiert. Ausschluss: kein Privilege-Boundary-Crossing,
explizite Nutzerinteraktion, bestehende sudo-Rechte.
Phase 4 — R3-Nachverfolgung
Alle 3 Findings aus Runde 3 auf Umsetzungsstand geprüft:
rkhunter_manager.py (K1 R3), fun_controller.py
(K2 R3) und agent/runner.py (K3 R3) —
alle drei im aktuellen Codestand geschlossen bestätigt.
Sicherheitsfokus-Module (Runde 4)
| Datei / Modul | Fokus | Ergebnis |
|---|---|---|
netmon/features/vpn_controller.py |
WireGuard-Import (sudo install), wg-quick up/down,
Interface-Name-Validierung, Filename-Regex, Pfad-Confinement |
K1 → FP |
netmon/features/hdd_controller.py |
Mountpoint-Validierung via realpath() +
_SAFE_MOUNT_BASES, LUKS-Passphrase via stdin,
sudo mount/umount |
Kein Finding |
netmon/features/linux_help_controller.py |
CSV/XLSX-Suchpfade nach CWD-Entfernung, kein Subprocess | Kein Finding |
netmon/core/lock.py |
EPERM-Behandlung bei os.kill(), Instanz-Lock-Datei |
Kein Finding |
netmon/core/command.py |
Generischer run_cmd-Wrapper, shell=False,
sudo -S -v-Passphrase via input= |
Kein Finding |
netmon/services/dns_control_service.py |
nmcli con mod <con> via List-Form,
DNS-Token-Validierung (Falsy-Zero-Fix) |
Kein Finding |
netmon/services/startup_guard_service.py |
SHA-256-Hashing, neue Dateien als “new”, kein Subprocess | Kein Finding |
netmon/features/security_monitoring_controller/_core.py |
Event-System, Baseline-Verwaltung, Shared Helpers, kein Subprocess | Kein Finding |
netmon/features/security_monitoring_controller/_scanning.py |
Geräte-/Integritäts-Scanning, kein Subprocess, Dateihash-Vergleich | Kein Finding |
netmon/features/firewall_controller.py |
UFW-Integration, Port-Regex + ipaddress-Validierung,
shell=False |
Kein Finding |
netmon/features/clamav_controller.py |
ClamAV-Subprocess, _safe_makedirs_no_symlink(),
realpath()-Pfad |
Kein Finding |
netmon/features/radio_controller.py |
mpv/ffmpeg-Subprocess, ALLOWED_RADIO_STREAM_SCHEMES,
URL-Schema-Allowlist |
Kein Finding |
netmon/features/security_audit_controller.py |
Lynis/rkhunter-Aufrufe, Startup-Guard-Baseline-Lesen | Kein Finding |
netmon/features/packet_capture_controller.py |
tshark-Subprocess, Interface-Name aus ip link,
shell=False |
Kein Finding |
netmon/features/dns_controller.py |
DNS-Proxy-Delegation, kein direkter Subprocess | Kein Finding |
netmon/features/chad_controller.py |
KI-Integration, getattr-Fix, kein Subprocess |
Kein Finding |
netmon/features/dot_controller.py |
DNS-Dropin via sudo install, Pfad hardcoded |
Kein Finding |
netmon/services/path_guard_service.py |
PATH-Analyse, read-only, kein Subprocess | Kein Finding |
agent/runner.py |
REPO_ROOT-Fix, Subprocess-Allowlist, Pfad-Confinement (K3 R3 geschlossen) | Kein Finding |
agent/tools.py |
Dateilesen mit relative_to(REPO_ROOT)-Guard |
Kein Finding |
netmon/app.py (Refaktorierung) |
82 Proxy-Methoden entfernt — Angriffsflächenreduktion, keine Regression | Keine Regression |
scripts/refactor_safety.py |
AST-Analyse, Caller-Check, Read-Only-Werkzeug | Kein Finding |
Bewerteter Kandidat (ausgeschlossen)
Ein Muster wurde im ersten Analysedurchgang identifiziert und in einem unabhängigen Filter-Agenten (vollständiger Quelltext gelesen) re-analysiert. Der Kandidat überschritt die Konfidenz-Schwelle von 8/10 nicht.
K1 — WireGuard-Konfig-Import: PostUp/PreDown-Hooks ohne Inhaltsprüfung
Konfidenz 7/10 → 3/10 → FP
Ausgeschlossen
Datei: netmon/features/vpn_controller.py —
import_wg_profiles_from_folder()
Initiale Bewertung (Phase 2): WireGuard-.conf-Dateien werden
per sudo -n install -m 600 <src> /etc/wireguard/<name>.conf
ohne Inhaltskontrolle in das System übernommen. Da wg-quick up
die Direktiven PostUp, PreDown, PostDown
und PreUp als Root-Shell-Befehle ausführt, würde eine
präparierte .conf-Datei bei Profil-Aktivierung beliebigen
Code als root ausführen können. Erstes Filter-Ergebnis: Konfidenz 7/10.
Vorhandene Schutzmaßnahmen:
- Dateiname via
re.fullmatch(r"[A-Za-z0-9_.-]+", name)validiert - Zielpfad via
os.path.commonpath()auf/etc/wireguard/begrenzt wg_profile_path()normalisiert den Pfad zusätzlich viaos.path.realpath()
for src in selected_files:
base = os.path.basename(src)
name = base[:-5] if base.endswith(".conf") else base
if not re.match(r"^[A-Za-z0-9_.-]+$", name): # Dateiname validiert
skipped += 1; continue
target = app._wg_profile_path(name) # Pfad-Confinement
if not target:
skipped += 1; continue
rc_cp, out_cp = run_cmd(
["sudo", "-n", "install", "-m", "600", src, target], timeout=10
)
# Dateiinhalt wird nicht geprueft — PostUp/PreDown-Hooks werden uebernommen
Unabhängige Nachprüfung: Nach vollständigem Lesen von
vpn_controller.py wurde der tatsächliche Angriffsweg bewertet.
filedialog.askopenfilenames()) und wählt
die .conf-Datei bewusst aus. WireGuard-Hook-Direktiven sind ein
dokumentiertes und erwartetes Feature des Protokolls — kein versteckter
Angriffspfad. Ein externer Angreifer müsste entweder bereits
Schreibzugriff auf das Dateisystem besitzen (= volle Kompromittierung)
oder den Nutzer durch Social Engineering zur gezielten Dateiauswahl bringen
(Supply-Chain-Problem, keine Code-Schwachstelle). Die Anwendung setzt bereits
sudo-Rechte voraus (ensure_sudo_session() gate); der Nutzer hat
dieselben Rechte, die wg-quick benötigt.
Konfidenz nach Filterrunde: 3/10 → False Positive.
Sicherheitsverbesserungen seit Runde 3
Die folgenden sicherheitsrelevanten Änderungen wurden seit Audit Runde 3 im Codestand implementiert.
HDD Mountpoint-Validierung — hdd_controller.py — Security Fix
Commit 0431faf8: _slot_mountpoint() kanonisiert
den Pfad jetzt via os.path.realpath() (verhindert
../-Tricks) und prüft anschließend gegen
_SAFE_MOUNT_BASES = ("/mnt/", "/media/", "/run/media/").
Ungültige Mountpoints fallen auf den sicheren Default
/mnt/<device-basename> zurück. Zuvor konnte
ein Angreifer mit Schreibzugriff auf die HDD-Slot-Konfiguration einen
beliebigen Mountpoint (z. B. /etc) setzen, der dann
per sudo mount mit einem präparierten Filesystem
überschrieben werden konnte → Privilege Escalation.
CWD-Entfernung aus CSV-Suchpfaden — linux_help_controller.py
Commit 363d64f5: os.getcwd()-Fallback wurde
aus den CSV-Suchpfaden entfernt. Zuvor konnte ein Angreifer eine manipulierte
linux-befehle.csv in einem world-writable Arbeitsverzeichnis
ablegen und damit Command-Injection-kompatible Inhalte in das Linux-Hilfe-Modul
einschleusen (Command-Injection-Risiko beseitigt).
EPERM-Behandlung in lock.py korrigiert
Commit 363d64f5: os.kill(pid, 0) wirft
PermissionError (EPERM), wenn der Prozess läuft, aber einem
anderen Benutzer gehört. Dieser Fall wurde bisher fälschlich als
veralteter Lock gewertet, wodurch eine zweite Instanz gestartet werden
konnte. Fix: EPERM → Prozess läuft, Lock ist gültig.
REPO_ROOT dynamisch aus __file__ — agent/runner.py
Commit 363d64f5: Hardcoded Pfad
/workspaces/dev-lab (Codespaces-Pfad) durch
Path(__file__).resolve().parent.parent ersetzt.
Verhindert, dass der Agent-Runner außerhalb der Codespaces-Umgebung
alle Befehle mit falschem CWD ausführt (Funktionskorrektheit +
Sicherheitsrelevanz: REPO_ROOT-Confinement war wirkunslos).
DNS-Token-Validierung: Falsy-Zero-Falle — dns_control_service.py
Commit 363d64f5: token.count(".") wurde
zu token.count(".") > 0 geändert. Das alte Muster
wertete den Ausdruck als falsy, wenn genau 0 Punkte vorhanden waren,
und konnte damit numerische IP-Tokens wie 192 (ohne Punkt)
als gültig durchlassen.
Angriffsflächenreduktion: 82 Proxy-Methoden aus app.py entfernt
Commits a279e38c + 920cfc9b:
Insgesamt 101 tote bzw. Proxy-Methoden aus app.py gelöscht.
Alle Methoden wurden vorab per scripts/refactor_safety.py auf
Null-Aufrufer verifiziert (AST-Check + grep). Die God-Object-Reduktion
verkleinert die effektive Angriffsfläche und macht den Kontrollfluß
transparenter.
R3-Findings bestätigt geschlossen — rkhunter, fun_controller, agent/runner
Alle 3 aus Runde 3 bestätigten Findings wurden im aktuellen Codestand
verifiziert: K1 R3 (rkhunter_manager.py
PATH-Hijacking) via TRUSTED_WEB_CMD_PREFIXES geschlossen;
K2 R3 (fun_controller.py Pfad-Confinement)
via realpath()-Check auf fun_games_dir geschlossen;
K3 R3 (agent/runner.py Pfad-Allowlist)
via REPO_ROOT-Confinement für black/ruff/pytest/ls geschlossen.
Hardening-Empfehlungen — alle geschlossen
Sämtliche seit Runde 1 offenen Defense-in-Depth-Empfehlungen wurden im aktuellen Codestand v1.1.15 vollständig umgesetzt und per direktem Quelltext-Check verifiziert.
IP-Validierung für WireGuard-Endpoints — vpn_controller.py:2479
parse_vpn_endpoint() verwendet ipaddress.ip_address(host)
zur strikten Host-Validierung. Nicht-IP-Hostnamen werden übersprungen und via
_warn_non_ip_endpoint_once() protokolliert. Der Host-Teil erreicht
UFW-Kommandos nur als validiertes endpoint_ip.compressed.
(Runde 1, Punkt 2 — geschlossen)
Schema-Validierung homepage-URLs im Radio-Modul — radio_controller.py:106+315
ALLOWED_RADIO_HOMEPAGE_SCHEMES = {"http", "https"}
(Zeile 106). Die Funktion _normalize_station_homepage_url()
(Zeilen 315–326) validiert jede Homepage-URL via
urllib.parse.urlparse() gegen diese Allowlist und wird in
open_selected_station_homepage() (Zeile 5231) aufgerufen
— bevor webbrowser.open() die URL erhält.
(Runde 2, Punkt 4 — geschlossen)
Shell-Quoting in Repository-Guard-Snippets — repository_guard_controller.py:799–802
get_repository_guard_quick_action_snippet() maskiert alle
eingebetteten Werte mit shlex.quote():
quoted_uri, quoted_path, quoted_host
und quoted_keyring_path.
(Runde 1, Punkt 5 — geschlossen)
realpath() im Roots-Baseline-Guard — roots_baseline_guard_controller.py:123
_normalize_guard_path_pattern() verwendet
os.path.realpath(os.path.expanduser(raw)) — Symlinks
werden vollständig aufgelöst, bevor fnmatch-Vergleiche
und Allowlist-Prüfungen greifen.
(Runde 1, Punkt 6 — geschlossen)
Pfadprüfung in image_to_data_url() — ai_online_service.py:35–39
resolved.relative_to(home_dir) stellt sicher, dass der
Bildpfad innerhalb des Benutzer-Homeverzeichnisses liegt. Zusätzlich
wird resolve(strict=True) verwendet (Symlinks aufgelöst,
Existenz sichergestellt) und die Dateiendung gegen eine Allowlist
erlaubter Bildformate geprüft.
(Runde 1, Punkt 3 — geschlossen)
WireGuard-Import: Hook-Hinweis — vpn_controller.py:141+3229+3328+3363
_warn_wireguard_import_hooks() (Zeile 141) scannt jede
ausgewählte .conf-Datei vor dem Import auf
PreUp/PostUp/PreDown/PostDown-Direktiven
und zeigt bei Fund einen expliziten Nutzerhinweis an
(messagebox mit Option zum Abbruch). Die Funktion wird an
drei Import-Einstiegspunkten aufgerufen (Zeilen 3229, 3328, 3363).
(K1-FP Defense-in-Depth — geschlossen)
Geprüfte Sicherheitskategorien
| Kategorie | Geprüft | Ergebnis |
|---|---|---|
| Command Injection (subprocess, shell=True) | Ja | Kein Finding |
| Path Traversal (Dateioperationen mit Nutzereingabe) | Ja | Kein Finding |
| Privilege Escalation (sudo mit unkontrollierten Argumenten) | Ja | Kein Finding |
| Argument Injection (list-form subprocess mit externen Daten) | Ja | Kein Finding |
| Unsichere Deserialisierung (pickle, yaml.load, eval) | Ja | Kein Finding |
| Eval/Exec mit Nutzereingabe | Ja | Kein Finding |
| Hardcoded Credentials / API-Keys | Ja | Kein Finding |
| Sensitive Data Exposure (PII in Logs) | Ja | Kein Finding |
| SSRF via urllib.request / externe URL-Weitergabe | Ja | Kein Finding |
| URL-Schema-Validierung an Media-Subprocessen | Ja | Kein Finding |
| Kryptographische Schwachstellen | Ja | Kein Finding |
| Symlink-Angriffe auf privilegierte Dateioperationen | Ja | Kein Finding |
| Mountpoint-Manipulation via Konfig-Datei | Ja | Kein Finding (Fix in v1.1.15) |
| WireGuard-Hook-Ausführung via Import | Ja | K1 → FP (kein Boundary Crossing) |
| PATH-Hijacking via shutil.which (R3-K1) | Ja | Geschlossen (R3) |
| Agent-CLI Pfad-Confinement (R3-K3) | Ja | Geschlossen (R3) |
Auditor: Claude Sonnet 4.6 (Anthropic) —
Automatisiertes Multi-Agenten-Sicherheits-Review + unabhängige False-Positive-Filterrunde
Projektverantwortlicher: Sven Fröhlich
Datum: 29. Juni 2026
Analysierter Codestand:
e334324a … 920cfc9b
+ Arbeitsstand (modified/untracked), gesamt 40+ Dateien — Runde 4
Methode: Statische Code-Analyse + manuelle Datenfluss-Verifikation
— kein Laufzeit-Penetrationstest
Haftungsausschluss: Dieses Audit ersetzt keinen professionellen
Penetrationstest durch eine zertifizierte Sicherheitsfirma. Die Analyse
beschränkt sich auf den angegebenen Codestand. Nicht erfasst werden:
Laufzeitverhalten, abhängige Systembibliotheken,
Netzwerkkonfiguration und soziale Angriffsvektoren.
Zur Downloadseite • Aktuelles • Dokumentation • Legal / Lizenz