Das Notebook brachte seinen eigenen MCP-Server mit
CVE-2026-75149 ist eine Code-Injection-Lücke in marimo: Ein Notebook ist eine einzige Python-Datei, diese Datei trägt ihre eigene Konfiguration in einem Kommentarblock, und bis Version 0.23.15 durfte diese Konfiguration einen MCP-Server deklarieren. Man öffnete das Notebook im Bearbeitungsmodus, und marimo startete den Befehl des Angreifers als Subprozess, bevor eine einzige Zelle lief. Der Patch sagt den Rest. Er streicht fünf ganze Kategorien von Umgebung, die ein heruntergeladenes Dokument setzen durfte, und zwar mit höherer Priorität als die Konfiguration des Betreibers. In Bromure Agentic Coding ist die Liste der Programme, die Ihr Agent starten darf, ein Panel auf dem Host, das beim Boot in die VM geschrieben wird, und nichts im Workspace kann etwas hinzufügen.
Ein Repository kann Ihren Coding-Agenten umkonfigurieren. Das wussten wir. Geändert hat sich die Größe des Dings, das das kann. Es ist jetzt eine einzige Datei, in Slack an Sie weitergeleitet, die Sie öffnen, um kurz reinzuschauen.
Jemand schickt Ihnen ein Notebook. Es ist ein marimo-Notebook, also eine ganz
gewöhnliche .py-Datei statt eines JSON-Bündels mit base64-Ausgaben darin, und
genau das ist der wesentliche Grund, warum Leute das Format mögen. Sie können es
lesen, Sie können es diffen, und Sie können es ohne Pre-Commit-Hook zum
Entfernen der Ausgaben in git legen.
Sie lesen es also, es sieht in Ordnung aus, und Sie öffnen es im Bearbeitungsmodus, um an einer Zelle herumzuspielen.
Ein Subprozess startet. Sie haben noch keine Zelle ausgeführt; das hat die Datei von sich aus verlangt, bevor das Notebook fertig geladen war.
Das ist CVE-2026-75149, veröffentlicht am 19. August und diese Woche von The Hacker News aufgegriffen. CVSS 8.7, CWE-94, Gregory Tan zugeschrieben, behoben in marimo 0.23.15. Das Advisory von VulnCheck bringt den Mechanismus in einen Satz: Das Notebook enthält einen präparierten MCP-Servereintrag, und es im Bearbeitungsmodus zu öffnen startet den Befehl dieses Eintrags als lokalen Subprozess.
Die Konfiguration stand in einem Kommentar
marimo-Notebooks tragen ihre eigenen Einstellungen über
PEP 723, die Python-Konvention für
Inline-Skript-Metadaten. Es ist eine kleine TOML-Tabelle, die in einem
Kommentarblock am Anfang der Datei lebt, und es gibt sie, damit ein Skript seine
Abhängigkeiten und seine Python-Version ohne eigene Projektdatei daneben
deklarieren kann. uv liest sie. marimo auch, und erweitert sie um eine
tool.marimo-Tabelle für die Notebook-Konfiguration.
Zwei Eigenschaften dieser Tabelle richteten den Schaden an, und keine davon ist für sich genommen ein Fehler.
Die erste: marimo mergte die Notebook-Metadaten mit der höchsten Priorität, über die Konfiguration derjenigen Person, die marimo ausführt. Für eine Formatierungsvorliebe ist das die richtige Voreinstellung. Ein Notebook, das zwei Leerzeichen Einrückung will, soll Ihre globale Einstellung schlagen, denn der Autor weiß, wie seine Datei aussieht, und Sie nicht.
Die zweite: Der Merge war fast ungefiltert. Vor dem Fix entfernte marimos
Sanitizer genau einen Schlüssel aus der vom Notebook gelieferten Konfiguration:
tool.marimo.runtime.auto_instantiate. Alles andere in tool.marimo kam durch,
einschließlich tool.marimo.mcp. Dort listet man Model-Context-Protocol-Server
auf, und eine Liste von MCP-Servern ist eine Liste zu startender Befehle.
Beides zusammen, und ein Kommentar am Anfang einer heruntergeladenen Datei konnte ein Programm benennen und marimo dazu bringen, es zu starten. Nichts davon ist ein Exploit. Die Datei benutzte die Funktion so, wie sie entworfen wurde, und der Entwurf ging davon aus, dass Sie die Datei selbst geschrieben hatten.
Der Patch ist die Offenlegung
Der Fix, PR #10281, trägt den Titel „additional pep 723 sanitization“ und tauscht eine Sperrliste gegen eine Erlaubnisliste. Vom Notebook gelieferte Konfiguration ist jetzt auf kosmetische und Editor-Abschnitte beschränkt: Formatierung, Speichern, Darstellung, Keymap, Diagnostik, Lint, Snippets, Datenquellen, Language Server, Sharing, venv, Runtime, Paketverwaltung.
Die gestrichenen Abschnitte sind die interessante Hälfte: ai, mcp,
completion, secrets, server.
Das sind fünf ganze Abschnitte, nicht fünf Schlüssel, und ein Dokument, das Sie
heruntergeladen hatten, konnte sie alle setzen. Die Begründung des PR benennt
außerdem eine zweite Nutzlast, die nie einen Subprozess brauchte. Der
ai-Abschnitt enthält API-Basis-URLs, sodass ein Notebook den Modell-Endpunkt
auf einen Host seiner Wahl umbiegen konnte, was nach Worten der Maintainer
„die Exfiltration der API-Schlüssel des Betreibers an
angreiferkontrollierte Endpunkte ermöglichen könnte“.
Die Basis-URL-Variante dieses Angriffs haben wir im Juli behandelt, als
ein Repository ANTHROPIC_BASE_URL setzen konnte
und Claude Code seinen eigenen Schlüssel an die vom Repo gewählte Adresse
schickte. Es ist der billigste Angriff dieser Kategorie, denn es läuft kein Code
und der Client des Opfers erledigt das Versenden. Die marimo-Variante schrumpft
das Transportmittel von einem Repository auf einen Kommentar.
Die Vertrauenseinheit wird immer kleiner
Diese Fehlerklasse hat eine kurze Geschichte, und in dieser Geschichte braucht der Angreifer jedes Mal weniger von Ihrer Maschine.
Im April meldete Wiz Research
CVE-2026-12957
in Amazon Q Developer, CVSS 8.5, im Mai gepatcht und im Juni offengelegt. Amazon
Q las .amazonq/mcp.json aus einem geöffneten Workspace und startete die dort
definierten Server. Diese Prozesse erbten die vollständige Umgebung des
Entwicklers: AWS-Schlüssel, Cloud-CLI-Token, API-Secrets, SSH-Agent-Sockets.
Amazons Fix war, vor dem Start eines MCP-Servers aus einem nicht
vertrauenswürdigen Workspace nachzufragen.
Im August
schrieb ChainDrop Agenten-Konfiguration in die Repositories, die es durchlief:
einen SessionStart-Hook in .claude/settings.json und eine
folderOpen-Aufgabe in .vscode/tasks.json, sodass das Projekt zu klonen und
zu öffnen genügte.
Beide brauchten ein Repository. Jemand musste ein Projekt veröffentlichen oder kompromittieren, Sie mussten es klonen, und ein Verzeichnis voller Dotfiles lag dort im Baum, wo eine misstrauische Person hineinsehen konnte.
Marimo braucht eine Datei. Eine Datei, die sich wie ein Dokument darstellt und die so ankommt, wie Dokumente ankommen: ein Slack-Thread, ein E-Mail-Anhang, ein Gist, den jemand im Standup verlinkt hat, ein Kaggle-Download. Ein Notebook klont man nicht. Man öffnet es, denn es zu öffnen ist die Art, herauszufinden, was es ist.
Der mcp-Block hat sich in den letzten achtzehn Monaten in den größten Teil des
Entwickler-Werkzeugs ausgebreitet, und diese Blöcke leben in Dateien, die
zwischen Menschen reisen: Repository-Wurzeln, Editor-Verzeichnisse und jetzt
Dokument-Metadaten. Jeder davon ist eine Liste von Programmen, die irgendetwas
auf Ihrer Maschine zu starten bereit ist, geschrieben von dem, der die Datei
zuletzt angefasst hat. Registry-Scanner lesen sie nicht, Lockfiles decken sie
nicht ab, und niemand hat eine Provenance-Attestierung vorgeschlagen, die für
einen Kommentar gälte.
Woher MCP-Server in einem Profil kommen
Bromure Agentic Coding beantwortet die Provenance-Frage dort, wo sie beantwortet werden kann: auf dem Host, bevor die VM bootet.
Jedes Profil hat in seinen Einstellungen ein MCP-Panel, neben Agents, Zugangsdaten, Guardrails und Supply Chain. Es enthält eine Liste von Servern. Jeder Eintrag lässt sich unabhängig ein- und ausschalten und nutzt einen von zwei Transporten: HTTP, eine entfernte URL mit optionalem Bearer-Token, oder stdio, ein lokaler Befehl, der innerhalb der VM gestartet wird. Bromure übersetzt diese Liste in das Format, das der aktive Agent erwartet, JSON für Claude Code und TOML für Codex, und injiziert sie beim Boot in die VM.
Lesen Sie die Fahrtrichtung in diesem letzten Satz. Die Konfiguration geht in den Gast, vom Host, beim Boot, aus einer Liste, die Sie in einem Einstellungs-Panel pflegen. Eine Datei, die danach im Workspace auftaucht, ist kein Ort, aus dem MCP-Server kommen, also gibt es keinen Merge, dessen Priorität man falsch setzen könnte, und keinen Sanitizer, den man mit einem Konfigurationsschema synchron halten müsste, das immer neue Abschnitte bekommt. Ein Dokument kann keinen Eintrag zu einer Liste hinzufügen, die es nicht erreicht.
Öffnen Sie das Notebook trotzdem
Nichts davon hilft an dem Tag, an dem Sie eine Version mit dem Fehler ausführen. Nehmen Sie also das marimo von vor 0.23.15, schenken Sie dem Angreifer den Sieg, und führen Sie ein feindseliges Notebook durch ein Profil.
Der Subprozess startet im Computer von jemand anderem. Sitzungsarbeit in
Bromure Agentic Coding findet in kitty-Tabs innerhalb der Ubuntu-VM des Profils
statt, einen Hypervisor von macOS entfernt. marimo edit läuft dort, und der
Befehl aus dem Kommentar ebenfalls. Unter Ressourcen → Speicher besteht das
Profil aus drei Schichten: einem profil-eigenen /home/ubuntu mit Home
löschen…, einer Workspace-Systemplatte mit Auf Basis zurücksetzen…, die aus
dem geteilten Image neu klont, und darunter einem schreibgeschützten Basis-OS,
das nichts im Gast anfassen kann. Ein Menüpunkt nimmt alles wieder weg, was der
Eintrag installiert hat.
Die Umgebung, die er erbt, ist ein Satz Köder. Der Amazon-Q-Fehler tat weh,
weil der gestartete Server ein Kind der echten Shell des Entwicklers war und
dessen echte Zugangsdaten erbte. In einem Bromure-Profil steht hinter diesen
Variablennamen nichts. Generische API-Schlüssel sind brm_…-Platzhalter, die in
die VM exportiert und auf dem Weg nach draußen vom Proxy des Hosts gegen echte
Werte getauscht werden. Die kubeconfig ist synthetisch, mit
Wegwerf-Client-Zertifikaten. AWS-Anfragen werden hostseitig neu signiert, sodass
ein Prozess, der am Proxy vorbei routet, InvalidSignatureException bekommt
statt Zugriff. ~/.docker/config.json enthält einen gefälschten base64-Blob.
Private SSH-Schlüssel
sind nie in der VM, weil der Host
über einen profil-eigenen Agenten signiert. Schalten Sie für ein bestimmtes
Zugangsdatum Genehmigung zur Nutzung erforderlich ein, und jeder Tausch wird
zu einem Dialog auf dem Host mit einer zeitlich begrenzten Freigabe: fünf
Minuten, eine Stunde, der Rest der Sitzung.
Der Basis-URL-Trick sammelt einen Platzhalter ein. Der Proxy ersetzt ein
echtes Zugangsdatum nur bei ausgehenden Anfragen an den zu diesem Zugangsdatum
gehörenden Host. Ein Notebook, das einen KI-Endpunkt auf attacker.example
umbiegt, sammelt das ein, was in der Umgebung der VM steht, nämlich eine
brm_…-Zeichenkette, die nichts aufschließt.
Der Server muss ans Netz. Ein MCP-Eintrag, der etwas für einen Angreifer
Nützliches startet, braucht Egress, sei es zum Nachladen einer späteren Stufe
oder zum Zurücksenden des Gefundenen. Guardrails → Ausgehende Verbindungen
ist eine Regeltabelle im pf-Stil: Aktion, Protokoll (tcp, udp, web,
any), Host oder CIDR, Portliste, und für web eine Liste von HTTP-Methoden,
von oben nach unten ausgewertet, erste Übereinstimmung gewinnt, dazu die
Einstellung Nicht zugeordneter Verkehr auf Erlauben oder Verweigern. Stellen
Sie sie auf Verweigern, listen Sie die Hosts auf, die Ihre Arbeit braucht, und
das Ziel des Eintrags steht nicht auf der Liste. Die Tabelle wird auf dem Host
durchgesetzt, im virtuellen Switch und im Proxy, sodass nichts, was der Gast an
seinem eigenen Routing ändert, die Antwort verändert.
Die Datei muss überhaupt erst in der VM sein. Ein Profil teilt höchstens
acht Mac-Ordner, jeden davon von Hand im Ordner-Panel ausgewählt und unter
/home/ubuntu/<basename> eingehängt. Ein Notebook, das in ~/Downloads
gelandet ist, ist nicht im Profil, bis Sie es dorthin legen, und der Rest Ihres
Macs ist nicht einen Verzeichnisdurchlauf von dem entfernt, was das Notebook
gestartet hat.
Und Sie können dabei zusehen. Das Sicherheitsprotokoll-Fenster (Fenster → Sicherheitsprotokoll…) ist eine einzige chronologische Tabelle auf dem Host: Paket-Urteile, Firewall-Entscheidungen, Zugangsdaten-Tausche, Prompt-Injection-Erkennungen. Gastcode kann sie nicht ändern, weil Gastcode sie nicht erreicht. Eine blockierte ausgehende Verbindung von einem Prozess, den Sie nicht gestartet haben, erscheint dort als Zeile, und das ist besser, als es sechs Wochen später von einer Cloud-Rechnung zu erfahren.
Schalten Sie es ein
Aktualisieren Sie marimo auf 0.23.15 oder neuer; die Erlaubnisliste dort ist ein
guter Patch. Sehen Sie sich dann jede andere Stelle an, aus der Ihr Werkzeug
einen mcp-Block liest, und fragen Sie, wer diese Datei schreiben darf.
In einem Profil sind die Einstellungen, die zwei Minuten wert sind, dieselbe kurze Liste wie immer. Guardrails → Ausgehende Verbindungen, mit Nicht zugeordneter Verkehr auf Verweigern und einer Erlaubnisliste für die Hosts, die Ihre Arbeit braucht. Zugangsdaten → Genehmigung zur Nutzung erforderlich für alles, was Geld ausgeben oder Daten löschen kann. Supply Chain → OSV-Schwachstellenprüfung, dazu socket.dev- oder Delpi-Filtern, falls Sie einen Schlüssel haben, obendrauf auf das standardmäßig aktive Zwei-Tage-Altersgate. Prompt Injection → der CLAUDE.md- und AGENTS.md-Scanner, der die Dateien, die Autorität über Ihren Agenten beanspruchen, genau als solche behandelt.
Notebooks werden weiter weitergeleitet werden, und Konfigurationsformate werden weiter Abschnitte dazubekommen, weil beides nützlich ist. Was Sie ändern können, ist, welche Maschine zuhört, wenn ein Dokument darum bittet, ein Programm zu starten. Installieren Sie Bromure Agentic Coding, halten Sie Ihre Serverliste im Profil-Editor, und lassen Sie den nächsten hilfsbereiten Kommentarblock eine VM konfigurieren, die Sie aus einem Menü löschen können.