Zusammenfassung
Die dritte statische Sicherheitsanalyse des NetMon-Arbeitsstands v1.1.14 durch ein
Multi-Agenten-System (Claude Sonnet 4.6) mit anschließender manueller Verifikation
bestätigte in Runde 3 insgesamt 3 Findings: PATH-Hijacking in
rkhunter_manager.py (HIGH), Pfad-Konfinement im lokalen Agent-Runner
(MEDIUM) und fehlende Launcher-Pfadbegrenzung im Fun-Controller (MEDIUM).
Alle drei Punkte sind im aktuellen Codestand inzwischen geschlossen.
K1 (geschlossen): rkhunter_manager.py begrenzt
WEB_CMD jetzt auf Trusted Prefixes unter /usr/bin/,
/bin/ und /usr/local/bin/. User-kontrollierte
PATH-Treffer wie ~/.local/bin/wget werden verworfen.
K2 (geschlossen): fun_controller.py startet jetzt
nur noch validierte Python-Launcher innerhalb von fun_games_dir.
Der Pfad wird via realpath() und Managed-Path-Check begrenzt.
K3 (geschlossen): agent/runner.py prueft jetzt die
Pfadargumente fuer black, ruff, pytest und
ls zusaetzlich gegen REPO_ROOT. Absolute oder relative
Ziele ausserhalb des Repositories werden abgelehnt.
Methodik
-
1
Reconnaissance — Alle neuen und geänderten Module (25+) gelesen; Fokus auf neue Controller (
rkhunter_manager.py,coordinator.py,links_controller.py,fun_controller.py), neue Services (lang_service.py,process_guard_service.py,repository_guard_service.py) und neue UI-Tabs. -
2
Kandidaten-Analyse — Datenflussketten von externen Quellen (PATH-Umgebung, Dateisystem, LLM-Input) bis zu sensitiven Operationen (
sudo-Calls,subprocess.Popen, Dateioperationen) nachverfolgt. -
3
False-Positive-Filter — Jeder Kandidat in unabhängigen Agenten re-analysiert. Ausschluss: DoS, Single-User-Desktop ohne Privilege-Boundary-Crossing,
stdout/stderr=DEVNULL-Isolation, Browser-Sandbox. Konfidenz-Schwelle: ≥ 8/10. -
4
Manuelle Verifikation — Alle 3 bestätigten Findings durch direktes Lesen der Quelldateien verifiziert: Datenfluss Zeile für Zeile nachvollzogen, Mitigationen geprüft.
Analysierte Module (Runde 3)
| Datei / Modul | Fokus | Ergebnis |
|---|---|---|
| netmon/core/rkhunter_manager.py | rkhunter-Konfigurationsreparatur, shutil.which, sudo install | Finding K1 geschlossen |
| netmon/features/fun_controller.py | Spieleordner-Scan, subprocess.Popen python3 | Finding K2 geschlossen |
| agent/runner.py | Agent-CLI-Allowlist, black/ruff/pytest/ls Pfadrestriktion | Finding K3 geschlossen |
| netmon/core/tooling.py | importlib-Modul-Loading aus sys.path | Ausgeschlossen K4 |
| netmon/services/lang_service.py | lang_code-Pfad-Traversal, json.load | Ausgeschlossen K5 |
| netmon/ui/tabs/cleanup_assistant_tab.py | TOCTOU-Race vor shutil.rmtree | Ausgeschlossen K6 |
| netmon/features/links_controller.py | .desktop-Exec-Parsing, xdg-open | Ausgeschlossen K7 |
| netmon/core/coordinator.py + state.py | Zustandsverwaltung, makedirs | Kein Finding |
| netmon/core/support_bundle.py + diagnostics.py | Dateioperationen, Subprocess | Kein Finding |
| netmon/features/process_guard_controller.py | UI-Zustandsmanagement | Kein Finding |
| netmon/features/repository_guard_controller.py | Snippet-Generierung, shlex.quote | Kein Finding |
| netmon/features/roots_baseline_guard_controller.py | sudo cat/sha256sum mit Pfad-Allowlist | Kein Finding |
| netmon/services/process_guard_service.py | Read-Only-Analyse | Kein Finding |
| netmon/services/repository_guard_service.py | APT-Source-Parsing, flatpak-Subprocess | Kein Finding |
| netmon/services/network_helpers.py | is_safe_trace_target, normalize_external_url | Kein Finding |
| netmon/services/startup_guard_service.py | .desktop/.service SHA256-Lesen | Kein Finding |
| netmon/netmon_security_parser.py + _store.py | In-Memory-Analyse | Kein Finding |
Bestätigte Findings
Bestätigtes Finding: _preferred_web_cmd() konnte
user-kontrollierte PATH-Treffer wie ~/.local/bin/wget ungefiltert
nach WEB_CMD=... in die rkhunter-Konfiguration
uebernehmen. Ein nachfolgendes rkhunter --update haette diese
Binary mit Root-Rechten ausgefuehrt.
Fix: Der aktuelle Codestand begrenzt die Rueckgabe jetzt auf
Trusted Prefixes unter /usr/bin/, /bin/ und
/usr/local/bin/. Untrusted Treffer werden verworfen; falls nur
solche Kandidaten vorhanden sind, bleibt WEB_CMD leer.
Bestätigtes Finding: Der Fun-Launcher konnte Python-Dateien
ueber subprocess.Popen(["python3", game["path"]]) starten,
ohne den Launcher-Pfad selbst gegen den verwalteten Spieleordner zu
begrenzen.
Fix: Der aktuelle Codestand akzeptiert nur noch `.py`-Dateien
innerhalb von fun_games_dir. Der Pfad wird via
realpath() gegen den Spieleordner geprueft und bei verfuegbarem
App-Kontext zusaetzlich mit _is_safe_netmon_managed_path()
abgesichert.
Bestätigtes Finding: Der lokale Agent-Runner liess
absolute oder relative Pfadargumente an black,
ruff, pytest und ls durch,
solange nur der Basisbefehl auf der Allowlist stand.
run_command() pruefte urspruenglich nur parts[0]
gegen die Allowlist. black, ruff, pytest
und ls konnten dadurch absolute oder relative Pfade ausserhalb
von REPO_ROOT erreichen.
REPO_ROOT begrenzt.
Das gilt fuer black, ruff, pytest und
ls, inklusive pytest-Node-IDs wie
tests/test_x.py::test_name.
Ausgeschlossene Kandidaten
load_speedtest_module_from_file() sucht netmon/modules/speedtest.py
via sys.path (enthält "" = CWD). Bei PYTHONPATH-Injektion oder
Ausführung aus einem beschreibbaren Verzeichnis könnte eine böswillige Datei geladen werden.
os.path.join(base_path, f"{lang_code}.json") — ein Angreifer der
settings["language"] auf ../../etc/passwd setzen kann,
könnte beliebige JSON-parsierbare Dateien lesen.
lstat()-Check (Symlink-sicher) und nachfolgendes shutil.rmtree()
sind nicht atomar — ein Symlink könnte dazwischen gesetzt werden.
shutil.rmtree gegen Symlinks
per NotADirectoryError. Race-Window extrem eng; erfordert präzise
Timing-Koordination durch denselben Benutzer. Als Defense-in-Depth-Empfehlung
notiert (siehe Abschnitt 6).
parse_desktop_entry() liest Exec=-Zeilen aus
~/.local/share/applications/*.desktop; Exec-Pfad wird via
subprocess.Popen ausgeführt.
~/.local/share/applications/ ist das Standard-XDG-Verzeichnis
für benutzerinstallierte Anwendungen; das Ausführen von Binaries aus Exec=-Zeilen
ist das erwartete Verhalten von Desktop-Umgebungen. Kein Privilege-Boundary-Crossing —
gleicher Benutzer.
Sicherheitsverbesserungen seit Runde 2
ALLOWED_RADIO_STREAM_SCHEMES = {"http", "https", "rtsp", "rtmp"} in
_normalize_station_stream_url() — verhindert SSRF via mpv/urllib.request.
_safe_makedirs_no_symlink() prüft via os.lstat() vor Verzeichnisanlage
auf Symlinks — wirft RuntimeError.
refactor_safety.py auf Null-Aufrufer verifiziert.
_slot_mountpoint() mit os.path.realpath() + _SAFE_MOUNT_BASES-Allowlist —
verhindert Privilege-Escalation via präpariertes Filesystem.
TRUSTED_WEB_CMD_PREFIXES = ("/usr/bin/", "/bin/", "/usr/local/bin/") in
_preferred_web_cmd() (Zeile 16–26) — verhindert PATH-Hijacking via ~/.local/bin.
black, ruff, pytest und ls
pruefen Pfadargumente jetzt gegen REPO_ROOT.
fun_games_dir
werden gestartet.
wg_profile_path() validiert Interface-Namen via re.fullmatch(r'[A-Za-z0-9_.-]+', ...)
und os.path.commonpath()-Confinement auf /etc/wireguard.
_is_allowed_guard_path(path) mit os.path.realpath()-Normalisierung
gegen ROOTS_GUARD_DEFAULT_AREA_PATHS — verhindert sudo-cat auf beliebige Pfade.
Hardening-Empfehlungen
Die bestätigten Findings aus Runde 3 sind geschlossen. Offen bleiben nur zusätzliche Defense-in-Depth-Maßnahmen ohne unmittelbaren Exploitpfad:
- TOCTOU cleanup_assistant_tab.py:
shutil.rmtreenachlstat()-Check atomar gestalten — z. B.os.open(path, O_NOFOLLOW)vor Löschoperation. - lang_service.py lang_code-Sanitierung:
lang_codemit Regex^[a-z]{2}(_[A-Z]{2})?$validieren bevoros.path.join()aufgerufen wird. - state.py get_netmon_documents_subdir: Jedes
part-Argument aufos.path.basename()normalisieren oder auf../-Freiheit prüfen voros.makedirs(). - WireGuard wg-quick Interface-Name:
wg show interfaces-Output gegenre.fullmatch(r'[A-Za-z0-9_.-]{1,15}', iface)validieren vor Weitergabe ansudo wg-quick down.