```html ```
top of page

You Were Told to Read the Notebook Before You Run It. In marimo, Opening It Was Already the Exploit — and the Command Came Out of the File's Own Metadata.

  • Writer: Patrick Duggan
    Patrick Duggan
  • 3 minutes ago
  • 5 min read

Every safety instruction anyone has ever given about notebooks rests on one assumption: you can look before you leap. Open the file, read the cells, decide whether you trust it, then run it. Reading is safe. Running is the risk. That is the whole mental model, and it is why nobody thinks twice about opening a notebook a colleague sent them.


[CVE-2026-75149](https://analytics.dugganusa.com/api/v1/dredd/kev-gap?cve=CVE-2026-75149) deletes that assumption. In marimo before 0.23.15, a crafted notebook carries an attacker-chosen MCP server command inside its own metadata, and marimo launches it as a local subprocess when the file is opened in edit modebefore a single cell executes.


CVSS v4 8.7, v3.1 8.8. No authentication required. The only user interaction needed is the one action everybody thought was the safe one.



Where the command hides


The payload lives in the notebook's PEP 723 metadata — the inline block that tells Python tooling what a script needs to run. It is supposed to be a manifest: dependencies, versions, the boring bookkeeping.


marimo also read configuration out of that block. Including an mcp section naming a server command to launch.


You can see the shape of the mistake in the fix. The hardening patch strips five sections during parsing: ai, mcp, completion, secrets, and server. Look at what those five have in common — every one of them is configuration that reaches outside the notebook. Model access. Tool servers. Credentials. Network listeners.


So the bug was never really a parser flaw. It was a category error: treating a section of a file somebody sent you as settings you chose. The patch's own language is the tell — it now treats notebook metadata as attacker-controlled and allowlists what may come out of it. That sentence is the actual finding, and I would rather quote a vendor doing the right thing than editorialise over it.





This is the second time, and the second time is the story


In April, marimo shipped [CVE-2026-39987](https://analytics.dugganusa.com/api/v1/dredd/kev-gap?cve=CVE-2026-39987) — missing authentication on the /terminal/ws endpoint. Pre-authentication remote code execution, shell on the host. It went into the CISA KEV catalogue, and it was publicly reported as exploited within ten hours of disclosure. Our own exploit harvester holds three separate proof-of-concept repositories for it, collected 16 May, 25 May and 2 August — people were still publishing exploits for it four months later.


Now compare the two.


April: you had to expose a service. The attack surface was a network endpoint on the machine the notebook runs on, and the defence was the ordinary one — do not put it on the internet.


August: nothing is exposed. The attack surface is a file, and what it reaches is not the web server but the agent's configuration — which model it talks to, which tool servers it launches, which secrets it holds.


Two dated observations. One product. A measurably different attack surface. That is what I mean when I say demonstrated evolution — it is checkable, unlike "increasingly sophisticated threat actors," which is an adjective wearing a lab coat. The direction is what matters here, and the direction is away from the server and toward the agent's settings.



Why the AI control plane keeps being the soft part


We have written this shape three or four times this year and it keeps holding. The models are hard to attack. The plumbing around the models is not, because it was written fast, by people shipping features, in the same eighteen months the whole category was invented.


MCP is a configuration surface that starts processes. That is its job. It is not a flaw that it launches servers — it is the product. The flaw is every place where something that came from outside gets to fill in that configuration: a notebook's metadata, a repo's checked-in settings file, a project template, a shared workspace config, a README that an agent reads and acts on.


If you are building on agents, the question worth asking this week is not is my model jailbreakable. It is: what, in my stack, can write to the agent's config, and did that thing come from someone I trust? For marimo the answer was "any .py file anyone sends you," and that was enough.



What to do


Upgrade to 0.23.15 or later; current release at time of writing is 0.24.0. Then go and think about the notebooks you have opened recently that came from outside your team — a shared repo, a Slack attachment, a tutorial, a customer's reproduction case — and check for subprocesses you did not start and MCP servers you did not configure.


The wider habit is the durable bit: treat a notebook as an executable, not a document. Not because of this CVE, which is patched, but because notebooks have now demonstrated twice in five months that they carry more machinery than their file extension advertises.



Sources


CVE-2026-75149, code injection in marimo, CVSS v4 8.7 / v3.1 8.8, affecting versions prior to 0.23.15, fixed in 0.23.15 (23 July 2026), CVE published 19 August 2026, CVSS record assigned by VulnCheck's CNA. Discovered by security researcher Gregory Tan, who co-authored the hardening patch. Reported by The Hacker News, 25 August 2026.


CVE-2026-39987 — missing authentication on /terminal/ws, patched in 0.23.0, listed in CISA KEV, publicly reported as exploited within ten hours of disclosure — is in our own cisa_kev index, and the three PoC repositories are in our iocs index under source=exploit-harvester.


Capped at 95 percent: we have no records for CVE-2026-75149 in our own corpus — no PoC has surfaced that our harvester caught, and its absence is not evidence that none exists. We have no telemetry on real-world exploitation of this one, no count of affected installations, and the fix predates the CVE by four weeks, which means an unknown number of people upgraded past this without ever knowing it was a security release.




That last detail is the one I keep turning over: the fix landed 23 July, the CVE arrived 19 August. Four weeks where the patch existed and the reason to apply it did not. If you upgraded marimo in early August for the features, you got lucky, and luck is not a patching strategy. Rate this post below.




Every indicator in this post is in the feed. Free.

1.58M+ IOCs, STIX 2.1 / TAXII, 88% novel vs ThreatFox, exploited-CVE leads ahead of CISA. No credit card — a free API key in 30 seconds, and you can audit every claim above against the live endpoints.



Was this useful? Thirty seconds, no cookies, no tracking, no third parties, your address hashed and never stored. If the box below does not load, the same question lives at https://analytics.dugganusa.com/nps.html?post=you-were-told-to-read-the-notebook-before-you-run-it-in-marimo-opening-it-was-already-the-exploit



Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page