MCP-Prozessbäume: Unerwartete Kindprozesse finden
Erfahre, wie du MCP-Prozessbäume unter macOS untersuchst, unerwartete Kindprozesse erkennst, Sockets zurückverfolgst und riskante lokale Agentenausführung begrenzt.

Lokale MCP-Server machen es leicht, eine saubere Protokollgrenze mit einer sauberen Ausführungsgrenze zu verwechseln. Ein Agent sendet einen Tool-Aufruf, der Server liefert ein Ergebnis zurück und das Protokoll wirkt abgeschlossen. Währenddessen kann der Server eine Shell, einen Paket-Runner, eine Laufzeitumgebung, einen Compiler, einen SSH-Client oder einen in ein Projektverzeichnis heruntergeladenen Helfer starten. Der Prozessbaum zeigt, wohin die Arbeit tatsächlich gelangt ist.
Bei der Prüfung behandle ich einen MCP-Server nicht als einen einzelnen Prozess. Ich betrachte den Server und jeden von ihm gestarteten Nachkommen als eine gemeinsame Ausführungsfamilie, bis diese Nachkommen beendet sind. Diese Regel findet Fehler, die Tool-Protokolle regelmäßig übersehen: ein Argument, das eine Shell dazu bringt, etwas anderes aufzurufen, einen über PATH ausgewählten Helfer, einen Kindprozess mit weitergereichtem API-Token oder ein langlebiges Programm, das einen Socket öffnet, nachdem die ursprüngliche Anfrage scheinbar abgeschlossen war.
Aus einem Tool-Aufruf können viele lokale Programme werden
Ein MCP-Server startet aus ganz normalen Gründen häufig Kindprozesse. Ein Repository-Tool kann git aufrufen, ein Code-Tool einen Formatter, ein Infrastruktur-Tool ssh und ein paketbewusster Server zunächst eine Laufzeitumgebung und anschließend ein Paketprogramm. Die Existenz eines Kindprozesses beweist noch keinen Einbruch.
Wichtig ist die Unterscheidung zwischen einem erwarteten Helfer und einem nicht erklärten Ausführungspfad. Ein erwarteter Helfer hat eine dokumentierte Aufgabe, einen bekannten Speicherort der ausführbaren Datei, zum Aufruf passende Argumente und eine zur Aufgabe passende Lebensdauer. Ein nicht erklärter Pfad verletzt eine dieser Annahmen. Er kann trotzdem harmlos sein, braucht aber eine Erklärung, bevor er Zugangsdaten, Dateisystemzugriff oder Netzwerkzugriff erhält.
Eine übergeordnete PID allein reicht nicht aus. Die Erstellung von Prozessen unter Unix erzeugt eine Abstammungslinie, aber keinen Sicherheitsvertrag. Ein Nachkomme übernimmt häufig die Benutzer-ID, das aktuelle Arbeitsverzeichnis, Umgebungsvariablen, Ressourcenlimits und manchmal geöffnete Dateideskriptoren des Elternprozesses. Was genau übernommen wird, hängt davon ab, wie der Elternprozess den Kindprozess startet. Meist erhält der Kindprozess jedoch genügend Kontext, um an den entscheidenden Stellen wie der Elternprozess zu handeln.
Darum ist «Der MCP-Server ruft nur einen Befehl auf» keine brauchbare Zusicherung. tool-server kann /bin/sh -c starten, das wiederum node startet, das ein Paket-Skript startet, das anschließend curl ausführt. Wer nur tool-server dokumentiert, hält den uninteressantesten Teil der Kette fest.
Das POSIX-Modell von exec erklärt die kritische Stelle. Ein Prozess kann sein Prozessabbild durch ein anderes Programm ersetzen, ohne die PID zu ändern. Eine einfache Prozessliste vor und nach einer Anfrage kann daher einen kurzlebigen Zwischenprozess übersehen. Wenn die Aufgabe sensibel genug ist, brauchst du sowohl eine Baumansicht als auch Ereignisdaten.
Erstelle eine Baseline, bevor du einen Agenten arbeiten lässt
Eine Baseline ist eine Aufzeichnung der Kindprozesse, die dein Server während einer unauffälligen, freigegebenen Anfrage startet. Sie liefert einen Vergleichspunkt für spätere Beobachtungen. Ohne sie wirkt jeder Interpreter verdächtig und jede Warnung führt zu einer Diskussion darüber, was normal sein könnte.
Starte den Server manuell in einer eigenen Terminal-Sitzung. Notiere sofort seine PID und erfasse anschließend die Prozesstabelle mit vollständigen Befehlszeilen:
ps -axo pid,ppid,user,etime,stat,command > mcp-process-baseline.txt
Die Spalten sind wichtig. PID identifiziert den Prozess, PPID seinen direkten Elternprozess, USER das Konto, dem er gehört, ETIME die bisherige Laufzeit und STAT kann einen angehaltenen oder Zombieprozess sichtbar machen. Wie vollständig COMMAND ist, hängt davon ab, was das Betriebssystem melden kann. Trotzdem findest du hier zuerst Shell-Wrapper, temporäre Pfade und unerwartete Optionen.
Unter macOS liefert ps keine überall verfügbare Baumansicht. Direkte Kindprozesse kannst du mit pgrep auflisten:
pgrep -P 48192 -alf
Ersetze 48192 durch die PID des Servers. Eine typische Ausgabe sieht so aus:
48207 /usr/bin/python3 /Users/me/tools/format_request.py
48211 /usr/bin/ssh -o BatchMode=yes build.example
Wiederhole den Befehl für jede Kind-PID, bis keine neuen Nachkommen mehr auftauchen. Das klingt mühsam und ist es bei einer einmaligen Prüfung auch. Gleichzeitig zwingt es dich, die tatsächliche Kette zu sehen, statt einem Diagramm in der Dokumentation zu vertrauen. Wenn du pstree über deinen normalen Paketprozess installierst, liefert pstree -p 48192 eine besser lesbare Momentaufnahme. Mache das Anzeigeprogramm nicht zum Bestandteil deiner Sicherheitszusage. Es spart lediglich Tipparbeit.
Führe einige erwartete Anfragen aus: eine schreibgeschützte Repository-Abfrage, die Formatter-Aktion und eine SSH-Aktion, falls der Server eine solche unterstützt. Speichere für jede Anfrage eine Momentaufnahme. Notiere die Pfade der Programme, normale Argumente, übliche Arbeitsverzeichnisse und ob Helfer beendet werden. Ein Compiler, der bei einer Build-Anfrage auftaucht, kann erwartbar sein. Derselbe Compiler bei einer Anfrage zum Zusammenfassen einer Textdatei wäre eine Abweichung.
Erstelle die Baseline nicht anhand eines Entwicklungs-Checkouts voller veränderlicher Wrapper und Paket-Skripte und erkläre das Ergebnis dann als vertrauenswürdig. Diese Einrichtung zeigt nur, was deine aktuelle Maschine zufällig ausführt. Sie sagt nicht, was der Server ausführen dürfen sollte. Notiere die erwarteten Pfade der ausführbaren Dateien separat.
Shell-Wrapper verbergen den Prozess, den du prüfen wolltest
Shell-Aufrufe sind beliebt, weil sich dynamische Befehle damit einfach zusammensetzen lassen. Sie verwandeln aber eine Argumentgrenze in Text, und Text kann durch Anführungszeichen, Expansion, Globbing, Umleitungen, Befehlsersetzung und Shell-Funktionen eine Bedeutung erhalten. Der Autor des Servers möchte vielleicht einen Formatter ausführen, während der tatsächliche Kindprozess eine Shell startet, die entscheidet, was der Formatter-Befehl bedeutet.
Das ist eine häufige Ursache irreführender Prüfungen. Jemand sieht in der Quelltext-Implementierung den Namen einer erlaubten Binärdatei und nimmt an, dass genau diese Datei ausgeführt wird. Die Laufzeitdaten zeigen jedoch /bin/sh -c ..., und die Shell löst den Rest erst später auf. Das sind unterschiedliche Aussagen.
Vergleiche diese beiden Muster in einer Server-Implementierung:
spawn("/usr/bin/git", ["status", "--short"], {
cwd: repositoryPath,
shell: false
});
exec(`git -C ${repositoryPath} status --short`);
Beim ersten Muster bleiben Programm und Argumente getrennt. repositoryPath muss weiterhin validiert werden, aber Shell-Parsing wird vermieden. Beim zweiten Muster kann repositoryPath die Befehlsgrammatik verändern, wenn das Quoting fehlerhaft ist. Außerdem kommt ein Shell-Prozess in den Baum, der möglicherweise weitere Prozesse startet, bevor der endgültige Befehl ausgeführt wird.
Akzeptiere «Wir bereinigen die Eingabe» nicht als Ersatz für einen Argumentvektor. Bereinigungsregeln veralten, sobald Optionen hinzukommen. Entwickler ergänzen eine Funktion, erlauben Leerzeichen, fügen ein bedingtes Flag hinzu und schon beschreibt der alte Filter die Befehlsgrammatik nicht mehr. Ein explizites Programm mit einem Array von Argumenten macht die Grenze im Code und in den Audit-Daten sichtbar.
Wenn ein Server tatsächlich Shell-Syntax benötigt, begrenze sie. Verwende ein festes Skript außerhalb des vom Agenten beschreibbaren Arbeitsbereichs, übergib Daten über Positionsparameter und notiere Pfad und Digest des Skripts in deinen Deployment-Unterlagen. Setze niemals eine Shell-Zeile aus Tool-Eingaben zusammen und erkläre den resultierenden Kindprozess nur deshalb als erwartet, weil er aus deinem Quellbaum stammt.
Der schwierige Fall ist Paketwerkzeug. Befehle wie Paket-Runner führen häufig Lifecycle-Skripte aus, die das Projekt bereitstellt. Ein Server, der einen Paketbefehl in einem vom Agenten bearbeiteten Checkout ausführt, kann daher Befehle starten, die der Agent in der Projektkonfiguration abgelegt hat. Das ist kein Fehler des Paketmanagers. Es ist eine Ausführungsentscheidung, die du getroffen hast, indem du ein veränderliches Repository als Quelle vertrauenswürdiger Anweisungen behandelst.
PATH und Arbeitsverzeichnisse verändern die Bedeutung eines Befehls
git ist keine Identität einer ausführbaren Datei. Es ist eine Suchanfrage. Der Prozess löst sie durch die Suche in PATH auf. Ein vom Agenten kontrolliertes Repository kann diese Suche beeinflussen, wenn der Server lokale Projektverzeichnisse oder geerbte Shell-Konfiguration einbezieht. Eine Datei namens git in einem solchen Verzeichnis kann vor /usr/bin/git ausgeführt werden.
Prüfe die Umgebung, die ein Prozess erhält, nicht nur den im Tool-Protokoll angezeigten Befehl. Für einen Prozess, der dir gehört, kannst du unter vielen macOS-Versionen Umgebungsinformationen über ps abfragen, wobei die Verfügbarkeit variiert. Praktisch ist es, wenn der Server beim Start bewusst eine kurze, bereinigte Umgebung protokolliert: PATH, HOME, TMPDIR, das aktuelle Verzeichnis und die Pfade festgelegter Programme. Schreibe keine Zugriffstoken, Sitzungscookies oder vollständigen Umgebungsdumps in ein gemeinsames Projekt-Log.
Löse sensible Befehle in absolute Pfade auf. Das ist keine Formsache, wenn ein Agent den Arbeitsbaum verändern kann. Zum Beispiel:
/usr/bin/git -C /Users/me/work/repo status --short
/usr/bin/ssh -o BatchMode=yes -o IdentitiesOnly=yes host.example
Absolute Pfade beseitigen ein Suchproblem. Sie machen das Zielprogramm aber nicht automatisch sicher. git kann unter bestimmten Konfigurationen Hooks oder externe Diff-Programme aufrufen. SSH kann Konfigurationen laden und Helfer starten. Das Arbeitsverzeichnis steuert außerdem relative Dateizugriffe, Projektkonfiguration und temporäre Ausgaben. Notiere es bei der Prüfung eines Kindprozesses.
Mit lsof kannst du das aktuelle Verzeichnis und die geöffneten Dateien eines Prozesses untersuchen:
lsof -nP -p 48207 | sed -n '1,35p'
Achte in der Spalte für Dateideskriptoren auf cwd, unter txt auf das ausführbare Programm und auf Dateien im Projekt, in temporären Verzeichnissen oder an Orten für Zugangsdaten. lsof liefert eine Momentaufnahme. Ein kurzlebiger Kindprozess kann auftauchen und verschwinden, bevor du den Befehl ausführst. Die Ausgabe erkennt aber zuverlässig einen Server, der einen Helfer unauffällig am Leben hält.
Ein Kindprozess, der aus /private/var/folders/... gestartet wird, verdient mehr Aufmerksamkeit als einer aus einem verwalteten Anwendungsverzeichnis, besonders wenn sein Name wie ein normales Dienstprogramm aussieht. Temporäre Orte sind legitime Speicherplätze für Build-Ergebnisse. Sie sind aber auch geeignete Verstecke zwischen anderen vergänglichen Dateien. Frage, welche Komponente ihn erstellt hat und warum diese Komponente dort eine ausführbare Datei benötigte.
Netzwerkaktivität muss zur angeforderten Aktion passen
Ein lokaler Kindprozess kann die Grenze der MCP-Anfrage überschreiten, auch wenn er keine verdächtige Datei schreibt. Ein Formatter sollte normalerweise keine ausgehende Verbindung öffnen. Ein SSH-Helfer sollte sich mit dem angeforderten Host verbinden und anschließend beendet werden. Eine Paketinstallation darf Registries kontaktieren, aber diese Aktion muss ausdrücklich geprüft werden, weil sie neuen Code herunterladen und ausführen kann.
Untersuche die Sockets des Servers und jedes Kindprozesses, der lange genug läuft:
lsof -nP -i -p 48211
Die Ausgabe enthält normalerweise Protokoll, lokale Adresse, entfernte Adresse und Verbindungsstatus. Die Optionen -nP verhindern Namens- und Dienstauflösungen. So bleibt die Ausgabe wörtlich und während der Untersuchung entsteht kein zusätzlicher Resolver-Verkehr. Ein Eintrag mit LISTEN bedeutet, dass der Prozess lokale oder Netzwerkverbindungen annimmt. Ein Eintrag mit ESTABLISHED zeigt einen aktiven Kommunikationspartner. Vergleiche jeden Eintrag mit der Aktion, die ihn ausgelöst hat.
Reagiere nicht auf jeden Prozess mit einer Netzwerkbibliothek über. Manche Entwicklerwerkzeuge prüfen Update-Dienste, den Zertifikatsstatus oder Metadaten zu Abhängigkeiten. Ich sehe dieses Verhalten trotzdem als Grund, den Server stärker zu begrenzen. Ein Tool-Aufruf, der angeblich lokalen Code liest, sollte keinen nicht deklarierten ausgehenden Kanal eröffnen, nur weil ein Helfer ihn praktisch findet.
Trenne die Injektion von Zugangsdaten von der Prozessbeobachtung. Die Prozessprüfung kann zeigen, dass curl ausgeführt wurde. Sie kann nicht garantieren, dass ein als Umgebungsvariable übergebenes Token nie kopiert, protokolliert oder an einen Enkelprozess vererbt wurde. Häufig lautet die Empfehlung: «Lege den API-Schlüssel einfach für einen Befehl in die Umgebung des Kindprozesses.» Das ist beliebt, weil es einfach ist und in einer Demo funktioniert. Für agentengesteuerte Aktionen ist es falsch, denn ein Kindprozess kann seine Umgebung ausgeben, sie weiterreichen oder nach der sichtbaren Rückkehr des Befehls am Leben bleiben.
Sallyport setzt für seine HTTP- und SSH-Kanäle eine andere Grenze: Der Agent erhält weder die gespeicherten API- noch die SSH-Zugangsdaten, sondern die App führt die Aktion selbst aus. Das macht die Prüfung lokaler MCP-Kindprozesse nicht überflüssig, verhindert aber, dass jeder Helferprozess zu einem möglichen Träger des Geheimnisses wird.
Ein Fehler beginnt oft mit einem vernünftigen Komfort-Wrapper
Betrachte einen lokalen Repository-MCP-Server mit einem Tool run_test. Der Autor möchte einen flexiblen Befehl ermöglichen. Deshalb wechselt der Handler in das Repository und führt über einen Paketbefehl ein vom Projekt definiertes Testskript aus. Der Agent darf den Checkout bearbeiten, weil das Bearbeiten von Code zu seiner Aufgabe gehört.
Der Agent ändert das Projektskript als Teil eines vorgeschlagenen Fixes. Der Server führt das Testtool aus. Der Paketbefehl startet eine Shell, diese startet die Laufzeitumgebung, die das veränderte Skript ausführt. Das Skript startet einen Hintergrundhelfer und leitet dessen Ausgabe in eine temporäre Datei um. Das Tool meldet «Tests bestanden», weil der Vordergrundbefehl erfolgreich beendet wurde.
Für diese Abfolge ist kein exotischer Exploit nötig. Das Problem ist, dass der Server veränderliche Projektmetadaten als freigegebene ausführbare Konfiguration behandelt hat. Ein Prüfer sieht möglicherweise nur die ursprüngliche MCP-Anfrage und das Erfolgsergebnis. Der Prozessbaum zeigt die entscheidende Geschichte:
mcp-repo-server(48192)
package-runner(48230)
sh(48233)
runtime(48234)
test-script(48240)
helper(48247)
Der Hintergrundhelfer ist der wichtigste Untersuchungspunkt. Prüfe seine vollständige Befehlszeile, den Pfad der ausführbaren Datei, die Elternkette, das Arbeitsverzeichnis, geöffnete Dateien, Sockets und die Startzeit. Prüfe, ob er nach dem Ende der Serversitzung weiterläuft. Untersuche anschließend die Projektänderung, aus der das Skript stammt. Stelle dies nicht allein als Fehler des Agenten dar. Der Server hat die Ausführung veränderlicher Anweisungen angeboten und das Ergebnis als Test bezeichnet.
Die Behebung hängt vom gewünschten Produktverhalten ab. Ein vorsichtiger Server kann eine festgelegte Testdatei mit festen Argumenten ausführen. Wenn projektspezifische Skripte erforderlich sind, behandle Skriptdatei und Paketmetadaten als ausführbare Eingaben: Zeige sie zur Freigabe an, führe sie in einer eingeschränkten Umgebung aus und verbiete Hintergrundprozesse, soweit möglich. Mindestens muss der Server jeden von ihm gestarteten Nachkommen melden, auch wenn dieser länger lebt als der Tool-Aufruf.
Der entscheidende Unterschied liegt zwischen einem Agenten, der den Server bittet, einen bekannten Testbefehl auszuführen, und einem Agenten, der vor der Ausführung verändert, was dieser Testbefehl bedeutet. Im MCP-Protokoll können beide als run_test erscheinen. Ihr Risiko ist sehr unterschiedlich.
Beobachte Prozessstarts, nicht nur Momentaufnahmen
Momentaufnahmen beantworten die Frage «Was ist jetzt am Leben?». Sie beantworten nicht «Was lief 200 Millisekunden und wurde dann beendet?». Beobachte bei verdächtigen oder sensiblen Tool-Aufrufen die Prozessstarts während der Anfrage.
macOS bietet Endpoint Security für Sicherheitsprodukte mit der erforderlichen Berechtigung. Gewöhnliche lokale Werkzeuge können diese Voraussetzung aber nicht annehmen. Entwirf keine Lösung, die von privilegierter Ereigniserfassung abhängt, wenn du diese Fähigkeit nicht tatsächlich auslieferst und betreibst. Für eine Untersuchung durch Entwickler kannst du einen kontrollierten Wrapper um das Programm verwenden und Start- sowie Enddaten sammeln oder den Server unter einem in deiner Umgebung verfügbaren Prozessmonitor ausführen.
Ein einfacher Wrapper macht die Ausführung sichtbar, wenn du den Befehlsweg des Servers kontrollierst:
#!/bin/sh
printf '%s pid=%s ppid=%s cwd=%s argv=%s\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$$" "$PPID" "$(pwd)" "$*" \
>> "$HOME/.local/state/mcp-exec.log"
exec /usr/bin/git "$@"
Dieses Artefakt zeichnet die Identität des Wrappers auf und ersetzt den Wrapper anschließend mit git, indem es exec verwendet. Der Wrapper bleibt dadurch nicht als irrelevanter Elternprozess bestehen. Das erfasst nicht jeden Nachkommen, den git erzeugen könnte, und der Wrapper darf keine Geheimnisse in Argumenten erhalten. Verwende ihn, um eine kontrollierte Annahme zu prüfen, nicht als vollständiges Auditsystem.
Bei einem bestehenden Prozess kann dtruss unter macOS Systemaufrufe sichtbar machen. Dafür sind jedoch häufig erhöhte Berechtigungen nötig, und die Ausgabe ist umfangreich. Es ist ein Untersuchungswerkzeug, keine Routineüberwachung. Beginne mit dem Prozessbaum, lsof und den Anwendungslogs. Verwende Systemaufruf-Überwachung erst, wenn du eine konkrete Frage beantworten musst, etwa ob ein Kindprozess einen weiteren Pfad ausgeführt oder eine Verbindung zu einem Socket hergestellt hat.
Verwende UTC-Zeitstempel und erfasse bei jedem Ereignis die Eltern-PID. Eine Befehlszeile ohne Elternprozess und Zeitangabe ist schwache Evidenz. PID-Werte werden nach dem Ende von Prozessen wiederverwendet. Eine spätere Momentaufnahme kann daher versehentlich einen neuen, unabhängigen Prozess einem alten Vorfall zuordnen. Die Startzeit aus ps oder deinem eigenen Ereignisprotokoll hilft, diesen Fehler zu vermeiden.
Eine Freigabe muss die ausführbare Grenze benennen
Eine Bestätigung wie «run_test ausführen?» liefert einem Menschen zu wenig Information. Die freigebende Person muss wissen, welcher signierte Prozess die Aktion initiiert hat, welches Programm ausgeführt wird, welches Arbeitsverzeichnis gilt, ob die Aktion das Netzwerk erreichen kann und ob sie weitere Programme starten darf. Eine vage Freigabekarte bringt Menschen dazu, vage Aktionen zu genehmigen.
Versuche nicht, das Problem zu lösen, indem du für jeden fork-Aufruf eine Freigabe verlangst. Das führt zu Freigabeermüdung. Nutzer bestätigen dann mechanisch oder schalten die Abfragen ab. Prüfe die wichtigen Übergänge: einen neuen Pfad zu einer ausführbaren Datei, den Wechsel von lokaler Arbeit zu Netzwerkzugriff, einen aus einer veränderlichen Projektdatei stammenden Befehl oder einen Kindprozess, der über die Anfrage hinaus bestehen bleibt.
Informationen zur Codesignatur liefern unter macOS ein nützliches Identitätssignal, aber übertreibe ihre Aussage nicht. Untersuche eine ausführbare Datei mit:
codesign -dv --verbose=4 /path/to/executable 2>&1 | sed -n '1,20p'
Der Befehl meldet die Signaturinstanz, falls eine vorhanden ist, und einen Fehler bei unsigniertem Code. Das zeigt, wer die von macOS untersuchte Datei signiert hat. Es sagt nicht, ob die Argumente sicher sind, ob die Konfiguration vertrauenswürdig ist oder ob das Programm anschließend ein unsigniertes Skript ausführt. Prüfe den Pfad des Kindprozesses und seinen Ausführungskontext separat.
Sallyports Sitzungsautorisierung stellt die Codesignatur des verbindenden Prozesses in den Mittelpunkt. Das hilft bei der Entscheidung, ob ein neuer Agentenprozess überhaupt handeln darf. Die Einstellung für Schlüssel pro Aufruf passt zu dem engeren Fall, in dem jede Verwendung einer bestimmten Zugangsdatenart eine menschliche Entscheidung erfordert. Keine dieser Kontrollen sollte dazu dienen, so zu tun, als erkläre die Freigabe eines Elternprozesses automatisch jeden Nachkommen, den er starten kann.
Formuliere Freigaben anhand ihrer Folgen. «Diese Anfrage führt /usr/bin/ssh unter deinem Konto aus und verbindet sich mit host.example» lässt sich prüfen. «Dieses Tool benötigt Zugriff» nicht. Wenn das Tool Projektskripte aufrufen kann, sage es ausdrücklich. Menschen können fundierte Entscheidungen treffen, wenn die Anfrage eine konkrete Form hat.
Begrenze den Server, bevor du den Kindprozess untersuchst
Wenn du einen unerwarteten Kindprozess findest, sichere zunächst genügend Beweise, um ihn zu erklären, und beende dann die gesamte Ausführungsfamilie. Beginne nicht damit, temporäre Dateien zu löschen oder den Rechner neu zu starten. Dadurch können der einzige Pfad, die Befehlszeile und die zeitlichen Beweise verschwinden.
Verwende bei einem noch laufenden Kindprozess diese Reihenfolge:
- Erfasse die
ps-Ausgabe für den Server, seinen Elternprozess und bekannte Nachkommen. Notiere PIDs, PPIDs, Laufzeit und vollständige Befehle. - Führe für den verdächtigen Prozess
lsof -nP -p <pid>undlsof -nP -i -p <pid>aus. Speichere die Ausgabe außerhalb des Agentenarbeitsbereichs. - Beende den verdächtigen Nachkommen mit
kill <pid>, wenn eine normale Beendigung sicher ist. Verwendekill -KILL <pid>nur, wenn er nicht beendet wird und die weitere Ausführung ein unvertretbares Risiko darstellt. - Stoppe den MCP-Server und widerrufe oder beende die Agentensitzung, die die Aktion ausgelöst hat. Prüfe auf Kindprozesse, die nach dem Ende des Servers einem anderen Elternprozess zugeordnet wurden.
- Untersuche die ausführbare Datei, die Quelle ihres Starts sowie Änderungen am Repository oder an der Konfiguration, die den Befehl erzeugt haben.
kill beendet nicht automatisch eine Prozessgruppe oder alle Kindprozesse. Ein in den Hintergrund verschobener Nachkomme kann weiterlaufen, nachdem sein direkter Elternprozess beendet wurde. Deshalb brauchst du den Baum vor der Bereinigung. Platziere Helfer bei einem kontrollierten Server in einer eigenen Prozessgruppe oder einem überwachten Bereich, damit der Server die Gruppe beim Ende der Sitzung beenden kann. Teste dieses Verhalten mit einem absichtlich in den Hintergrund verschobenen Helfer, bevor du dich darauf verlässt.
Prüfe außerdem die Persistenz. Mit launchctl print kannst du unter macOS geladene Launch-Dienste untersuchen, wenn du ein bestimmtes Label oder eine bestimmte Domain hast. Entlade nicht blind fremde Jobs, nur weil ihre Namen unbekannt wirken. Ordne einen verdächtigen Kindprozess zuerst anhand von Pfad der ausführbaren Datei, Startkonfiguration und Zeitstempeln zu. Ein Prozess, der nach einem sauberen Neustart oder dem frischen Start des Servers wiederkehrt, erfordert eine andere Untersuchung als einer, der nur in einer Testsitzung existierte.
Mache jede Aktion nachträglich erklärbar
Ein nützliches Audit-Log verbindet einen vom Nutzer freigegebenen Agentenprozess mit einer Anfrage, einer ausgeführten Aktion und ihrem beobachteten Ergebnis. Die Prozessdaten sollten den Pfad der ausführbaren Datei, die Elternkette, das Arbeitsverzeichnis, Start- und Endzeit sowie das Ziel von Netzwerkaktivität enthalten. Fehlt der Prozessbaum, kann das Protokoll nicht beantworten, ob ein Helfer zur Anfrage gehörte oder ein unabhängiger Prozess auf dem Rechner war.
Lege keine Geheimnisse in Audit-Daten ab, nur um sie vollständig zu machen. Speichere Namen oder undurchsichtige Kennungen von Zugangsdaten, die für Prüfer erforderlichen Metadaten und das bereinigte Ergebnis der Aktion. Ein Log, das die Herkunft durch das Kopieren von Bearer-Token klärt, hat einen zweiten Zugangsdaten-Speicher mit schwächeren Kontrollen geschaffen.
Manipulationsschutz ist nach einem Vorfall wichtig, weil lokale Logs von demselben Konto bearbeitet werden können, das den Befehl ausgeführt hat. Sallyport führt sowohl seine Sitzungs- als auch seine Aktivitätsjournale aus einem verschlüsselten, hashverketteten Audit-Log. sp audit verify prüft diese Kette offline, ohne einen Tresorschlüssel zu benötigen. Das liefert nützliche Belege für eine Agentenaktion. Die Prozessdaten brauchen aber weiterhin genügend Kontext, um zu erklären, was der lokale Server gestartet hat.
Nimm eine Prüfungsfrage in die Betriebsanleitung deines Servers auf: «Welches Programm hat diese Anfrage ausgeführt, und warum durfte dieses Programm in diesem Arbeitsverzeichnis vorhanden sein?» Wenn niemand die Frage anhand der Anfrageaufzeichnung und einer Prozessaufzeichnung beantworten kann, verkleinere den Funktionsumfang des Tools. Ein lokaler MCP-Server, der seine Nachkommen nicht erklären kann, hat mehr Befugnisse, als seine Betreiber sicher prüfen können.
FAQ
Wie sehe ich alle Kindprozesse, die ein MCP-Server unter macOS startet?
Beginne mit ps -axo pid,ppid,user,etime,command und suche die PID des Servers. Führe anschließend pgrep -P <pid> für direkte Kindprozesse aus und verwende pstree -p <pid>, falls du es installiert hast. Untersuche den gesamten Prozessbaum, denn der riskante Prozess ist häufig ein Enkelprozess, der von einer Shell gestartet wurde.
Übernehmen MCP-Kindprozesse die Berechtigungen des MCP-Servers?
Ein Kindprozess übernimmt die Berechtigungen seines Elternprozesses nicht auf einfache und allgemein gültige Weise. Häufig übernimmt er jedoch dieselbe Benutzeridentität, dasselbe Arbeitsverzeichnis, dieselben Umgebungsvariablen, geöffnete Dateideskriptoren und den Zugriff auf erreichbare lokale Dienste. Das kann bereits ausreichen, um Schaden anzurichten, wenn der Elternprozess weitreichenden Zugriff hat. Behandle jeden Nachkommen als Code innerhalb der operativen Grenze des Agenten.
Kann ein signierter MCP-Server einen unsignierten Kindprozess starten?
Ja. Ein signierter Elternprozess kann ein unsigniertes Skript, einen Interpreter oder eine Binärdatei aus einem beschreibbaren Verzeichnis starten. Erfasse die Signatur jedes relevanten Programms, nicht nur die des obersten Servers.
Wie prüfe ich, ob ein MCP-Kindprozess eine Netzwerkverbindung geöffnet hat?
Verwende lsof -nP -i -p <pid> für einen Prozess und wiederhole den Befehl für verdächtige Nachkommen. Die Ausgabe zeigt, ob ein Prozess auf Verbindungen wartet oder ausgehende Verbindungen besitzt, und vermeidet DNS-Abfragen, die den zeitlichen Ablauf verschleiern können. Eine Netzwerkverbindung ist nicht automatisch bösartig, muss aber zur Aufgabe und zum erwarteten Ziel passen.
Ist es sicher, wenn ein MCP-Server Befehle über eine Shell ausführt?
Eine Shell ist nicht automatisch unsicher, aber sie verbirgt viel von der Struktur, die eine Überprüfung ermöglicht. Sie kann Variablen expandieren, Befehlsersetzungen ausführen, Verzeichnisse wechseln und eine weitere Shell starten, bevor das eigentliche Programm ausgeführt wird. Verwende nach Möglichkeit ein festgelegtes Programm mit einer separaten Argumentliste. Wenn das nicht möglich ist, untersuche die von der Shell gestarteten Nachkommen.
Warum ist `PATH` für lokale MCP-Server riskant?
Nein. Über PATH kann ein anderes Programm ausgewählt werden als vom Autor beabsichtigt, besonders wenn ein vom Agenten kontrolliertes Verzeichnis an erster Stelle steht. Protokolliere den aufgelösten Pfad, verwende für sensible Befehle absolute Pfade und halte beschreibbare Projektverzeichnisse aus dem PATH des Dienstes heraus.
Was soll ich tun, wenn ein MCP-Server einen unerwarteten Prozess startet?
Beende den Nachkommen zuerst, wenn du ihn identifizieren kannst. Stoppe danach den MCP-Server und widerrufe die Agentensitzung, die ihn gestartet hat. Sichere vor der Bereinigung die Befehlszeile, die übergeordnete PID, geöffnete Dateien, Netzwerksockets und relevante Logs. Ein Neustart löscht häufig die Beweise, die zur Erklärung des Vorfalls nötig sind.
Kann die Überwachung von Prozessbäumen den Diebstahl von Zugangsdaten verhindern?
Die Prozessinspektion zeigt, was ausgeführt wurde und welcher Elternprozess es gestartet hat. Sie beweist weder, dass sich das Programm korrekt verhalten hat, noch schützt sie Zugangsdaten, die der Server bereits in die Umgebung des Kindprozesses gelegt hat. Verbinde die Inspektion mit eng begrenzten Zugangsdaten und einer Aktionsgrenze, die Geheimnisse aus agentengesteuerten Prozessen heraushält.
Wie oft sollte ich MCP-Prozessbäume prüfen?
Eine dauerhafte Überwachung jedes Prozesses erzeugt mehr Lärm als Sicherheit. Erfasse beim Start des Servers eine Baseline, untersuche jedes neue ausführbare Programm und jeden netzwerkfähigen Kindprozess und prüfe Abweichungen bei sensiblen Aufgaben. So erhalten Prüfer eine kurze Liste, die sie tatsächlich lesen können.
Was gilt in einer MCP-Umgebung als unerwarteter Kindprozess?
Ein Kindprozess ist erwartbar, wenn die dokumentierte Aufgabe des Servers einen Hilfsprozess wie eine Laufzeitumgebung, einen Compiler, einen Formatter oder einen SSH-Client benötigt. Verdächtig wird er, wenn Programm, Argumente, Arbeitsverzeichnis, Laufzeit oder Netzwerkverhalten nicht zu dieser Aufgabe passen. Der Kontext ist wichtiger als ein ungewohnter Prozessname.