Sicherheits-Audit — Juli 2026 — Runde 9
Code-Security-Review
NetMon Roots v1.1.19 — Release-Stand
Neuntes automatisiertes Security-Review auf Basis von
Claude Fable 5 — erstmals auf dem finalen
Release-Stand v1.1.19 statt auf einem Zwischenstand.
Schwerpunkte dieser Runde: der VPN/UFW-Killswitch-Reconcile,
die DNS-Startup-Recovery, der In-App-Update-Check,
die Debian-Maintainer-Skripte (Install/Deinstall als root)
sowie der komplette Release-Verifikationspfad
(SHA256SUMS + minisign). Anders als in den
Vorrunden wurden diesmal zwei belastbare Schwachstellen
mittleren Schweregrads bestätigt — eine fehlende
Schema-Allowlist beim automatischen Öffnen der Update-URL und ein
Fail-Open des Killswitch in frühen Fehlerpfaden
des VPN-Slot-Wechsels. Beide wurden umgesetzt, mit
Regressionstests abgesichert und verifiziert. Die
Release-Signaturkette (minisign) wurde empirisch geprüft und ist
gültig.
Umfang & Methodik
Geprüft wurde der finale Release-Stand v1.1.19
(constants.py = 1.1.19, deb-control,
latest-version.json und die Download-Seiten stimmen
überein). Schwerpunkt auf den seit den Vorrunden nicht im Fokus
gewesenen, sicherheitskritischen Pfaden: dem VPN/UFW-Killswitch-
Reconcile (vpn_controller.py,
firewall_controller.py), der DNS-Startup-Recovery
(app.py, dns_control_service.py), dem
In-App-Update-Check (_fetch_latest_release_info(),
_open_url_with_xdg_open()), den Debian-Maintainer-Skripten
(postinst/prerm aus der gebauten
.deb) und dem Release-Verifikationspfad
(verify_netmon_download.sh,
verify_netmon_signature.sh, SHA256SUMS.minisig).
Die in Runde 7/8 gehärteten Bereiche wurden auf Regressionen
gegengeprüft.
Phase 1 — Release-Recon
Verifikation der Artefakte: sha256sum -c SHA256SUMS
(alle drei OK), minisign -V gegen den gepinnten
Public Key (Signatur gültig). Extraktion und Review der
.deb-Maintainer-Skripte. Versionskonsistenz-Abgleich.
Phase 2 — Kandidaten-Analyse
Ein Analyse-Sub-Agent verfolgte Datenflüsse von Fremdeingaben
(Update-/API-Antworten, VPN-Profil-/Slot-Daten, Install-Zeit-Zustand)
bis zu privilegierten Senken (ufw/nmcli/
wg-quick, xdg-open, root-cp/
chown). 5 Kandidaten identifiziert.
Phase 3 — False-Positive-Validierung
Die zwei belastbaren Kandidaten wurden von je einem eigenen Validierungs-Sub-Agenten gegengeprüft (beide Konfidenz 8/10, MEDIUM). Drei weitere Kandidaten (postinst-DNS-Policy, State-Datei-Parsing, root-Symlink-chown) lagen als Policy-/Grenzfall unter der Schwelle.
Phase 4 — Härtung & Regressionscheck
Beide MEDIUM-Findings wurden umgesetzt, mit neuen Regressionstests abgesichert und verifiziert. Suiten erneut ausgeführt: Killswitch/VPN/DNS/Firewall (253) und Diagnostik/Update-Check (80) — alle sicherheitsrelevanten Tests grün.
Bestätigte Findings (Runde 9)
Zwei Findings mittleren Schweregrads wurden bestätigt (Konfidenz je 8/10) und direkt behoben. Keine kritischen oder hohen, keine klar remote-ausnutzbaren RCE-Befunde.
F1 — Update-Check öffnet server-gelieferte URL ohne Schema-Allowlist
Mittel · 8/10 BehobenDatei: netmon/app.py —
_fetch_latest_release_info(),
check_about_latest_version(),
_open_url_with_xdg_open().
Der In-App-Update-Check lädt latest-version.json
per HTTPS und übernimmt das Feld download_url
daraus ungeprüft. Wird beim Öffnen des
„Über“-Dialogs eine neuere Version erkannt, wird
diese URL ohne weiteren Klick an xdg-open
übergeben — ganz ohne Schema-/Host-Prüfung. Genau
diese http/https-Allowlist existiert an vergleichbaren Stellen
bereits (Secure-Browser-fetch_url(), Radio-
_station_logo_url()); der Update-Pfad war die einzige
übersehene Fremd-URL-Senke.
Bedrohung: Ein kompromittierter Server/CDN
(im Bedrohungsmodell ausdrücklich enthalten) kann eine
download_url mit beliebigem Schema liefern
(file://, Custom-Handler wie vnc://) oder
auf eine Phishing-Seite verweisen, die xdg-open an den
zuständigen Desktop-Handler weiterreicht. Kein direktes RCE
(Listenform-Popen, keine Shell-Injection), aber
zuverlässig für Phishing und — situativ —
zum Ansteuern eines registrierten Scheme-Handlers.
_open_url_with_xdg_open() begrenzt Ziel-URLs jetzt auf
http/https (deckt alle Aufrufer ab);
zusätzlich lässt der Update-Pfad über
_is_trusted_update_url() nur eine
netmonroots.com-URL automatisch öffnen und
fällt sonst auf die fest konfigurierte Download-Seite
zurück. Mit Regressionstests abgesichert
(test_is_trusted_update_url_accepts_only_netmonroots_https,
test_open_url_with_xdg_open_rejects_non_web_scheme).
F2 — Killswitch-Fail-Open in frühen VPN-Slot-Wechsel-Fehlerpfaden
Mittel · 8/10 BehobenDateien:
netmon/features/vpn_controller.py
(vpn_switch()),
netmon/features/firewall_controller.py
(recover_firewall_after_killswitch_failure()).
recover_firewall_after_killswitch_failure() setzt die
Firewall bedingungslos auf ufw default allow outgoing.
Sie wurde in drei frühen Fehlerpfaden von
vpn_switch() aufgerufen (leeres Ziel, nicht
auflösbares Runtime-Profil, WG-Cleanup-Fehler) —
ohne zu prüfen, ob der Killswitch zu diesem Zeitpunkt
bewusst aktiv war.
Bedrohung: Nutzer hat Killswitch AN + VPN aktiv,
wählt einen VPN-Slot, dessen Profil gelöscht/umbenannt
wurde oder dessen IPv4-Runtime-Kopie fehlt →
vpn_switch() bricht früh ab → die
deny-outgoing-Garantie wird still auf allow
zurückgesetzt, und Klartext-Traffic kann das System verlassen,
obwohl der Nutzer Killswitch-Schutz erwartet. Das ist genau der
Leak, den das Feature verhindern soll — ein Fail-Open in
einer Kontrolle, deren einziger Zweck Fail-Closed ist. (Kein
Remote-Trigger; die UI-Statusanzeige wechselt zwar auf Rot, es gab
aber keinen Warnhinweis.)
_recover_firewall_preserving_killswitch(): War der
Killswitch aktiv (killswitch_is_on(), ausgewertet
vor jeder Zustandsänderung), wird die
deny-outgoing-Policy per idempotentem
_killswitch_on_sync() erhalten statt
geöffnet; nur ohne aktiven Killswitch wird wie bisher auf
allow-outgoing zurückgesetzt, damit sich der Nutzer nicht
aussperrt. Mit Regressionstests abgesichert
(test_recover_preserving_killswitch_*,
test_vpn_switch_empty_target_preserves_active_killswitch).
Release-Verifikation & weitere Beobachtungen
Signaturkette technisch dicht — Empfehlung zur Sichtbarkeit
SHA256SUMS deckt alle drei Artefakte korrekt ab
(sha256sum -c OK) und ist per
minisign signiert; die Signatur wurde gegen den
gepinnten Public Key gültig verifiziert. Das
Verifier-Skript prüft jedoch nur die Hashes, und die
prominente „Schnellprüfung“ auf der Downloadseite
empfiehlt ebenfalls nur sha256sum -c — ein
MitM, der .deb und SHA256SUMS
gemeinsam ersetzt, bestünde diesen Schnellcheck.
Empfehlung (Doku/UX, keine Code-Lücke): die
minisign-Vollprüfung als empfohlenen Standardweg
hervorheben statt als optionalen „falls signiert“-Zusatz.
Kindersicherung — weiterhin bewusste Design-Entscheidung
Die PBKDF2-gestützte Kindersicherung im Secure-Browser bleibt wie in Runde 8 dokumentiert: Alltagshürde gegen Gelegenheitszugriff im selben Konto, keine harte Grenze gegen technisch versierte Nutzer mit Kontozugriff. Keine Codeänderung, unverändert gültig.
postinst setzt aktive NM-DNS auf 8.8.8.8 — Policy-Hinweis
Das root-postinst biegt aktive
NetworkManager-Verbindungen bei der Installation auf
ipv4.dns 8.8.8.8 um (Vorzustand wird gesichert und
in prerm restauriert). Funktional sauber, aber für
ein Privacy-Tool eine überraschende Richtung.
Empfehlung: Opt-in statt hart verdrahtetem
Public-Resolver. Kein ausnutzbarer Befund (State-Datei
0600 root, Argumentlisten-Aufrufe, keine
Injection). Keine Codeänderung in dieser Runde — als
Design-Entscheidung des Projektverantwortlichen offen.
Umgesetzte Härtungsmaßnahmen (Runde 9)
Schema-Allowlist + Host-Pin für die Update-URL — app.py
_open_url_with_xdg_open() lässt nur noch
http/https zu;
_is_trusted_update_url() beschränkt die
automatisch geöffnete Update-URL zusätzlich auf die
netmonroots.com-Domain.
Killswitch-erhaltender Recover-Pfad — vpn_controller.py
_recover_firewall_preserving_killswitch() hält die
deny-outgoing-Policy bei aktivem Killswitch aufrecht, statt sie bei
einem fehlgeschlagenen Slot-Wechsel still zu öffnen.
Neue Regressionstests
Fünf neue Tests in test_netmon_diagnostics.py und
test_netmon_killswitch.py sichern die Update-URL-
Allowlist und das Killswitch-erhaltende Recover-Verhalten ab.
Sicherheitsrelevante Suiten (253 + 80 Tests) sind grün.
Geprüfte Sicherheitskategorien
| Kategorie | Geprüft | Ergebnis |
|---|---|---|
| Sicherheitskontroll-Umgehung (Killswitch Fail-Open) | Ja | F2 → behoben (fail-closed) |
Unvalidierte Fremd-URL an Desktop-Handler (xdg-open) |
Ja | F1 → behoben (Allowlist+Host-Pin) |
| Command-/Argument-Injection (ufw/nmcli/wg-quick, subprocess) | Ja | Kein Finding (durchgängig list-form) |
| DNS-Startup-Recovery (privilegierte nmcli/resolvectl-Aufrufe) | Ja | Kein Finding (validiert, fail-safe) |
Debian-Maintainer-Skripte (root: cp/chown, State-Parsing) |
Ja | Kein ausnutzbarer Befund (Policy-Hinweis) |
| Release-Integrität (SHA256SUMS + minisign) | Ja | Signatur gültig (UX-Empfehlung) |
| Pfad-Traversal / TOCTOU / Symlink (privilegierte Dateiops) | Ja | Kein Finding im Threat-Model |
| Autorisierung / Kindersicherung | Ja | Bewusste Design-Entscheidung |
Unsichere Deserialisierung (pickle, yaml.load, eval, shell=True) |
Ja | Kein Finding |
| Regression der Runde-7/8-Härtungen | Ja | Alle intakt |
| Versionskonsistenz (App / deb / Site / latest-version.json) | Ja | Durchgängig 1.1.19 |
Auditor: Claude Fable 5 (Anthropic) —
Analyse-Sub-Agent + zwei dedizierte False-Positive-Validierungs-Sub-Agenten
Projektverantwortlicher: Sven Fröhlich
Datum: 18. Juli 2026
Analysierter Codestand:
finaler Release-Stand v1.1.19 (Arbeitsstand auf
c2087b63), einschließlich der gebauten
netmon_1.1.19_all.deb und der signierten
Release-Artefakte — Runde 9
Methode: Statische Code-Analyse + manuelle
Datenfluss-Verifikation + empirische Verifikation der Release-Signatur
(sha256sum -c + minisign -V) +
Konfidenz-gescorte FP-Validierung pro Kandidat + Umsetzung von zwei
Defense-in-Depth-Härtungen mit Regressionstests +
Regressions-Gegenprüfung der Runde-7/8-Härtungen (Suiten:
253 + 80 Tests, alle sicherheitsrelevanten bestanden) — 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.