§1
RoamSwitch ist eine Menüleisten-App für den Mac. Je nachdem, wie sehr Sie dem Netzwerk vertrauen, dem Sie vertrauen
aktuell verbunden, schaltet es automatisch die macOS-Firewall, die Freigabedienste, AirDrop usw. um.
und DNS. Es überwacht auch ARP-Spoofing, nach außen offene Ports, USB-Speicher usw
Ransomware-ähnliche Verschlüsselungsaktivitäten, und wenn etwas Gefährliches entdeckt wird, geht es so weit
Unterbrechen des Datenverkehrs mit dem Paketfilter (pf) – ein Notfall-Air-Gap.
Mit anderen Worten: RoamSwitch installiert einen Root-privilegierten Daemon und könnte, wenn er wollte, alle stoppen Netzwerkverkehr auf dem Mac. Es wird von einer Person gebaut und „Bitte vertrau mir“ reicht nicht aus rechtfertigen Sie dieses Privileg. Stattdessen erklärt dieses Dokument das Design in einer Form, die Sie überprüfen können.
Für wen das ist
- Ingenieure entscheiden über die Installation
- Sicherheitsforscher und Journalisten, die vor einer Rezension oder einem Artikel die Interna verstehen wollen
- Sicherheitspersonal bei Partnerunternehmen evaluiert einen internen Rollout oder eine OEM-Bündelung
Was dieses Dokument nicht abdeckt
Es geht nicht um die Optimierung der Erkennungsschwelle, falsch-positive Statistiken oder exemplarische Vorgehensweisen für die Benutzeroberfläche.
Es erklärt vier Dinge: Privilegien, Prozessgrenzen, Datenfluss und
Kryptographie. Die Funktionsspezifikationen selbst befinden sich in der gebündelten MCP-Ressource
roamswitch://docs/features und in der In-App-Hilfe.
Was wird offengelegt und wie weit (Offenlegungsrichtlinie)
Dieses Dokument basiert auf der Annahme, dass ein Angreifer bereits über die verteilte Binärdatei verfügt.
Alle Endpunkt-URLs, Kennungen, Dateipfade, XPC-Protokolle und eingebetteten öffentlichen Schlüssel, die hier angezeigt werden
kann in wenigen Minuten aus dem Versand RoamSwitch.app gezogen werden
strings, codesign -d oder ein Verkehrsproxy. Schreiben Sie sie daher hier auf
gibt einem Angreifer nichts Neues. Der einzige Fortschritt ist das Verständnis des Rezensenten.
Andererseits ist die serverseitige Implementierung nicht aus der Binärdatei ersichtlich – Rate-Limit Schwellenwerte, Schlüssel, Admin-Endpunkte, das DB-Schema, das Firebase-Projektlayout – sind nicht enthalten. Das OEM- und Partner-Integrationsdesign liegt hier ebenfalls nicht im Rahmen und wird separat behandelt internes Dokument. „Was man durch die Beobachtung des Klienten lernen kann“ lautet die Offenlegungszeile hierfür Dokument.
Dieses Dokument entspricht der Quelle für die oben angegebene Version. Wo sich das Verhalten ändert
In einer späteren Version wird das Dokument überarbeitet und die Versionsnummer und der Ziel-Build werden aktualisiert.
Sollten Sie eine Diskrepanz zwischen dem Text und dem Code feststellen, teilen Sie uns dies bitte unter mit
lafine.net/contact.html.
§2
RoamSwitch.app besteht aus drei ausführbaren Dateien. Nur einer von ihnen ist privilegiert; die
Die anderen beiden werden mit Login-Benutzerrechten ausgeführt. Alle drei werden mit aktivierter Hardened Runtime und Entwickler-ID ausgeliefert
unterzeichnet und notariell beglaubigt.
| Ausführbar | Privileg | Kann | Geht nicht |
|---|---|---|---|
| RoamSwitch .app |
Benutzer anmelden | Überwachen Sie den Netzwerkstatus, führen Sie Diagnosen aus, zeichnen Sie die Benutzeroberfläche, rufen Sie den Helfer über XPC auf, ändern Sie AirDrop über defaults, starten Sie ClamAV (optional). |
Betreiben Sie die Firewall, PF oder System-Daemons direkt (alles läuft über den Helfer). |
| RoamSwitch Helper |
root | Nur die in HelperProtocol (Tabelle §3) aufgeführten Vorgänge: Firewall/Stealth, Laden/Entladen der Sharing-Daemons, Anwenden des PF-Regelsatzes, DNS-Änderung, Senden von Signalen an Prozesse |
Noch etwas. Es gibt keine Schnittstelle zum Ausführen beliebiger Befehle. Es besteht auch keine Berechtigung zum Senden über das Netzwerk |
| RoamSwitch MCPServer |
Benutzer anmelden | Diagnosewerte lesen und formatieren, lokale Wissensdatenbank durchsuchen; Die Ergebnisse werden über stdio an den Client zurückgegeben | Ändern Sie Einstellungen, schalten Sie die Sperre um, isolieren Sie einen Port, werfen Sie ein Gerät aus. Es öffnet sich kein Socket. Es wird nichts über das Netzwerk gesendet |
Berechtigungen und Unterzeichnung
- Alle drei Ziele haben
ENABLE_HARDENED_RUNTIME = true. - App Sandbox ist deaktiviert (
com.apple.security.app-sandbox = false). Die Berechtigungen des Helfers und des MCPServers sind leere Wörterbücher. - Distributions-Builds werden vom Entwickler-ID-Antrag unterzeichnet, von Apple notariell beglaubigt und geheftet (§10).
App Sandbox wird nicht verwendet. RoamSwitch muss die Hardware-UUID von IOKit lesen, CoreWLAN verwenden und
DiskArbitration, Listening-Sockets anderer Prozesse (lsof) auflisten, einen XPC öffnen
Verbindung zu einem LaunchDaemon herstellen und Systembinärdateien erzeugen. Nichts davon ist im Inneren möglich
Sandbox, daher bleibt sie deaktiviert.
Vier Dinge gleichen das aus. Erstens: Gehärtete Laufzeit. Zweitens: Signieren der Entwickler-ID und notarielle Beurkundung. Drittens wird nur eine ausführbare Datei als Root ausgeführt – der Helfer – und was dieser Helfer tun kann ist festgelegt und aufgezählt (Tabelle §3). Viertens werden Verbindungen zum Helfer durch Code eingeschränkt Unterschrift (§3).
§3
Wie es registriert wird
Der Helfer wird mit SMAppService.daemon(plistName:) als LaunchDaemon registriert. Die
plist in die App eingebettet
(Contents/Library/LaunchDaemons/com.tetsuharu.RoamSwitch.Helper.plist) deklariert nur
Label, BundleProgram, ein einzelner MachServices-Eintrag und
AssociatedBundleIdentifiers. Aufgrund der Funktionsweise von SMAppService
Eine Registrierung ist nicht einmal möglich, es sei denn, die App ist in /Applications. Am ersten
Nach der Registrierung wird der Helfer erst dann aktiv, wenn der Benutzer ihn im System manuell genehmigt
Einstellungen.
Wessen Verbindungen es akzeptiert (ClientValidator)
Der Helfer überprüft den Verbindungsvorgang in NSXPCListeners
shouldAcceptNewConnection und verteilt HelperProtocol nur an diejenigen, die
passieren. Die Inspektion verwendet den audit_token anstelle der PID
Vermeiden Sie die Wiederverwendung von PIDs und TOCTOU.
# Code-Signing-Anforderungen, die der Release-Build an verbindende Clients stellt
identifier "com.tetsuharu.RoamSwitch"
and anchor apple generic
and certificate leaf[subject.OU] = "GV76B6G4YU"
Diese Anforderung wird mit SecCodeCopyGuestWithAttributes und überprüft
SecStaticCodeCheckValidity, und die Verbindung wird getrennt, wenn sie nicht erfolgreich ist. Nur
DEBUG-Builds verzichten aus Gründen der Entwicklungsfreundlichkeit auf die Team-ID-PIN. Was tatsächlich versendet wird, ist immer ein
Release-Build.
Die Sicherheit des Helfers beruht auf dieser einzigen Codesignaturanforderung. Alles, was es erfüllt (a
ordnungsgemäß signiert RoamSwitch.app) kann jede Operation in der folgenden Tabelle aufrufen. Es gibt
Kein Kanal für die Eingabe beliebiger Befehle, aber die Operationen in dieser Tabelle weisen keine Schwachstellen auf
sich selbst. Wenn RoamSwitch.app selbst übernommen wird, gehen diese Operationen an den über
Angreifer.
Was der Helfer tun kann (die vollständige Liste)
Die in Shared/HelperProtocol.swift definierten privilegierten Operationen sind alle davon.
Es gibt keine privilegierte API, die hier nicht aufgeführt ist.
| Methode | Was es bewirkt | Binär/API aufgerufen |
|---|---|---|
| setBlockAll(_:) | Schaltet die Anwendungs-Firewall und den Stealth-Modus ein oder aus | /usr/libexec/ApplicationFirewall/ --setblockall / --setstealthmode |
| getBlockAllStatus(...) | Liest die aktuellen Werte der oben genannten | socketfilterfw --getblockall |
| setSharingServicesEnabled(_:) | Entladen/Laden der SSH-/SMB-/Screen-Sharing-Daemons. Beim Stoppen werden nur „die ausgeführten“ aufgezeichnet und nur diese wiederhergestellt | /bin/launchctl list / unload -w / load -w (festgelegt auf die drei: ssh.plist / com.apple.smbd.plist / com.apple.screensharing.plist) |
| enableNetworkAirGap(...) disableNetworkAirGap(...) |
Wendet die Not-Vollsperre (block drop all) an und hebt sie auf. Geht durch PFRulesetCoordinator (§4) |
/sbin/pfctl -f / -e / -sr |
| setGuardedDevServerPorts(_:) | Verwendet pf, um nur external Verbindungen zu den angegebenen Dev-Server-Ports zu blockieren (localhost leitet durch). Durch das Übergeben eines leeren Arrays werden alle angehoben | /sbin/pfctl (auch über den Koordinator) |
| setSecureDNSServers(_:) restoreOriginalDNSServers(...) getCurrentDNSServers(...) |
Schaltet das DNS aktiver Netzwerkdienste auf Malware-blockierendes DNS (Quad9 9.9.9.9 / Cloudflare 1.1.1.2) um, sichert die ursprünglichen Einstellungen und stellt sie wieder her |
/usr/sbin/networksetup -listallnetworkservices / -getdnsservers / -setdnsservers |
| setLinkGuardSinkhole(_:) | Linkschutz (§5). Schreibt die angegebenen Phishing-/Betrugsdomänen in einen durch Trennzeichen getrennten verwalteten Abschnitt von /etc/hosts als 0.0.0.0 und leert dann den DNS-Cache. Ein leeres Array entfernt den Abschnitt. Domänen werden normalisiert und dedupliziert; IPs und Junk werden verworfen; auf 60.000 begrenzt; geschrieben über eine temporäre Datei + atomares Ersetzen |
schreibt /etc/hosts (FileManager.replaceItemAt)/usr/bin/dscacheutil -flushcache /usr/bin/killall -HUP mDNSResponder neu |
| lockGatewayARP(_:) unlockGatewayARP(...) getGatewayARPLockStatus(...) |
Präventive Gateway-ARP/NDP-Sperre (§5). Pinnt die angegebenen IP → MAC-Zuordnungen (das IPv4-Gateway, den IPv6-Standardrouter, On-Link-DNS-Resolver) als permanent-Neighbour-Cache-Einträge. IP- und MAC-Formate werden validiert; ein Abgleichsmodell (Pins, die nicht in der Anfrage enthalten sind, werden entfernt). Der Pinsatz wird auf gateway_arp_lock.json beibehalten. |
/usr/sbin/arp -s / -d /usr/sbin/ndp -s / -d |
| wireGuardImport(_:) wireGuardForget(...) |
VPN-Tunnel (§5). Der Helfer speichert/löscht den WireGuard .conf-Text bei 0600 |
Datei nur schreiben |
| wireGuardUp(endpointIPv4:endpointIPv6:port:) wireGuardDown(...) wireGuardStatus(...) |
Bringen Sie den Tunnel nach oben/unten/lesen Sie den Status. Der Endpunkt-Hostname wird von der App aufgelöst und die IP wird an den Helfer übergeben (der DNS des Helfers kann durch den Kill-Switch unterbrochen werden). | Homebrew wg-quick up/down, wg show (wireguard-tools; deaktiviert, wenn nicht installiert) |
| termineProcess(pid:forceKill:) | Hält einen Prozess an (SIGSTOP) oder erzwingt das Beenden (SIGKILL). Wird verwendet, um Ransomware-ähnliche Prozesse einzudämmen. Nur pid > 1 |
kill(2)-Systemaufruf (kein Unterprozess) |
| getHelperVersion(...) | Gibt die Versionszeichenfolge des Helfers zurück (wird für App-Kompatibilitätsprüfungen verwendet) | — |
terminateProcess kann SIGKILL an jeden Prozess senden, solange
pid > 1. setSecureDNSServers akzeptiert jede DNS-Serverzeichenfolge. Das ist
die Breite, die das Feature benötigt, aber es ist nicht schmal. Beurteilen Sie es mit dem Verständnis, dass die
Die Codesignaturprüfung davor (ClientValidator) ist das einzige Tor.
Zustand im Helfer
HelperTool.sharedist eine einzelne Instanz, die von allen Verbindungen gemeinsam genutzt wird. Früher war es eine separate Instanz pro Verbindung, sodass eine Notfalleindämmung, die eine neue Verbindung öffnete, zu einem Rennen führen konnte, bei dem der Überblick darüber verloren ging, „welche Dienste wiederhergestellt werden sollten“.- Der Sharing-Service und die DNS-Backups werden nur in einer seriellen Warteschlange (
stateQueue) mutiert.
§4
Vier Funktionen berühren Pf: den Notfall-Air-Gap, den Dev-Server-Port-Guard und die VPN-Tunnel
WireGuard-Kill-Switch (§5, 1.7.6+) und sein Tailscale-Kill-Switch (§5, 1.8.0+ – nur die ausgewählten Backends). Alle gehen immer
über einen einzelnen Einstiegspunkt, PFRulesetCoordinator, und wird niemals ausgeführt
pfctl -f selbst.
Warum es einen einzigen Einstiegspunkt gibt
Zuvor luden die beiden Funktionen jeweils unabhängig voneinander Regeln mit pfctl -f und konkurrierten
über den einzigen Hauptregelsatz von pf. Wenn die enge Regel des Hafenwächters block ... port {…} war
Nachdem der Luftspalt block drop all geladen wurde, könnte es zu einem Zustand kommen, in dem der Bildschirm nicht mehr funktioniert
sagte „isoliert“, aber der Mac war immer noch erreichbar. Dieser Fehler wurde durch einen tatsächlichen Angriff auf gefunden
Maschine von einem anderen Host und wurde in 1.4.3 behoben (die Geschichte ist geschrieben in
docs/marketing/zenn/03_lan_side_attack_test.md).
Wie es jetzt funktioniert
- Jedes Mal vollständig neu erstellt. Der gesamte erforderliche Regelsatz wird ausgehend vom aktuellen Status neu erstellt und auf einmal angewendet. Es wird niemals als Diff angewendet.
- Eine serielle Warteschlange. Jede PF-Änderung wird auf demselben
DispatchQueueausgeführt. Unabhängig davon, ob sie von einer XPC-Verbindung, dem Start des Hilfsprogramms oder dem Failsafe-Timer stammt, werden Änderungen der Reihe nach verarbeitet. - Die Prioritätsreihenfolge ist wie folgt (weiter oben stehende Einträge haben Vorrang).
- Notluftspalt →
set skip on lo0undblock drop all(nichts anderes wird berücksichtigt) - VPN-Kill-Switch →
block drop allpluspass quicknur für:lo, die Tunnelschnittstelle (utunN), den UDP-Handshake zu den angepinnten Endpunkt-IP(s), DHCP und ICMP - Dev-Server-Wächter →
block drop in quick proto tcp ... port { … } - Keine →
/etc/pf.confneu laden und pf in seinen ursprünglichen Zustand zurückversetzen
- Notluftspalt →
- Nach der Anwendung zurücklesen.
pfctl -srliest die Regeln zurück, um zu bestätigen, dassblock drop alloder die Regel jedes Ports tatsächlich geladen ist. Ein Fall, in dempfctl -fstillschweigend ignoriert wurde, wird nicht als Erfolg gewertet. - Die temporäre Datei wird in einen Pfad geschrieben, der einen
UUIDenthält, und nach der Anwendung gelöscht. (v1.4.5 hat den festen Pfad zugunsten eines schwer zu erratenden Pfads gelöscht). Das Statusverzeichnis ist/Library/Application Support/RoamSwitch.
Die XPC-Antworten von enableNetworkAirGap und setGuardedDevServerPorts,
(Bool, String?), melden Sie, ob der Vorgang vollständig durchlaufen wurde
zurücklesen. Der Aufrufer (z. B. ARPSpoofContainmentManager) versucht es bei einem Fehler erneut
Wenn es immer noch fehlschlägt, wird die Meldung direkt auf dem Bildschirm angezeigt: „Der Verkehr wurde noch nicht gestoppt. Ausschalten.“
Jetzt WLAN.“
§5
Die Arten von Blöcken sind jeweils unterschiedlich
| Freundlich | Umfang | Auslöser | Loopback |
|---|---|---|---|
| Notluftspalt | Stoppt den gesamten eingehenden und ausgehenden Datenverkehr | Wenn Ransomware-ähnliche Verschlüsselungsaktivitäten erkannt werden. Bei ARP-Spoofing-Erkennung wird es nur im Lockdown sofort ausgelöst; Bei ausgeglichenen/vertrauenswürdigen Netzwerken wird stattdessen eine Benachrichtigung gesendet (Sie lösen es manuell aus). Bei einem bekannten „ClickFix“-artigen Befehlsmuster im Shell-Verlauf, etwa dem Base64-Dekodieren direkt in eine Shell oder AppleScript (1.8.7+, Pro, standardmäßig deaktiviert). Wird nicht für den alltäglichen Schutz unterwegs verwendet | Durchgereicht mit set skip on lo0 |
| VPN-Kill-Switch | Alles außer dem Tunnel, seinem Handshake, DHCP und ICMP | Wenn der VPN-Tunnel (§5, Pro, standardmäßig deaktiviert) aktiviert ist und Sie sich einem nicht vertrauenswürdigen Netzwerk anschließen. Wird gehalten, bis der Tunnel eingerichtet ist (und solange er geschlossen ist) | Durchgereicht mit set skip on lo0 |
| Dev-Server-Portwächter | Nur external TCP-Verbindungen zu den angegebenen Ports | Eine manuelle Isolierung mit einem Klick oder automatische Blockierung, wenn ein unbekannter Abhörport erkannt wird (Pro) | Von localhost, unverändert |
| Täglicher Schutz vor nicht vertrauenswürdigen Netzwerken | Firewall und Stealth, Freigabe gestoppt (§6). pf wird nicht verwendet | Wenn Sie eine Verbindung zu einem Netzwerk herstellen, sind Sie nicht registriert | — |
| Linkschutz | Nur Namensauflösung von Phishing-/Betrugsdomänen (0.0.0.0 über /etc/hosts). pf wird nicht verwendet |
Ein Ziel im Bedrohungs-Feed oder ein Markenhomograf. Standardmäßig aktiviert (Pro) | Nicht betroffen |
| Präventive ARP/NDP-Sperre | Nur der MAC des Gateways, des IPv6-Routers und On-Link-DNS (Nachbarcache). pf wird nicht verwendet | Beim Beitritt zu einem nicht vertrauenswürdigen Netzwerk (Pro, standardmäßig deaktiviert). Wird bei jedem Netzwerkwechsel neu angepinnt | Nicht betroffen |
Wie verhindert wird, dass der Luftspalt bestehen bleibt
- Sie wird nach spätestens 10 Minuten aufgehoben. Der Helfer selbst führt weiterhin unmittelbar nach dem Start und über einen 60-Sekunden-Timer
releaseAirGapIfExpired()aus, aber als vollständig unabhängiges Sicherheitsnetz erwacht ein dedizierter LaunchDaemon (AirGapFailsafe) ohne jeglichesKeepAliveselbstständig alle 3 Minuten und führt dieselbe Prüfung und Aufhebung durch. Stürzt der Helfer selbst ab und reißt seinen eigenen Selbstaufhebungs-Timer mit sich, bleibt dieses Sicherheitsnetz davon unberührt (ab 1.8.6). - Dieser dedizierte Daemon rührt niemals die eigene Startkonfiguration des interaktiven Helfers an. Ein früherer Ansatz (1.8.5) nutzte für diese Wiederherstellung das eigene
KeepAlive.PathStatedes Helfers, das jedoch gelegentlich mit einem normalen Beenden der App kollidierte und einen unbeabsichtigten Neustart auslöste, weshalb er zugunsten des jetzigen, vollständig unabhängigen Daemons zurückgenommen wurde. - Die Zeitstempelprüfung verwendet eine Hybridlösung aus monotoner Systemlaufzeit (
ProcessInfo.systemUptime) und der Uhrzeit. Bevorzugt wird normalerweise die monotone Systemlaufzeit, sodass NTP-Korrekturen und manuelle Uhrzeitänderungen keine Auswirkung haben. Nur wenn die Systemlaufzeit nicht vertrauenswürdig ist – unmittelbar nach einem Neustart, erkannt daran, dass der aktuelle Wert kleiner ist als der gespeicherte – wird auf die Uhrzeit zurückgegriffen. - Es wird nach einem Neustart oder einem Daemon-Respawn erneut angewendet. Beim Start liest der Helfer den Zustand auf der Festplatte mit
reapplyFromDisk()und stellt ihn selbst wieder her, in der Reihenfolge Air Gap, Port Guard, Systemstandard (auch hier gilt die 10-Minuten-Regel). - ___ Auf dem Bildschirm wird nie fälschlicherweise „angehoben“ angezeigt.
- Sie können jederzeit die Kontrolle zurückerlangen – mit der Lift-Taste im Modal oder einfach durch Ausschalten des WLANs.
Wie ein Port-Guard-Falsch-Positiv-Ergebnis wiederhergestellt wird
Die automatische Blockierung unbekannter Ports (Pro, standardmäßig aktiviert, sobald Pro aktiviert ist) kann einen legitimen Port stoppen
LAN-Empfänger – LocalSend, Syncthing, alles, was nach der Aktivierung des Wächters gestartet wurde. Wenn das
passiert, erlauben Sie es über die Schaltfläche „Zulassen“ im Benachrichtigungsbanner oder in der entsprechenden Zeile
den Bildschirm „Freigelegte Anschlüsse“. Eine einmal zulässige ausführbare Datei wird als bekannt aufgezeichnet und nicht blockiert
wieder (PortAnomalyGuard.allowPort(_:)). Beachten Sie, dass generische Skriptinterpreter
(Python, Node.js, Netcat usw.) haben einen strengen Gültigkeitsbereich von path:port und nicht von Binärdateien
allein, um Angriffe von Landbewohnern zu verhindern. Apple-System-Daemons, die zufriedenstellend sind
anchor apple (rapportd, das Handoff unterstützt und dergleichen) sind es nicht
als erstes gesehen.
Selbstheilender Ransomware-Kanarienköder und Eindämmungsunterdrückung
Wenn Canary-Decoy-Dateien manipuliert oder umbenannt werden und den Notfall-Luftspalt auslösen, erfolgt die Freigabe Nach der Sicherheitsüberprüfung werden fehlende oder beschädigte Canary-Dateien automatisch neu generiert kehren zu ihrem authentischen Basis-Hash zurück, der aus eingebetteten Vorlagen generiert wurde, wodurch die ununterbrochene Überwachung der K-Warteschlange sofort wiederhergestellt wird (wodurch eine Kontamination der Basislinie durch den Gegner verhindert wird). Darüber hinaus werden während der aktiven Eindämmung (während das Notfallmodal angezeigt wird) redundante Benachrichtigungen und erneut auslösende Ereignisse aus regelmäßigen Hintergrund-Integritätsabfragen automatisch unterdrückt, um eine Ablenkung des Benutzers während der Reaktion auf den Vorfall zu vermeiden.
Mehrschichtige Erkennung heruntergeladener Dateien (statische Signaturen + ClamAV) – statische Signaturschicht ab 1.8.7
Neu im Downloads-, Schreibtisch- oder Dokumente-Ordner abgelegte Dateien durchlaufen zunächst eine leichtgewichtige statische Signaturprüfung durch StaticSignatureScanner (nur die ersten 4 MB, einfacher AND/OR-Bytemuster-Abgleich – keine regulären Ausdrücke oder Entropieberechnungen). Sie benötigt kein EndpointSecurity-Entitlement und funktioniert auch dort weiter, wo ClamAV nicht installiert ist. Die Erkennung ist bewusst auf das beschränkt, was ohne einen echten Malware-Korpus als Tatsache belegt werden kann: die branchenübliche EICAR-Testsignatur sowie fünf in öffentlichen Offensive-Security-Referenzen (z. B. PayloadsAllTheThings) dokumentierte Lehrbuch-Reverse-Shell-Einzeiler – /dev/tcp/ von bash/sh, -e von netcat, pty.spawn von Python, Socket von Perl und fsockopen von PHP. Wir schreiben keine bytegenauen „Signaturen“ für namentlich bekannte Malware-Familien, ohne eine echte Probe zu besitzen, aus der sie extrahiert werden könnten – das wäre nur ein trügerisches Sicherheitsgefühl. Die Muster selbst werden XOR-0x5A-verschleiert gespeichert, damit RoamSwitchs eigene Binärdatei niemals eine wörtliche EICAR-Zeichenfolge oder einen Reverse-Shell-Einzeiler enthält, wegen der ClamAV selbst sie melden könnte.
Nur Dateien, die die statische Signaturschicht als „sauber“ einstuft, gehen weiter in den ClamAV-Scan. Neu im Downloads-, Schreibtisch- oder Dokumente-Ordner abgelegte Dateien werden unabhängig von einer .tmp-Erweiterung oder einem com.apple.quarantine-Attribut (etwa bei einer über das Terminal erstellten Kopie) sofort von ClamAV gescannt. EICAR und andere branchenübliche Testsignaturen gelten als harmlos: Es wird keine Benachrichtigung angezeigt, sondern nur ein Eintrag im Benachrichtigungsverlauf angelegt – und sie werden nie unter Quarantäne gestellt oder blockiert (nur echte Malware-Proben werden unter Quarantäne gestellt). Und war bereits zuvor eine Datei gleichen Namens unter Quarantäne gestellt worden, wird die neue trotzdem unter einem eindeutigen, zeitgestempelten Namen in den Quarantäne-Ordner verschoben, sodass eine Bedrohung wegen einer Namenskollision nie an ihrem ursprünglichen Ort verbleiben kann.
Überwachung neuer Autostart-Persistenz (LaunchAgent/LaunchDaemon) – 1.8.7
Neue .plist-Dateien unter ~/Library/LaunchAgents, /Library/LaunchAgents oder /Library/LaunchDaemons werden per FSEvents in Echtzeit erkannt (PersistenceMonitorGuard). Ohne ES_EVENT_TYPE_AUTH_CREATE von EndpointSecurity lässt sich der Schreibvorgang selbst nicht blockieren. Möglich ist jedoch, ihn innerhalb von Sekunden zu bemerken und den Inhalt zu bewerten.
Die Bewertung stützt sich darauf, was direkt gestartet wird, nicht auf die Signatur der ausführbaren Datei selbst. Reale Techniken aus dem Jahr 2026 – ein als Google Update getarnter LaunchAgent, der ein Base64-dekodiertes Bash-Skript verbirgt, oder ein LaunchDaemon mit Root-Rechten, der bei jedem Neustart eine Base64-dekodierte AppleScript-Payload erneut ausführt – verstecken den bösartigen Code beide Male im Skript selbst, während sie ein gültig signiertes /bin/bash oder /usr/bin/osascript unverändert aufrufen. Eine Prüfung, die nur die Signatur der ausführbaren Datei betrachtet, würde beides ungehindert durchlassen. Deshalb wird jeder neu registrierte LaunchAgent/LaunchDaemon, der einen rohen Skriptinterpreter (bash, sh, zsh, osascript, python3, perl, ruby, php und Ähnliches) direkt aufruft, unabhängig von dessen gültiger eigener Signatur, ausnahmslos gemeldet. Die dabei übergebenen Argumente werden zudem durch StaticSignatureScanner geprüft, und jedes gefundene bekannte bösartige Muster wird in die Benachrichtigung aufgenommen. Einträge, die über BundleProgram (verweist auf eine signierte, kompilierte Binärdatei innerhalb eines App-Bundles – einschließlich RoamSwitchs eigenem Helfer) registriert wurden, werden ganz normal per codesign --verify --strict verifiziert.
ClickFix-Erkennung und Notfall-Abriegelung – 1.8.7, standardmäßig deaktiviert
„ClickFix“ ist eine Social-Engineering-Technik, bei der ein gefälschtes CAPTCHA oder eine Fehlermeldung dazu auffordert, das Terminal zu öffnen und einen Befehl „zur Verifizierung“ einzufügen. Die Erkennungen stiegen von 2024 bis 2025 um über 500 %, und bis 2026 gilt sie als einer der am weitesten verbreiteten macOS-Angriffsvektoren. Da es sich um die eigene, legitime Shell handelt, die genau das ausführt, was eingegeben wurde, umgeht sie Gatekeepers Signaturprüfung vollständig.
Menüleiste → Malware-Schutz → ClickFix-Abwehr (Pro, standardmäßig deaktiviert). Überwacht per FSEvents neu angehängte Zeilen in ~/.zsh_history/~/.bash_history (nur Zeilen, die nach dem Start der Wache hinzugefügt werden – der bestehende Verlauf wird nie durchsucht) und gleicht ausschließlich die Reverse-Shell-Einzeiler ab, die sie mit StaticSignatureScanner teilt, sowie die spezifische Kombination aus Base64-Dekodierung direkt in eine Shell oder in osascript. Ein bloßes curl | bash – bei legitimen Installationsprogrammen äußerst verbreitet – ist bewusst ausgeschlossen, da es sich nicht von den offiziellen Installationsanleitungen für Homebrew, rustup, nvm und Ähnliches unterscheiden lässt. Sobald eine Übereinstimmung gefunden wird, löst sie denselben oben beschriebenen Notfall-Air-Gap aus. Da der Befehl bereits ausgeführt wurde, geschieht dies zwangsläufig im Nachhinein, kann aber dennoch einen laufenden zweiten Download oder eine Zugangsdaten-Exfiltration unterbrechen. Wie jeder andere Air-Gap-Auslöser hebt sie sich von selbst nach spätestens 10 Minuten auf (siehe Beginn dieses Abschnitts).
Eine bekannte Einschränkung. Da die Erkennung von einem Schreibvorgang in den Shell-Verlauf abhängt, kann sie die dateilose Variante nicht erfassen, die das Terminal vollständig umgeht, indem sie den Skripteditor direkt über das applescript://-URL-Schema aufruft und nichts auf die Festplatte schreibt. Apple hat hierfür eine gewisse Abschwächung eingeführt (ein Bestätigungsdialog für nicht identifizierte Skripte), doch es wurden Varianten gemeldet, die diese umgehen. Wir haben diesen Pfad auf echter Hardware gegen das einheitliche macOS-Protokoll (log show/log stream) getestet und kein charakteristisches Protokollsignal gefunden, an dem eine Erkennung ansetzen könnte. Wir stellen diese Einschränkung lieber offen dar, als eine Abdeckung zu behaupten, die wir nicht haben.
Docker-Risikoerkennung — 1.8.9, Pro, standardmäßig deaktiviert
Menüleiste → „Malware-Schutz“ → „Privilegierte Docker-Container & docker.sock-Mounts erkennen“ (DockerEventGuard). Da es in dieser Codebasis keinen Präzedenzfall für eine dauerhafte Streaming-Verbindung wie docker events gibt, wird derselbe Timer-Polling-Ansatz wie bei PortAnomalyGuard verwendet. Alle 20 Sekunden wird mit einem leichten docker ps -q die Menge der Container-IDs abgerufen, und nur die Differenz zum vorherigen Stand (neu gestartete Container) wird mit docker inspect --format im Detail geprüft. Der zur Erkennung verwendete Format-String ist absichtlich identisch mit dem der Linux-Edition (roamswitch_core::health::LinuxHealthChecker::DOCKER_INSPECT_RISK_FORMAT), sodass beide Plattformen exakt dieselben Bedingungen erkennen (Start im --privileged-Modus oder ein /var/run/docker.sock-Bind-Mount).
Bei der Erkennung wird keine automatische Aktion ausgelöst. Ein privilegierter Container oder ein docker.sock-Mount ist eine riskante „Konfiguration“, die einen Container-Escape ermöglichen könnte, aber keine bestätigte Kompromittierung – es gibt auch legitime Anwendungsfälle, etwa den bewussten Betrieb eines Überwachungs-Agents als privilegiert. Da die meisten Nutzer überhaupt kein Docker verwenden, bleibt diese Funktion auch mit Pro-Lizenz standardmäßig deaktiviert. Sie verwendet nicht das EndpointSecurity-Entitlement; die docker-CLI wird zunächst am Standard-Installationspfad von Docker Desktop, dann über Homebrew und schließlich über which gesucht. Verifiziert an einer echten Docker-Desktop-Installation (29.7.2) anhand von drei Szenarien – ein privilegierter Container, ein Container mit docker.sock-Mount und ein normaler Container – ohne Fehlalarme oder verpasste Erkennungen.
Geheimnis-/API-Schlüssel-Leck-Audit — hinzugefügt in 1.8.4, Ordner-Scan hinzugefügt in 1.8.9
Menüleiste → „Malware-Schutz“ → „Geheimnis-/API-Schlüssel-Leck-Audit“. Text einfügen, und er wird sofort auf durchgesickerte API-Schlüssel und Tokens geprüft, mit Zeilennummer, maskiertem String und einer Handlungsempfehlung je Fund (SecretLeakAuditor, vollständig auf dem Gerät). In 1.8.9 kam die Option „Ordner zum Scannen auswählen“ hinzu, mit der dieselbe Erkennungs-Engine ein Verzeichnis – etwa einen Quellcode-Checkout – rekursiv über auditDirectory(at:) prüfen kann. .git, node_modules, target, vendor, dist, build, __pycache__ und venv werden automatisch ausgeschlossen, ebenso Dateien über 2 MB oder als Binärdatei erkannte Dateien. Die Verarbeitung läuft außerhalb des Hauptthreads, um die Oberfläche nicht zu blockieren, und es wird niemals etwas nach außen gesendet.
Link Guard (blockiert Phishing-Verbindungen) – 1.7.2 und höher, verstärkt in 1.8.0
Menüleiste → „Malware-Schutz“ → „Link Guard“ (Pro). Blockiert Verbindungen zu Phishing-/Betrugsseiten auf dem Gerät, in jedem Browser und in jeder App. Es gibt zwei Durchsetzungspunkte, die zusammen in der Reihenfolge ihrer Priorität verwendet werden.
- ① Content-Filter-Systemerweiterung (
RoamSwitchLinkFilter, 1.8.0+, nach Genehmigung bevorzugt). EineNEFilterDataProvider-Systemerweiterung – keine Apple-Überprüfung (Content-Filter-Anbieter sind Selbstbedienungsanbieter, keine Genehmigungswarteschlange). Es überprüft den tatsächlichen ausgehenden TCP-Fluss after Namensauflösung. Der Zielname stammt aus dem vom Betriebssystem aufgelösten Hostnamen oder, falls dies nicht der Fall ist, wurde der TLS SNI aus den ersten Bytes des Flusses geparst – ein Browser, der sein eigenes DoH/DoT ausführt und eine Verbindung zu einer nackten IP herstellt, wird also immer noch blockiert. Es schreibt/etc/hostsnie neu und ordnet jeden Fluss einem Prozess zu. QUIC (UDP/443) hat kein lesbares SNI, daher wird es imblock-Modus gelöscht, wodurch der Browser gezwungen wird, auf HTTP/2 über TCP zurückzugreifen. Erfordert eine einmalige Genehmigung in den Systemeinstellungen. - ②
/etc/hostsSinkhole (Fallback, während die Erweiterung nicht genehmigt/abgelehnt ist). Der privilegierte Helfer schreibt die Zieldomänen in einen durch Trennzeichen getrennten verwalteten Abschnitt von/etc/hostsals0.0.0.0und leert den Cache. Maximal 60.000. Sobald die Erweiterung aktiv ist, wird dieser Abschnitt entfernt. - Drei Modi. „Aus“ deaktiviert es. „Nur warnen“ pausiert die passende Verbindung und fragt den Benutzer (siehe Real
warnunten). „Offensichtliche Betrugsseiten automatisch blockieren (empfohlen)“ wird sofort entfernt. Der Standardwert ist Block ab 1.7.2. - ___ Sobald das Tippen des Benutzers
allowlist.txt/extra.txterreicht – die Erweiterung fragt alle 1,5 s die kleinen Statusdateien ab – werden die angehaltenen Flows fortgesetzt (zulassen) oder gelöscht (blockieren). Keine Antwort innerhalb von 25 s schlägt open fehl (eine Warnung ist beratend). Die Entscheidung wird pro Host zwischengespeichert (5 Minuten zulassen/1 Stunde blockieren), sodass die anderen Flüsse der Seite und spätere Besuche sofort erfolgen und erzwungen werden. Dieblock-Benachrichtigung ist unverändert: „Blockiert …“ mit der Schaltfläche „Einmal zulassen (5 Min.)“. - Nur eindeutige Fälle werden blockiert. Eine Auflistung im Bedrohungs-Feed oder ein Marken-Unicode-Homograph – alles andere (Hochrisiko-TLDs, Subdomain-Nachahmung usw.) ist eine Warnung. Die Urteils-Engine wird mit der Linux-Edition geteilt und sendet URLs nirgendwohin.
- Wiederherstellung nach einer falschen Blockierung. Eine Domain über die Benachrichtigung oder das Menü zulassen (5 Minuten oder dauerhaft). Die Zulassungsliste wird abgezogen, wenn der Abschnitt neu generiert wird.
- Pro-gated. Die Durchsetzung (
applyMode()) erfolgt nur bei einer gültigen Pro-Lizenz. Ohne Pro wird der Modus gespeichert, aber/etc/hostswird nie berührt. Die Aktivierung oder das Erlöschen einer Lizenz wird mitten in der Sitzung wirksam. - Feed und gebündelter Seed. Die Blockliste stammt aus dem signierten Bedrohungs-Feed (§7, verifiziert mit einem Feed-spezifischen Schlüssel). Es funktioniert bereits vor dem ersten Abruf mit dem gebündelten Seed der App (ca. 60.000 Einträge) und die Deaktivierung von „Auto-Update“ bedeutet, dass kein ausgehender Datenverkehr stattfindet.
Präventive Gateway-ARP/NDP-Sperre – 1.7.5 und höher
Menüleiste → „Port- und Gerätemonitor“ → „ARP/NDP des Gateways an nicht vertrauenswürdige Netzwerke anheften (präventiv)“ (Pro, standardmäßig deaktiviert).
- Wie es funktioniert. Beim Beitritt zu einem nicht vertrauenswürdigen Netzwerk wird der aktuelle MAC des IPv4-Gateways, des IPv6-Standardrouters und der On-Link-DNS-Resolver von
route/scutil --dns/arp -n/ndp -anerfasst, und der Helfer pinnt jeden alspermanent-Eintrag mitarp -s/ndp -s(Vertrauen bei der ersten Verwendung – der erste beobachtete MAC ist vertrauenswürdig). Gefälschte ARP/NDP-Antworten für diese IPs werden dann ignoriert, sodass kein Man-in-the-Middle-Angriff durchgeführt werden kann. - Scope. Nur diese drei Arten von Einträgen werden angeheftet. Vertrauenswürdige (offene) Netzwerke werden niemals gepinnt. Bei jedem Netzwerkwechsel wird es einmal entsperrt und erneut angepinnt. Öffentliche Off-Link-Resolver (8.8.8.8, …) haben keinen On-Link-ARP-Eintrag und werden automatisch ausgeschlossen.
arp -sist präventiv; der Luftspalt ist nachträglich. Dieser Pin existiert, daher kann eine Parodie nicht erfolgreich sein; Die ARP-Spoofing-Erkennung (ARPSpoofContainmentManager) und der Notfall-Luftspalt dienen dazu, „schneller als ein Mensch zu schneiden“, sobald einer erkannt wird. Sie laufen unabhängig.- Persistence. Der Pin-Satz wird unter
/Library/Application Support/RoamSwitch/gateway_arp_lock.jsongespeichert, sodass er bei einer erneuten XPC-Verbindung oder einem Neustart des Hilfsprogramms entsperrt werden kann.
Der VPN-Tunnel (WireGuard / Tailscale) und sein Kill-Switch – 1.7.6 und höher, auswählbares Backend in 1.8.0
Menüleiste → „Port- und Gerätemonitor“ → „VPN-Tunnel“ (Pro, standardmäßig deaktiviert). Dies ist die primäre Anti-MITM-Abwehr – sie hängt nicht von der Integrität von L2 (ARP/NDP) ab. Das Backend ist wählbar (Untermenü → „Backend“) zwischen „WireGuard (config file)“ und „Tailscale (Exit Node)“. RoamSwitch implementiert selbst keine Kryptografie; Nur das ausgewählte Backend ist scharfgeschaltet.
- (A) WireGuard-Backend. Steuert Homebrews
wireguard-tools(keine Berechtigung zur Apple Network Extension). Importieren Sie Ihr eigenes.conf; Der Helfer speichert es bei0600. Kill-Switch: pfblock drop allplus einpass quicknur fürlo, die Tunnelschnittstelle, den UDP-Handshake zu den angehefteten Endpunkt-IP(s), DHCP und ICMP. Ein nicht vollständiger TunnelAllowedIPslöst eine Split-Tunnel-Warnung aus. - (B) Tailscale-Backend (1.8.0+). Für Benutzer, die Tailscale bereits ausführen. RoamSwitch führt nicht aus, führt
tailscale upaus / meldet sich an / installiert es – es liesttailscale status --jsonund führttailscale set --exit-node=<node>aus. Die eigenständige CLI (brew install tailscale) wird empfohlen – der App Store (GUI)-Build kann nicht von außerhalb der App (Sandbox) gesteuert werden; Damit wählen Sie den Ausgangsknoten in der Tailscale-App aus und RoamSwitch zeigt nur den Status an. Ein Ausgangsknoten ist erforderlich (leitet den gesamten Datenverkehr durch den Tunnel); Es wird automatisch deaktiviert, wenn es offline geht oder nicht erreichbar ist. - ___ Der Datenverkehr sinkt für ca. 5–10 s und die Schutzstufe wird in diesem Fenster nicht herabgestuft.
- Der Tailscale-Kill-Switch ist standardmäßig deaktiviert (Opt-in). Der Exit-Knoten tunnelt bereits alles; der pf
block drop all(erlaubt nur CGNAT/MagicDNS/STUN/DERP/DHCP/ICMP/DNS) ist für fortgeschrittene Benutzer und ist „lecksicher, nicht lecksicher“ (DNS ist erlaubt, daher bleibttailscaledbestehen). - Automatisch / bei Lizenzverlust. Kommt bei nicht vertrauenswürdigen Netzwerken hoch, bei vertrauenswürdigen herunter; wird aufgehoben, wenn die Pro-Lizenz erlischt.
§6
Wenn Sie eine Verbindung zu einem Netzwerk herstellen, das Sie nicht registriert haben, wird der Schalter „Schutzstufe“ nicht verwendet pf. Es ändert lediglich die Standardeinstellungen des Betriebssystems auf eine Weise, die später rückgängig gemacht werden kann.
| Betrieb | Umsetzung | Privileg | Wie es wiederhergestellt wird |
|---|---|---|---|
| Firewall + Stealth-Modus EIN | socketfilterfw --setblockall on / --setstealthmode on | root (Helfer) | off, wenn Sie zu einem sicheren Netzwerk zurückkehren |
| Stoppen Sie SSH/SMB/Bildschirmfreigabe | launchctl unload -w | root (Helfer) | Zeichnet nur diejenigen auf, die ausgeführt wurden, als sie gestoppt wurden, und load -w bei der Rückkehr (mit SSH auch gekoppelt an /usr/sbin/systemsetup -setremotelogin on für garantierte moderne macOS-Wiederherstellung) |
| Deaktivieren Sie AirDrop | Standardmäßig wird com.apple.sharingd DiscoverableMode geschrieben | Benutzer (die App selbst) | Speichert den vorherigen Wert und schreibt ihn bei Rückkehr zurück |
Dabei handelt es sich nicht um einen neuen Blockierungsmechanismus, den RoamSwitch hinzufügt – es handelt sich lediglich um das Umschalten der Betriebssystemeinstellungen. Wenn löscht man die App, stoppt nur noch die netzwerkabhängige Umschaltung; das letzte Betriebssystem Die angewendeten Einstellungen bleiben unverändert. Nichts bleibt gesperrt, aber wenn Sie sich irren möchten Stellen Sie es auf der sicheren Seite in einem vertrauenswürdigen Netzwerk wieder auf „Offen“, bevor Sie es deinstallieren.
§7
Was bleibt auf dem Mac
| Daten | Standort | Inhalt |
|---|---|---|
| Lizenztoken | Schlüsselbund com.tetsuharu.RoamSwitch.license | Ein Ed25519-signiertes Token. kSecAttrAccessibleAfterFirstUnlock |
| App-Einstellungen / Schutz ein-aus | UserDefaults suite com.tetsuharu.RoamSwitch | Registrierungen vertrauenswürdiger Netzwerke, Schutzrichtlinien, Ausschlusslisten usw |
| PF-Zustand | /Library/Application Support/ | Der Air-Gap-Zeitstempel, JSON der geschützten Ports, der VPN-Kill-Switch-Status, die temporäre Datei für den angewendeten Regelsatz |
| Link Guard-Bedrohungsfeed | ~/Library/Application Support/ | Die heruntergeladene Phishing-/Betrugsdomänenliste (oder der gebündelte Seed der App, falls noch nicht abgerufen). Die Feed-Version befindet sich in UserDefaults |
| Von Link Guard verwalteter Abschnitt | /etc/hosts | Ein durch # BEGIN RoamSwitch link guard … # END begrenzter, abgegrenzter Abschnitt, der blockierte Domänen auf 0.0.0.0 auf Null setzt. Wird entfernt, wenn der Modus „Aus“ ist (§5) |
| ARP/NDP-Sicherungsstifte | /Library/Application Support/ | Der IP → MAC-Satz wurde durch die präventive Sperre (§5) zu permanent gemacht. Beim Entsperren gelöscht |
| WireGuard-Konfiguration | /Library/Application Support/0600) | Der .conf, den der Benutzer importiert hat. Der Endpunkt-Hostname wird auch in UserDefaults gespeichert (die App löst ihn auf). |
| Geräte-Fallback-UUID | Benutzerstandards | Ein Zufallswert, der nur generiert wird, wenn IOKit keine UUID zurückgibt (§9) |
| Protokolle | os.Logger / NSLog | Einheitliche Protokollierung. Es wird nichts nach außen gesendet |
Verkehr, der die Maschine verlässt (die vollständige Liste)
Es gibt nirgendwo einen Code, der Diagnoseergebnisse, Portinformationen, URLs usw. sammelt und sendet Protokolle. Es sind kein Analyse-SDK und kein Crash-Reporter-SDK enthalten. Die einzige externe Bibliothek ist Sparkle (Updates). Was an das Netzwerk gesendet wird, sind diese acht, und das ist alles (die sechste nur, wenn der Benutzer ein VPN konfiguriert, die siebte nur, wenn die RoamSwitch Sensor-Kopplung aktiviert ist).
| Verbindung | Ziel | Wenn es passiert | Was wird gesendet? |
|---|---|---|---|
| Lizenzaktivierung/-deaktivierung | lafine.net /api/v1/license/* |
Nur wenn der Benutzer einen Lizenzschlüssel eingibt oder Pro deaktiviert | Lizenzschlüssel, Geräte-Hash, Hostname, App-Version. Persönliche Daten werden beim Kauf von Stripe verarbeitet. Die App kommt damit nicht zurecht |
| Update-Prüfung | lafine.net /updates/appcast.xml |
Sparkle, alle 24 Stunden und beim Start | Eine HTTP-Anfrage (ein Standard-UA und eine Standardversion). Das heruntergeladene Element wird durch die EdDSA-Signatur überprüft (§10). |
| Link Guard-Bedrohungsfeed | lafine.net /updates/v1/{manifest, feed/ |
Wenn Link Guard (§5) aktiviert und „Auto-Update“ aktiviert ist, alle 24 Stunden (und beim Start). Wenn Sie „Automatische Aktualisierung“ deaktivieren, wird dieser Pfad entfernt | Nur ein GET. Keine Abfragezeichenfolge, keine Cookies, nichts, was die Maschine identifiziert. Eine receive-only signierte statische Datei; Das Manifest und der Feedtext werden beide mit Ed25519 verifiziert. Der Signaturschlüssel ist dem Feed gewidmet – ein separater Schlüssel vom App-Update SUPublicEDKey (ein Leck ist also auf „eine fehlerhafte Sperrliste“ beschränkt). |
| Aktualisierungsdaten für Paket-CVE-Abgleich / aktive Schwachstellenprüfung | lafine.net /updates/v1/manifest |
Alle 24 Stunden ab dem App-Start (immer — für die Aktualisierungsdaten des Paket-CVE-Abgleichs gibt es keinen Ausschalter, da es sich um rein empfangene Daten handelt, die nur für einen lokalen Listenabgleich verwendet werden; die CVE-Map der aktiven Schwachstellenprüfung wird nur abgerufen, wenn „Aktive Schwachstellenprüfung (aktive Erreichbarkeitsprüfung)“ aktiviert ist) | Nur ein GET. Keine Query-String, keine Cookies, nichts, was das Gerät identifiziert. Eine nur empfangene, signierte statische Datei; Manifest und Feed-Inhalt werden beide mit Ed25519 verifiziert. Verwendet denselben Schlüssel und dasselbe Manifest wie der Link-Guard-Bedrohungsfeed, aber die tatsächlich abgerufenen Feeds selbst (bekannte CVE-Maps für Homebrew/npm/PyPI/crates.io/RubyGems/Packagist/Go/Maven, die CVE-Map der aktiven Schwachstellenprüfung sowie die Liste beliebter npm-Paketnamen für die Typosquatting-Erkennung) sind separate Dateien |
| Aktualisierung der ClamAV-Virendefinition | Offizielle ClamAV-Spiegel | Nur wenn der Benutzer ClamAV installiert hat und die Scanfunktion verwendet. Es startet freshclam |
Ein standardmäßiger ClamAV-Definitionsabruf. Es enthält keine von RoamSwitch abgeleiteten Informationen |
| VPN-Tunnel (§5, WireGuard / Tailscale) | Der vom Benutzer konfigurierte WireGuard-Endpunkt oder die Tailscale-Kontrollebene (Tailscale, Inc.) | Nur wenn der Benutzer den VPN-Tunnel eingerichtet hat (Pro, standardmäßig deaktiviert) und sich einem nicht vertrauenswürdigen Netzwerk anschließt. Bei WireGuard wird der Endpunkt-Hostname einmal über DNS aufgelöst, bevor der Tunnel aufgebaut wird. Bei Tailscale handelt es sich um Kontrollebenen-Datenverkehr, der von der bereits vom Benutzer installierten und angemeldeten tailscaled-Instanz abgewickelt wird — RoamSwitch selbst liest nur tailscale status --json und führt tailscale set --exit-node= aus | Bei WireGuard: der WireGuard-Handshake (UDP) und der Verkehr im Tunnel. Das Ziel ist der eigene VPN-Server des Benutzers, der Inhalt ist der eigene Datenverkehr des Benutzers. Bei Tailscale: Datenverkehr zwischen dem eigenen Tailscale-Konto des Benutzers und der Kontrollebene von Tailscale, Inc. (Geräteregistrierung, DERP-Relay usw., gemäß der eigenen Datenschutzrichtlinie von Tailscale). In beiden Fällen fügt RoamSwitch keine Kennung und keine Diagnosedaten hinzu |
| RoamSwitch Sensor-Kopplung & Audit-Verbindung | RoamSwitch Sensor im selben LAN (vom Benutzer selbst eingerichtete Hardware, die unter einer festen IP läuft) | Nur wenn „RoamSwitch Sensor-Kopplung“ (Pro, standardmäßig deaktiviert) aktiviert ist und Sie die IP-Adresse und den Pairing-Code des Sensors zum Pairen eingeben (ein einmaliger Austausch). Nach dem Pairing verbindet es sich erneut mit derselben festen IP nur, wenn Sie „Audit von Sensor anfordern“ drücken, sowie beim automatischen Ergebnisabruf (beginnend 5 Minuten nach der Anfrage, danach alle 5 Minuten, bis zu 5 Versuche) | Beim Pairing werden Hostname und Ed25519-Public-Key dieses Endpunkts gesendet, und der von Sensor ausgestellte Public Key wird empfangen. Audit-Anfragen und Ergebnisabruf werden mit einer Ed25519-Signatur authentifiziert — gesendet werden nur signierte Anfragen. Diagnoseergebnisse und Portinformationen werden ausschließlich als Antwort von Sensor empfangen; Dateiinhalte werden nie gesendet. Einziges Ziel ist die feste IP-Adresse des vom Benutzer selbst eingerichteten Sensors — sie erreicht nie einen externen Server, auch nicht lafine.net. Gegenseitiges Vertrauen entsteht ausschließlich durch den expliziten Austausch des Pairing-Codes |
| Checkout-Seite | Streifenkasse | Nur wenn der Benutzer den Kaufen-Button drückt (er öffnet sich im Browser) | — (eine Browsernavigation) |
„Zero Telemetry“ bedeutet hier, dass es keine Telemetrie gibt, die Nutzungsdaten sammelt und versendet bzw Diagnoseergebnisse. Das bedeutet nicht, dass überhaupt kein Netzwerkverkehr stattfindet. Die acht Wege im Die obige Tabelle existiert. Aber jeder von ihnen ist entweder etwas, das der Benutzer initiiert, oder ein signaturverifiziert, receive-only Abruf und die Diagnoseergebnisse, Ports, URLs, und Dateiinhalte auf dem Mac verlassen ihn nie.
Der Link Guard-Bedrohungsfeed (Zeile 3) fügt zusätzlich zu „Es sendet nichts“ hinzu: „Es ruft Updates ab“. Verteidigung. Die beiden werden getrennt gehalten; Das Linux-Whitepaper §1.1 spaltet ebenfalls „Zero Telemetrie“ von „Nur Updates empfangen“. Schalten Sie „Auto-Update“ aus und Link Guard wird auf dem Paket ausgeführt Daten (ca. 60.000 Phishing-/Betrugsdomänen) plus Offline-Homograph-Erkennung, und dieser Pfad tritt nicht auf.
Der VPN-Tunnel (Zeile 6) tritt nur auf, wenn der Benutzer seinen eigenen WireGuard-Server konfiguriert oder das Tailscale-Backend wählt und bereits mit seinem eigenen Tailscale-Konto bei tailscaled angemeldet ist; Ziel und Inhalt unterliegen der Kontrolle des Benutzers. RoamSwitch baut lediglich den Tunnel auf, hält den Kill-Switch oder liest den Tailscale-Status aus, um den Exit-Node zu wechseln — es fügt keine Kennung und keine Nutzungsdaten hinzu. Ohne konfiguriertes VPN existiert dieser Pfad nicht.
Die RoamSwitch Sensor-Kopplung (Zeile 7) ist eine reine Pro-Funktion, die standardmäßig deaktiviert ist; dieser Pfad existiert nicht, solange Sie ihn nicht aktivieren. Das Pairing selbst entsteht nur durch eine explizite Aktion — die Eingabe des vom Sensor-Betreiber ausgestellten Pairing-Codes zusammen mit der festen IP-Adresse des Sensors. Jede weitere Kommunikation (Audit-Anfragen, Ergebnisabruf) geht ebenfalls an dieselbe, dem Benutzer gehörende feste IP und wird mit einer Ed25519-Signatur authentifiziert. Sie erreicht nie einen externen Server, auch nicht lafine.net. Diagnoseergebnisse oder Dateiinhalte werden nie gesendet, außer als Antwort von Sensor selbst.
Das In-App-Blatt „Link-Sicherheitsprüfung“ sendet eine HEAD-Anfrage an die Ziel-URL, um sie anzuzeigen
wo eine verkürzte URL landet (folgende Weiterleitungen zu privaten oder lokalen Adressen werden durch die gestoppt
v1.4.5 SSRF-Abschwächung). Beim MCP audit_url_safety handelt es sich dagegen um eine Offline-Analyse
Das wird sofort abgeschlossen und sendet die URL nirgendwo hin (§8).
Dies wird nicht nur behauptet. Am 29.08.2026 wurde eine laufende 1.4.7-Installation mit geprüft
tcpdump + Zuordnung pro Prozess (nettop / lsof / a gefiltert
pktap Erfassung) + LuLu, über einen Zeitraum von ca. 2 Stunden, mit festgelegter Sicherheitsstufe
Maximaler Lockdown und die Appcast-Prüfung erzwungen.
Ergebnis: Kein ausgehender Fluss zugeordnet zu RoamSwitch, RoamSwitchHelper,
oder RoamSwitchMCPServer außer der Appcast-Prüfung an lafine.net; der MCP
Die einzigen Sockets des Servers waren zu localhost; Die Berechtigungs-Dumps sind leer.
Vollständige Beschreibung und ein Skript, das jeder ausführen kann, um es zu reproduzieren: audit/RESULTS-2026-08-29.md
Diese Messung stammt aus 1.4.7, vor dem Link Guard-Bedrohungsfeed (1.7.2, Zeile 3) und dem VPN
Tunnel (1.7.6, Reihe 5). Bei der Prüfung ab Version 1.7.2 wird two „receive-only“ angezeigt
GETs bis lafine.net (der Appcast und der Bedrohungs-Feed); wenn ein VPN
konfiguriert ist, sehen Sie auch UDP zum eigenen WireGuard-Server des Benutzers (Ziel und Inhalt).
unter der Kontrolle des Benutzers). Wenn „Auto-Update: Aus“ aktiviert ist und kein VPN konfiguriert ist, verschwindet alles.
§8
RoamSwitchMCPServer ist ein eigenständiges Befehlszeilentool, das unter gebündelt ist
RoamSwitch.app/Contents/MacOS/. Ein MCP-Client wie Claude Desktop oder Claude Code
startet ihn als Unterprozess und kommuniziert mit ihm über stdio (durch Zeilenumbrüche getrennter JSON-RPC).
2.0). Das offizielle SDK lässt sich nicht auf dem macOS SDK dieser Maschine aufbauen, ist also so
von Hand auf Foundations JSONSerialization implementiert.
Was das Design einschränkt
- Es ist schreibgeschützt. Es gibt einfach keine API zum Ändern der Sicherheitsstufe, zum Isolieren eines Ports oder zum Auswerfen eines Geräts. Dies wurde in Version 1 nicht vergessen – es wurde bewusst weggelassen. Wenn externer Code (hier ein LLM) den Schutzstatus eines Sicherheitstools neu schreiben würde, würde dies das Vertrauen aller Benutzer zerstören.
- Es öffnet keinen Socket. Es registriert weder einen Dienst noch lauscht es. Es liest eine Zeile von stdin, gibt eine Zeile von stdout zurück und dann beendet der Client den Prozess.
- Es wird nichts gesendet. Alle Diagnosen sind im Mac abgeschlossen.
- Es liest Einstellungen aus einer anderen Domäne.
UserDefaults(suiteName: "com.tetsuharu.RoamSwitch")liest die Domäne der App explizit (ihre eigene Bundle-ID-Domäne ist leer). Es liest nur; es schreibt nicht.
Die Werkzeuge, die es offenlegt
| Werkzeug | Was es zurückgibt | Verkehr |
|---|---|---|
| get_security_report | Eine 18-Punkte-Prüfung (FileVault / SIP / Gatekeeper / Auto-Update / XProtect / Firewall / Wi-Fi-Verschlüsselung / ARP / Gateway-ARP-Sperre / SSH-Konfigurationsprüfung / Sudo-NOPASSWD-Prüfung / offengelegte Ports / Guard-Konfiguration und mehr) mit einer Bewertung und Korrekturempfehlungen pro Punkt | Nur lokal |
| get_exposed_ports | Eine Liste der lauschenden TCP-Ports. Für alles, was über „localhost“ hinaus verfügbar gemacht wird, wird ein Querverweis auf eine Datenbank mit bekanntermaßen gefährlichen Diensten erstellt und CORS/Header mit einer HTTP-Prüfung auf 127.0.0.1:port (lokal, geschlossen) überprüft. | Nur die Sonde auf 127.0.0.1 |
| get_guard_status | Der Ein-/Aus-Status der Pro-Auto-Response-Guards (Portanomalie/ARP/USB/Bluetooth/Web+Mail-Download/DNS-Bedrohungsschutz), die aktuelle Schutzstufe und der Status des vertrauenswürdigen Netzwerks | Nur lokal |
| audit_url_safety | Eine Beurteilung einer URL für Phishing/Homograph (Unicode-Spoofing)/Marken-Subdomain-Spoofing/Hochrisiko-TLD/Klartext-HTTP. Es ist synchron und vollständig offline (analyzeURL; es folgt keinen Weiterleitungen) | Keine |
| get_app_help | Eine Volltextsuche in der gebündelten Wissensdatenbank (Funktionsspezifikationen/Einstellungen/Fehlerbehebung/Erklärungen zu Benachrichtigungsmeldungen) | Keine |
| audit_secrets | Erkennt offengelegte API-Schlüssel und private Schlüssel in Text, Dateien oder Verzeichnisbäumen (Treffer werden in der Ausgabe maskiert). | Keine |
| audit_security_logs | Wertet aktuelle Sicherheitslogs aus (Mac: fehlgeschlagene Sudo-Authentifizierungen, SSH-Brute-Force-Versuche, Gatekeeper-Blockaden, XProtect-Erkennungen usw. / Linux: fehlgeschlagene Sudo-Authentifizierungen, SSH-Brute-Force, Firewall-Blockaden, AppArmor-Ablehnungen, ClamAV-Erkennungen usw.), maskiert automatisch sensible Daten wie API-Schlüssel und Tokens und liefert zusätzlich Ergebnisse der Log-Template-basierten Erkennung neuer Muster sowie der Häufigkeitsanomalie-Erkennung (statistische Spitzen). | Nur lokal |
| run_active_vuln_scan | Nicht-destruktive, ausschließlich auf 127.0.0.1 beschränkte aktive Schwachstellenprüfung. Das einzige Tool, das das Netzwerk nutzt — standardmäßig deaktiviert und nur nach Zustimmung in den Einstellungen. | Nur 127.0.0.1 (standardmäßig aus) |
| run_package_cve_scan | Gleicht installierte Pakete (Mac: Homebrew / Linux: dpkg, dnf, zypper, pacman) mit einer lokalen CVE-Zuordnung ab. Es findet überhaupt keine Netzwerkkommunikation statt. | Keine |
| run_package_cve_scan_languages | Gleicht Abhängigkeits-Lockfiles für npm, PyPI, crates.io, RubyGems, Packagist, Go und Maven mit derselben lokalen CVE-Zuordnung ab. Es findet überhaupt keine Netzwerkkommunikation statt. | Keine |
| get_quarantine_status | Liefert den Inhalt des Malware-Quarantäne-Tresors: ursprünglicher Pfad, erkannter Bedrohungsname, Zeitpunkt und Größe. | Nur lokal |
| get_canary_status | Liefert den Zustand der Ransomware-Köderdateien (Canary) sowie bis zu die 50 jüngsten erkannten Vorfälle. | Nur lokal |
| get_notification_history | Gibt den Verlauf der von RoamSwitch gesendeten Benachrichtigungen (Sicherheitslog-Auffälligkeiten, ClickFix-Erkennungen u. a.) der letzten 7 Tage zurück, neueste zuerst. | Nur lokal |
| get_port_anomaly_incidents | Liefert den Baseline-Zustand des Port-Anomalie-Schutzes, aktuell automatisch blockierte Ports und bis zu die 50 jüngsten Vorfälle. Die Antwort weist ausdrücklich darauf hin, dass die aktuell blockierten Ports eine zeitstempellose Momentaufnahme sind, die sich von der zeitgestempelten Vorfallshistorie unterscheidet. | Nur lokal |
| get_runtime_threat_status | Gibt an, ob dieser Mac wegen eines XProtect-Malwarefunds per Air-Gap isoliert wurde, und nennt den auslösenden Vorfall. Bei aktivem Air-Gap zuerst prüfen. | Nur lokal |
Im Feld instructions in der Antwort initialize heißt es auch deutlich:
„Die Sicherheitsstufe kann nicht geändert, Ports nicht isoliert oder Geräte ausgeworfen werden“, um die Funktion zu kommunizieren
Grenze zum clientseitigen LLM. Die MCP-Ressourcen (roamswitch://docs/*) sind schreibgeschützt
Auch Markdown-Dokumente.
Im September 2026 führten wir mit diesem MCP-Server eine Übung durch, bei der ein lokales LLM (Qwen 3.8 27B) einen Vorfall untersuchen sollte, und entdeckten dabei einen Fall, in dem das Modell die von get_port_anomaly_incidents zurückgegebenen Daten falsch interpretierte und eine unabhängige, ältere Portsperrung in die Erzählung eines laufenden Vorfalls einflocht. Die Ursache: Eine zeitstempellose Momentaufnahme der aktuell blockierten Ports wurde in derselben Antwort wie die echte, zeitgestempelte Vorfallshistorie zurückgegeben, ohne dass etwas zwischen beiden unterschied. Die Korrektur fügt explizite Hinweisfelder hinzu, die kennzeichnen, welches Feld eine Zeitleiste und welches eine Momentaufnahme ist. Beim erneuten Ausführen desselben Szenarios mit demselben Modell zitierte dieses den Wortlaut des Hinweises und lehnte es ausdrücklich ab, den alten Port dem aktuellen Vorfall zuzuordnen. Die Wirksamkeit der Korrektur wurde sowohl durch einen automatisierten Regressionstest als auch durch die erneute Ausführung mit dem echten lokalen LLM bestätigt.
Der Quellcode dieses Servers und der von ihm verwendeten Erkennungslogik (ARP-Überwachung, Port-Scanning,
Port-Audit, der 18-Punkte-Gesundheitscheck, URL-Sicherheitsanalyse) wird unter
github.com/lafine1211/roamswitch-mcp veröffentlicht (MIT, ein Spiegel des ausgelieferten Codes, pro
Release getaggt). Sie können direkt im Code prüfen, ob er schreibgeschützt ist, was er an das LLM übergibt und
dass er nichts nach außen sendet. Er enthält nicht den privilegierten Helfer, die pf-Steuerung, die eingreifenden
Wachen oder die Lizenzierung – diese verbleiben im App-Repository.
Die Tests sind ebenfalls im Lieferumfang enthalten – gespiegelte Unit-Tests, gegnerische Eingabetests und Mutations-Fuzzing.
Wird von swift test ausgeführt und in CI überprüft. Fuzzing erlebte einen unbeaufsichtigten Absturz
(JSONSerialization Stapelüberläufe bei einem tief verschachtelten JSON-Objekt); es ist mit fixiert
eine Verschachtelungstiefenprüfung vor dem Parser und aufgezeichnet in SECURITY_TESTING.md.
§9
Der Token
- Es verwendet Ed25519 (Curve25519-Signaturen). Der öffentliche Schlüssel ist in die App eingebettet (
LicenseVerifier.embeddedPublicKeyBase64). Der entsprechende private Schlüssel existiert nur im Lizenz-Backend (eine Firebase Functions-Umgebungsvariable) und befindet sich nicht im Repository. - ___
- Es ist so konzipiert, dass es beim Schließen fehlschlägt. Wenn der eingebettete Schlüssel fehlt oder fehlerhaft ist oder wenn die Signatur oder der kanonische JSON nicht erstellt werden kann, wird das Ergebnis nicht „verifiziert“ – es wird
invalidSignaturezurückgegeben.
Gerätebindung
device_hash = SHA-256( "RoamSwitch-LifetimeSalt-v1" : lowercase(IOPlatformUUID) )
Die reine Hardware-UUID wird nicht an den Server gesendet. In dem seltenen Fall, dass IOKit kein a zurückgibt
UUID, es greift auf eine zufällige UUID zurück, die in UserDefaults gespeichert ist. Zum Zeitpunkt der Überprüfung, wenn die
Der device_hash des Tokens stimmt nicht mit dem aktuellen Geräte-Hash überein. Das Ergebnis ist
deviceMismatch.
Es funktioniert offline
Alles, was validateSavedLicense() beim Start tut, ist, das Token aus dem Schlüsselbund zu lesen und
Überprüfen Sie es lokal mit dem eingebetteten öffentlichen Schlüssel. Es stellt keine Verbindung zum Netzwerk her. Wenn die
Wenn der Lizenzserver jemals heruntergefahren wird, funktionieren die Pro-Funktionen weiterhin auf einem Mac, der bereits aktiviert ist.
Der Server wird nur für eine erneute Aktivierung und für eine explizite Deaktivierung kontaktiert. Die
Deaktivierungsbenachrichtigung an den Server ist die beste Lösung – selbst wenn sie fehlschlägt, erfolgt die lokale Deaktivierung
wird immer abgeschlossen.
Der Standardwert ist ein einmaliger (lebenslanger) Kauf; expires_at wird nur überprüft, wenn
is_lifetime ist falsch. Die Anzahl der Sitzplätze wird nach Stufe ausgedrückt – 2 für Personal Pro, 5 für
Team.
§10
Unterzeichnung und Beglaubigung (scripts/release.sh)
- Führen Sie
xcodebuild archiveaus (Release, manuelle Signatur, Entwickler-ID-Anwendung). - Export mit
-exportArchivealsmethod: developer-id. - Führen Sie nach
notarytool submit --waitstapler staplein der .app aus. - Überprüfen Sie mit
spctl -a -t exec -vv. - Nach dem Heften erneut komprimieren, um das Sparkle-Update-Artefakt zu erstellen (damit das Beglaubigungsticket enthalten ist und es ohne Gatekeeper-Warnung offline ausgeführt wird).
- Erstellen Sie die DMG, beglaubigen Sie die DMG, beglaubigen Sie sie, heften Sie sie ebenfalls zusammen und überprüfen Sie sie mit
stapler validate.
Updates (Sparkle 2.9.6)
| Schlüssel | Wert |
|---|---|
| SUFeedURL | https://lafine.net/updates/appcast.xml |
| SUPublicEDKey | CNxzwijMzMCJzliId76Yl88S/9np6t/xg/zQ9YbYzHs= |
| SUEnableAutomaticChecks | wahr |
| SUScheduledCheckInterval | 86400 |
Bevor ein Update angewendet wird, wird die im Appcast aufgeführte EdDSA-Signatur überprüft
gegen den in der App eingebetteten SUPublicEDKey. Der private Signaturschlüssel existiert nur in
die Build-Umgebung. Der Appcast wird über HTTPS bereitgestellt. Delta-Updates sind signaturverifiziert
auf die gleiche Weise.
Die Tatsache, dass die URL appcast.xml öffentlich ist, ist an sich keine Schwäche. Sein Inhalt
sind nur Versionsnummern, Versionshinweise, Download-URLs, Dateigrößen und die EdDSA-Signatur von
Jeder Build – nichts Geheimnisvolles. Der Vertrauensanker ist nicht „Ist der Appcast während der Übertragung authentisch“, sondern
Überprüfen der Signatur des Artefakts mit dem in die App integrierten öffentlichen Schlüssel. Ein Angreifer, der es kann
Ersetzen Sie den Appcast vollständig (MITM, DNS-Hijacking, Kompromittierung des Webhosts) und können Sie immer noch nicht pushen
ein bösartiges Update ohne den Signaturschlüssel. Gatekeeper (Entwickler-ID und Beglaubigung) ist ein
zweites Tor.
Es bleiben zwei Risiken bestehen. Einer davon ist, dass Updates nicht eintreffen, weil der Host ausgefallen ist oder der Appcast defekt ist (Es passiert keine fehlerhafte Installation – Sie werden einfach nicht aktualisiert). Der andere ist ein Freeze-Angriff ein Sicherheitsupdate bewusst zurückhält. Sparkle 2.x lehnt Downgrades und Wiederholungen durch Überprüfung ab Versionsbestellung, aber ein vollständiger Schutz gegen das Einfrieren erfordert einen dedizierten Update-Server mit Ablauf. Das steht auf unserer Liste.
§11
Womit RoamSwitch umgehen soll
- Sondierungen und Angriffe von einem Angreifer im selben LAN oder von einem kompromittierten IoT-Gerät aus. Die Reaktion erfolgt durch Tarnung, Überwachung exponierter Ports und Isolierung von außen.
- Exposition in einem Netzwerk, dem Sie nicht vertrauen. Es stoppt automatisch die Freigabe von Diensten und AirDrop.
- ___ All Pro, standardmäßig deaktiviert (Erkennung ist standardmäßig aktiviert).
- Auffinden von Entwicklungsservern und Datenbanken (Redis, MongoDB, Elasticsearch usw.), die auf
0.0.0.0ohne Authentifizierung verfügbar sind, und Blockieren dieser von außen. - Erkennen Sie Ransomware-ähnliche, nicht autorisierte Verschlüsselungsaktivitäten frühzeitig und stoppen Sie den gesamten Datenverkehr (nicht auf Signaturen angewiesen).
- BadUSB und physischer Tastatur-Genehmigungsschutz (
USBKeyboardGuard, CGEventTap + IOKit), der Tastenanschläge von nicht zugelassenen USB-Tastaturen/-Kabeln (Rubber Ducky, O.MG-Kabel usw.) abfängt und verwirft, um automatisierte Befehlsinjektionsangriffe zu verhindern. - Eine Genehmigungsaufforderung für unbekannten USB-Speicher (ein nicht erkanntes Laufwerk wird schreibgeschützt gehalten und nicht sofort ausgeworfen) und ein automatischer ClamAV-Scan auf angeschlossenem Speicher (optional).
- Erkennt sensible API-Schlüssel (OpenAI, Anthropic, GitHub, AWS usw.) in der Zwischenablage vollständig lokal und verhindert ein versehentliches Einfügen in ein Webformular oder einen KI-Chat, bevor es passiert.
- Erkennt und warnt vor gefährlichen KI-Modelldateien im Pickle-Format (Risiko der Ausführung beliebigen Codes), die von Hugging Face oder aus dem Web heruntergeladen wurden.
- Die branchenübliche EICAR-Testsignatur und gut dokumentierte Reverse-Shell-Einzeiler in heruntergeladenen Dateien (
StaticSignatureScanner, ab 1.8.7, funktioniert auch ohne installiertes ClamAV). - Ein neu registrierter LaunchAgent/LaunchDaemon, der einen rohen Skriptinterpreter direkt aufruft (
PersistenceMonitorGuard, ab 1.8.7). - „ClickFix“-artige Angriffe, bei denen ein gefälschter Warnbildschirm dazu verleitet, selbst einen Befehl im Terminal auszuführen – erkannt anhand bekannter bösartiger Befehlsmuster im Shell-Verlauf, was einen Notfall-Air-Gap auslöst (
ClickFixGuard, ab 1.8.7, Pro, standardmäßig deaktiviert). - Erkennt und benachrichtigt sofort, wenn ein Container mit einer Container-Escape-riskanten Docker-Konfiguration startet, etwa im
--privileged-Modus oder mit/var/run/docker.sockeingebunden (DockerEventGuard, ab 1.8.9, Pro, standardmäßig deaktiviert, nur Benachrichtigung). - Geheimnis-/API-Schlüssel-Leck-Audit, das jetzt nicht nur eingefügten Text, sondern auch rekursives Scannen ganzer Ordner abdeckt (
SecretLeakAuditor, Ordner-Scan hinzugefügt in 1.8.9, vollständig auf dem Gerät).
Was wir bewusst nicht getan haben
- Es ist kein Ersatz für Antivirus. ClamAV und XProtect werden als Hilfsmittel verwendet; RoamSwitch allein ist kein Allzweck-Malware-Detektor.
- Wir haben uns entschieden, das EndpointSecurity-Entitlement nicht zu beantragen. Die Genehmigungsraten für ein individuelles Apple-Developer-Konto sind niedrig, und jede Funktion, die tatsächlich eine Pre-Exec-Blockierung benötigt (z. B.
ES_EVENT_TYPE_AUTH_EXEC), wird als geschlossenes Nicht-Ziel behandelt, solange das gilt. Anstelle der Ausführung selbst zu stoppen, haben wir ohne das Entitlement das praktisch Machbare als nachträgliche Erkennung umgesetzt: statische Signaturerkennung, Autostart-Überwachung und ClickFix-Abwehr. - Es ist keine Garantie. Es handelt sich um eine Ebene in einem Defense-in-Depth-Stack, nicht um etwas, das „Ransomware vollständig verhindert“. Auch Marketingtexte werden unter dieser Prämisse überprüft.
- Ein bereits kompromittiertes Root oder Kernel kann nicht geschützt werden. Wenn ein Angreifer bereits über Root verfügt, kann er auch die Pf-Regeln des Helfers entfernen.
- ___ Der VPN-Tunnel (§5) ist die Antwort, wenn Sie diese Annahme nicht annehmen möchten: Selbst wenn L2 vergiftet ist, werden die Inhalte verschlüsselt und der Kill-Switch verhindert, dass Klartext durchsickert. Es ist kein Ersatz für Unternehmens-DHCP-Snooping oder dynamische ARP-Inspektion.
- Es wird kein VPN-Server bereitgestellt. Die Tunnelfunktion verwendet eine WireGuard-Konfiguration, die der Benutzer bereitstellt; RoamSwitch wird kein VPN-Anbieter.
Angriffsfläche, die durch die Installation von RoamSwitch hinzugefügt wird
| Angriffsfläche | Wie es enthalten ist |
|---|---|
Ein LaunchDaemon, der als Root ausgeführt wird, und sein Mach-Dienst (com.tetsuharu.RoamSwitch.Helper) |
Die Bedienoberfläche ist auf HelperProtocol festgelegt (Tabelle §3). Es gibt keinen Kanal für beliebige Befehle. Verbindungen werden durch eine Codesignaturanforderung unter Verwendung von audit_token autorisiert. |
Wenn RoamSwitch.app selbst kompromittiert wird, gehen alle Vorgänge des Helfers an den Angreifer über |
Die gehärtete Laufzeit ist aktiviert und die App erhält keine unnötigen Berechtigungen. Ausgehender Datenverkehr ist auf die acht oben genannten Pfade beschränkt (der sechste nur, wenn der Benutzer ein VPN konfiguriert, der siebte nur, wenn die RoamSwitch Sensor-Kopplung aktiviert ist). Wir planen, dies von einem Dritten überprüfen zu lassen. |
| Die systembinären Pfade, die der Helfer erzeugt | Absolute Pfade wie /sbin/pfctl werden direkt angegeben, ohne Abhängigkeit von PATH. Auch die Argumente sind fest codiert (abgesehen von Portnummern, DNS-Strings, den ARP-IP/MAC-Paaren und der VPN-Endpunkt-IP – die alle formatvalidiert sind). |
Link Guard wird neu geschrieben /etc/hosts (setLinkGuardSinkhole) |
Schreibvorgänge sind auf einen durch Trennzeichen getrennten verwalteten Abschnitt beschränkt. Zeilen außerhalb davon bleiben wörtlich erhalten. Domänen werden normalisiert und validiert, IPs und Junk werden gelöscht, die Liste ist auf 60.000 begrenzt und die Datei wird über eine temporäre Datei + atomares Ersetzen geschrieben. Es werden nur eindeutige Fälle (Bedrohungs-Feed-Auflistung oder Markennamen-Homographie) ausgewertet, die von einer ausschließlich lokalen Urteilsmaschine entschieden werden. Die Sperrliste selbst stammt aus dem signaturverifizierten Bedrohungs-Feed (Feed-dedizierter Schlüssel). |
Bedrohungs-Feed-Abruf von Link Guard (ein täglicher Empfang nur GET) |
Ein Abruf einer statischen Datei ohne Abfragezeichenfolge und ohne Bezeichner. Das Manifest und der Feed-Text sind beide Ed25519-verifiziert und ein Abruf, der die Überprüfung nicht besteht, wird verworfen (kein Fallback auf nicht signierte Daten). „Auto-Update: Aus“ entfernt den Pfad vollständig. |
Präventiver ARP/NDP-Pin (lockGatewayARP) |
Nur die Neighbor-Cache-Einträge für „das Gateway, den IPv6-Router, On-Link-DNS“ werden angeheftet. Die übergebenen IP/MAC-Paare werden formatvalidiert und mit dem Anforderungssatz abgeglichen (es werden niemals Einträge von selbst hinzugefügt). Es läuft nur in nicht vertrauenswürdigen Netzwerken; Pro, standardmäßig deaktiviert. Das Trust-on-First-Use-Limit ist in §11 „Was wir beschlossen haben, nicht zu tun“ angegeben. |
VPN-Tunnel (wireGuardImport/Up/…, Homebrew wireguard-tools) |
Der .conf wird vom Helfer unter 0600 gespeichert. Die Endpunkt-IP wird von der App aufgelöst und an den Helfer übergeben (der Helfer löst niemals einen beliebigen Hostnamen auf). Ziel und Inhalt des Tunnels unterliegen der Kontrolle des Benutzers. Die Funktion ist deaktiviert, wenn wireguard-tools nicht installiert ist. Pro, standardmäßig deaktiviert. Der Kill-Switch geht über PFRulesetCoordinator (§4). |
| Der MCP-Server übergibt den Systemstatus an einen LLM (einen verwirrten Stellvertreter). | Es ist schreibgeschützt, es ist keine Schreib-API implementiert. URL-Prüfungen sind offline. Auf die Einstellungsdomäne wird schreibgeschützt verwiesen. |
| Kaperung des Update-Pfads | EdDSA-Signaturüberprüfung (SUPublicEDKey) sowie ein gebündeltes Beglaubigungsticket. Der Appcast erfolgt über HTTPS. |
Anhang A
Alles, was in diesem Dokument angegeben ist, kann mit den folgenden Befehlen anhand des verteilten Artefakts überprüft werden.
Unterzeichnung und notarielle Beglaubigung
# Developer-ID-Signatur und Team-ID
codesign -dvvv /Applications/RoamSwitch.app 2>&1 | grep -E 'Authority|TeamIdentifier|flags'
# Ob das Notarization-Ticket gestapelt (staple) ist
stapler validate /Applications/RoamSwitch.app
spctl -a -t exec -vvv /Applications/RoamSwitch.app
# Signatur des mitgelieferten Helpers / MCP-Servers
codesign -dvvv /Applications/RoamSwitch.app/Contents/MacOS/RoamSwitchHelper
codesign -dvvv /Applications/RoamSwitch.app/Contents/MacOS/RoamSwitchMCPServer
Berechtigungen (keine Berechtigung zum Senden über das Netzwerk)
codesign -d --entitlements :- /Applications/RoamSwitch.app/Contents/MacOS/RoamSwitchHelper
codesign -d --entitlements :- /Applications/RoamSwitch.app/Contents/MacOS/RoamSwitchMCPServer
# → Ein leeres Entitlements-Dictionary. Keine app-sandbox-/network-client-Schlüssel.
Den Verkehr messen
# tcpdump parallel mitlaufen lassen, um zu bestätigen, dass bei normaler Nutzung kein Traffic entsteht
sudo tcpdump -i any -n 'host not 127.0.0.1' and 'not port 53'
# Kein Traffic außer Lizenzaktivierung, Update-Prüfungen und ClamAV-Definitionsupdates
Eine strengere, automatisierte Prüfung mit Zuordnung pro Prozess finden Sie unter
rs-zerotel-audit.sh in roamswitch-support/audit/
Verteidigungsarchitektur und Penetrationstests (5 Verteidigungsgrenzen)
# Verifiziert automatisch in einem Durchgang: XPC-Autorisierungsgrenze, pf-Air-Gap-Priorität, Port-Expositionserkennung, MCP-Nur-Lesen, ARP-Überwachung
git clone https://github.com/lafine1211/roamswitch-support
cd roamswitch-support/audit
./rs-defense-audit.sh all
Live-Penetrationstests und mehrschichtiges Verteidigungsaudit von einer macOS-VM: RESULTS-DEFENSE-2026-08-30
Überprüfung des DNS-Bedrohungsschutzes (Quad9-Malware und C2-Blockierung).
# Abfrage der offiziellen Quad9-Testdomain zur Verifikation der Bedrohungsblockierung
nslookup test.dns9.quad9.net
# → Sollte ** server can't find test.dns9.quad9.net: NXDOMAIN liefern, d. h. die Namensauflösung ist blockiert
Überprüfung des Web- und Mail-Download-Guards (ClamAV-Echtzeiterkennung).
# Die harmlose branchenübliche Antiviren-Teststring (EICAR) in Downloads erstellen
echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > ~/Downloads/eicar_test.com
# → Die FSEvents-Erkennung löst einen sofortigen ClamAV-Scan aus; es erscheint keine Benachrichtigung, "EICAR-Testsignatur erkannt (harmlos)"
# wird nur im Benachrichtigungsverlauf erfasst. Da EICAR eine branchenübliche Testdatei und keine echte Bedrohung ist,
# wird sie weder unter Quarantäne gestellt noch blockiert, und die Datei bleibt unverändert (nur eine echte Malware-Probe wird in den Quarantäne-Manager verschoben).
Überprüfung der statischen Signaturerkennung (ohne EndpointSecurity)
## Ein Lehrbuch-Reverse-Shell-Einzeiler aus öffentlichen Offensive-Security-Referenzen (harmlos, wird nie ausgeführt)
echo 'bash -i >& /dev/tcp/127.0.0.1/4444 0>&1' > ~/Downloads/rs_test.sh
## → Auch ohne installiertes ClamAV erscheint eine Benachrichtigung „Gefährliche Download-Datei unter Quarantäne gestellt“, und die Datei wird
## sofort in den Quarantäne-Ordner verschoben (unabhängig von ClamAVs Signaturdatenbank)
Überprüfung der neuen Autostart-Überwachung (PersistenceMonitorGuard)
# Einen harmlosen LaunchAgent anlegen, der einen rohen Skriptinterpreter direkt aufruft (wird nie tatsächlich ausgeführt)
cat > ~/Library/LaunchAgents/com.example.selfcheck.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>com.example.selfcheck</string>
<key>ProgramArguments</key><array><string>/bin/bash</string><string>-c</string><string>echo hi</string></array>
</dict></plist>
EOF
# → Innerhalb weniger Sekunden sollte eine Benachrichtigung „Neue Autostart-Registrierung erkannt“ erscheinen. Danach entfernen:
rm ~/Library/LaunchAgents/com.example.selfcheck.plist
Link Guard (blockiert Phishing-Verbindungen)
# Ob der verwaltete Abschnitt vorhanden ist (2 BEGIN/END-Zeilen bei Pro + Standardmodus)
sudo grep -c 'RoamSwitch link guard' /etc/hosts
# Inhalt des Abschnitts und Anzahl der Sinkholes
sudo sed -n '/BEGIN RoamSwitch link guard/,/END RoamSwitch link guard/p' /etc/hosts | head -4
sudo sed -n '/BEGIN RoamSwitch link guard/,/END RoamSwitch link guard/p' /etc/hosts | grep -c '^0\.0\.0\.0'
# Ob es tatsächlich blockiert wird (mit einem Eintrag aus dem Abschnitt geprüft; harmlos)
D=$(sudo sed -n '/BEGIN RoamSwitch/,/END RoamSwitch/p' /etc/hosts | awk '/^0\.0\.0\.0/{print $2; exit}')
dscacheutil -q host -a name "$D" # → ip_address: 0.0.0.0 (Namensauflösung ist blockiert)
# Auch bestätigen, dass der obige Abschnitt über Menü → „Linkschutz" → „Aus" verschwindet
Der Nur-Empfangs-Bedrohungs-Feed (signaturverifiziert)
# Öffentlicher Feed und Manifest (von jedem abrufbar und überprüfbar)
curl -s https://lafine.net/updates/v1/manifest # version/generated/threatfeed{...}
curl -sI https://lafine.net/updates/v1/manifest.sig # → text/plain
# Es wird nur ein GET ohne Query-String, Cookies oder Identifikatoren gesendet. Mit parallel laufendem tcpdump überprüfbar.
Präventive ARP/NDP-Sperre (§5, 1.7.5+)
# Mit aktivierter präventiver Sperre mit einem nicht vertrauenswürdigen Netzwerk verbinden → auf permanent-Einträge prüfen
arp -an | grep -i permanent # Gateway und andere IPs werden als (permanent) aufgeführt
ndp -an | grep -i 'P ' # IPv6-Seite (P = permanent)
sudo cat "/Library/Application Support/RoamSwitch/gateway_arp_lock.json" # Die Menge der aktuell fixierten IP→MAC-Zuordnungen
# Sperre über das Menü aufheben → das Obige sollte verschwinden
VPN-Tunnel + Kill-Switch (§5, 1.7.6+)
# Setzt wireguard-tools voraus (über Homebrew)
brew list wireguard-tools >/dev/null && echo "wireguard-tools: OK"
# VPN in einem nicht vertrauenswürdigen Netzwerk aktivieren → bevor der Tunnel steht, befindet sich pf im Kill-Switch-Zustand
sudo pfctl -sr | grep -E 'block drop all|pass .*(utun|udp)' # block drop all + eine begrenzte Menge an pass-quick-Regeln
# Nachdem der Tunnel steht (wg-quick up bereits ausgeführt)
wg show # handshake / transfer läuft
route -n get default | grep interface # → utunN (die Standardroute ist der Tunnel)
# VPN deaktivieren → Kill-Switch wird aufgehoben, pf kehrt in den vorherigen Zustand zurück
Der privilegierte Helfer selbst
# Der registrierte LaunchDaemon
sudo launchctl print system/com.tetsuharu.RoamSwitch.Helper
# Die aktuell geladenen pf-Regeln (tatsächlicher Zustand von Air-Gap / Port-Guard)
sudo pfctl -sr
# Das Zustandsverzeichnis des Helpers
ls -la "/Library/Application Support/RoamSwitch/"
Die Antwort des MCP-Servers (Offline-Prüfung)
BIN=/Applications/RoamSwitch.app/Contents/MacOS/RoamSwitchMCPServer
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | "$BIN"
# Liefert serverInfo und die Definitionen der 5 Tools zurück. Es entsteht keine Netzwerkverbindung.
Die Quelle und Tests des MCP-Servers
git clone https://github.com/lafine1211/roamswitch-mcp
cd roamswitch-mcp
swift build -c release # Derselbe Quellcode wie in der ausgelieferten Binärdatei
swift test # Unit-, Adversarial-Input-, stdio- und Mutations-Fuzzing-Tests
# Testinhalte und gefundene Probleme siehe SECURITY_TESTING.md
Gerätekennung
# Der Rohwert, der an das Token gebunden ist (gesendet wird nur ein gesalzener SHA-256)
ioreg -d2 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformUUID/{print $4}'