A generated abstract illustration on a deep navy field. A wide slate-coloured bridge structure spans the frame, and one rectangular port on its left face stands open and glowing amber. Through that opening a stream of small identical blocks pours outward across a faint blueprint grid, crossing a thin dotted cyan boundary drawn in the foreground without being stopped. At lower right the blocks feed into a low translucent lattice of connected nodes that resembles a memory store. No text, letters, numbers, people, brand marks or organisation logos appear. The OrcaRouter logo is composited in the bottom-right corner of a white strip below the artwork.
AI Safety Incidents

RufRoot: one unauthenticated request to Ruflo's MCP bridge ran commands, stole keys and poisoned AI memory

Author

Rowan Sterling

Date Published

Latest models · 20View all models →
Benchmarks: Artificial Analysis · updated daily
Back to all posts

On 30 June 2026, Noma Labs handed the maintainer of Ruflo a working exploit against the project itself. The research arm of Noma Security had found that Ruflo — an open-source agent orchestration platform formerly released as Claude Flow — shipped a default Docker Compose deployment that bound its Model Context Protocol bridge to port 3001 on every network interface and asked for no authentication at all. A single HTTP request to the /mcp endpoint was enough to run shell commands inside the container as the node user, and from that shell an attacker could read every LLM provider API key in the environment, dump every stored conversation, spawn agent swarms billed to the victim's keys, and write instructions into the AgentDB learning store that would keep steering the AI's answers for every user of that instance. The flaw is CVE-2026-59726, nicknamed RufRoot, scored CVSS 10.0 and fixed in 3.16.3 within 24 hours. No source we read reports it being exploited in the wild. The archive's own record for this flaw, with its source list and confidence grade, sits in the AI incident archive, next to the other MCP and infrastructure entries filed around it.

The default that opened the door

Ruflo is what Noma Labs calls an agent meta-harness: a self-hosted layer for swarms of up to 100 agents, built for Claude Code and OpenAI Codex, with long-term memory and a built-in MCP server for tool calling. The usual setup is the one Noma describes — clone the repository, add LLM API keys, run docker compose up -d. The project's default compose file bound the bridge to 0.0.0.0 on port 3001 and bound its companion MongoDB to all interfaces on 27017, with Mongo authentication off. How far that reaches depends on the firewall, security group and network segmentation around the deployment; Noma's own formulation is that any network-reachable instance was fully exploitable without authentication.

The MCP bridge is an Express.js server that turns JSON-RPC requests arriving over HTTP into calls against Ruflo's internal tool library. In the vulnerable line that library held 233 tools covering shell access, database operations, agent management and memory storage. Noma's writeup is blunt about what that makes it: "The MCP Bridge isn't a random auxiliary debug interface; rather, it is Ruflo's central nervous system. Every tool call, every agent action, every memory operation goes through the MCP Bridge."

The authentication layer was missing, not merely weak. POST /mcp and POST /mcp/:group implement the MCP JSON-RPC protocol and pass incoming tools/call requests straight to the internal executeTool() function. Ruflo did maintain a command blocklist, AUTOPILOT_BLOCKED_PATTERNS, intended to stop dangerous shell operations, but the GitHub advisory is explicit that it "was enforced only in the autopilot flow. POST /mcp and POST /mcp/:group bypassed it." One unauthenticated call naming ruflo__terminal_execute therefore ran arbitrary commands as the container's node user, recorded in the advisory as uid 1000, with no token, session or prior interaction required. No privilege escalation was needed for what came next: the credentials, the agent APIs, the memory store and the database were all reachable from that shell.

The chain, in eight steps

Noma Labs published an eight-step proof of concept and confirmed each step live against a default Ruflo deployment on AWS EC2, using out-of-band callbacks to Burp Collaborator to verify execution and exfiltration. The steps are not speculative consequences stacked on a theoretical bug; they are a demonstrated sequence, and the advisory's remediation section is written for operators who may already be inside it.

A hand-built HTML card headed 'The RufRoot chain, step by step', kicker 'RufRoot - CVE-2026-59726', subtitle 'Eight steps Noma Labs confirmed live against a default Ruflo deployment on AWS EC2'. Eight rows, each with a circled step number, the action, and what it yielded. 1, call tools/list: enumerate the 233 exposed tools. 2, call ruflo__terminal_execute over POST /mcp: shell inside the bridge container as node, uid 1000, verified by an out-of-band callback. 3, run printenv: every LLM provider API key in the container environment. 4, call ruflo__swarm_init and ruflo__agent_spawn: attacker-controlled agent swarms running on the victim's keys and compute. 5, call ruflo__agentdb_pattern-store: a fabricated SOC2 policy in the learning store, steering future output for all users. 6, query MongoDB on port 27017: every stored conversation, title and metadata. 7, write /app/beacon.js and inject it into index.js, then kill PID 1: a backdoor that survives the container restart policy. 8, clear shell history: no forensic trace. A footer cites Noma Labs and CVE-2026-59726, and marks step 5 as the step a patched redeploy does not undo.

Reconnaissance comes first because the tool list is public to any caller: tools/list returns all 233 entries without a credential. Command execution follows from the same endpoint, and Noma's proof of concept confirmed it with an out-of-band callback rather than an inference from a response body. The environment step is the one that turns a host compromise into a billing and data-loss problem: the compose file passed provider keys into the container and backend processes inherited the full environment through { ...process.env }, so a single printenv dumped them. Those keys then allowed ruflo__swarm_init and ruflo__agent_spawn to start attacker-controlled swarms that consumed the victim's quota and compute.

Step five is the one that outlives the intrusion. Using ruflo__agentdb_pattern-store, Noma planted a fabricated SOC2 compliance policy that instructed the AI to embed an attacker-controlled URL in every deployment script it subsequently generated. Because AgentDB exists to persist exactly this kind of pattern so that agent behaviour improves across sessions, the injected entry behaved as the system was designed to behave. "Future conversations produce attacker-influenced output, and the user has no way to tell," Noma's writeup states. Dark Reading, which reported the flaw on 29 July, put the distinction to an outside expert: John Gallagher, vice president at Viakoo, called it a different class of problem: "This is not exploiting software flaws, it is memory poisoning."

The remaining steps are conventional post-exploitation, executed with unusual convenience. MongoDB on the internal Docker network had no authentication, so the attackers installed a client in /tmp, dumped conversations and shipped them out. Persistence came from writing /app/beacon.js, adding a require('./beacon.js') line to index.js and killing PID 1, after which Docker's restart policy brought the container back with the beacon active. The final step was clearing shell history. All of it followed from a single unauthenticated request; nothing in the default deployment stopped the chain at any point.

The fix, and the part a patch cannot reach

The maintainer shipped Ruflo 3.16.3 within 24 hours of the 30 June disclosure, and GitHub published the advisory GHSA-c4hm-4h84-2cf3 on 1 July 2026. The remediation, tracked in the repository as ADR-166 and merged in pull request 2521, changes the defaults rather than just adding a check:

• The MCP bridge binds 127.0.0.1 by default; a public bind requires MCP_AUTH_TOKEN, and the process exits with a fatal message at boot if the token is absent.

• Bearer-token middleware with a constant-time comparison sits in front of the bridge.

• terminal_execute is gated server-side behind MCP_ENABLE_TERMINAL=true, off by default, and the gate is enforced identically on /mcp, /mcp/:group and the autopilot flow — the missing link that had let the disclosed chain reach a shell.

• MongoDB requires authentication, with a root password required for the container to boot.

• The bridge container runs a read-only root filesystem with tmpfs, a CORS allowlist replaces the wildcard, and CI regression locks cover the static configuration and the runtime behaviour.

None of that removes an entry already written into AgentDB. The advisory tells operators of any exposed instance to firewall ports 3001 and 27017 immediately, rotate the provider keys, audit the pattern store for injected agentdb_pattern-store entries and purge them, audit MongoDB for tampering, and rebuild containers from clean images. Its warning is unusually direct: "A patched redeploy alone does NOT undo poisoning." Noma's writeup draws the same conclusion — auditing the pattern store is part of remediation, and the patch is not.

The reason is structural rather than careless. A pattern store that shapes future model output has no way to distinguish a legitimate learned instruction from an attacker's planted one; both arrive as patterns, and the system is built to trust them. That turns a network vulnerability into a data-integrity problem with an unknown backlog, and it means the question for an operator is not only whether the port was reachable but what the memory holds now.

A hand-built HTML timeline card headed 'From disclosure to coverage', kicker 'RufRoot - disclosure timeline', subtitle 'Five dates, from the private report to the day the record was filed'. Six dated rows. 30 June 2026: Noma Labs discloses the flaw to Ruflo's maintainer with a working proof of concept. 1 July 2026: Ruflo 3.16.3 ships and GHSA-c4hm-4h84-2cf3 is published with CVSS 10.0. 9 July 2026: the CVE record for CVE-2026-59726 is published in the National Vulnerability Database, later marked Deferred. 29 July 2026: The Hacker News and Dark Reading report the flaw; Noma Labs publishes its research. 30 July 2026: SecurityWeek reports it, and the record is dated to this day. A separate amber note reads: no source we read reports exploitation in the wild at any point on this timeline. A footer cites The Hacker News, the GitHub advisory API, the NVD record and SecurityWeek.

What is established, and what is not

The technical record here is primary and consistent. The advisory names the affected package (npm ruflo below 3.16.3), the patched version, the exact endpoints, the container user and the two ports; the maintainer's release notes describe the same chain; Noma's writeup supplies the proof-of-concept detail; and independent outlets — The Hacker News and Dark Reading on 29 July 2026, SecurityWeek on 30 July — report the same mechanism without adding contradictory specifics. A Cloud Security Alliance research note dated 5 August 2026 restates the sequence and places it alongside earlier unauthenticated MCP and agent-orchestration flaws it had tracked. The archive grades the record confidence A, undisputed, and marks it a landmark; its severity grade for the record is high, distinct from the CVSS score itself.

One scoring detail is worth stating precisely, because two primary documents disagree. GHSA-c4hm-4h84-2cf3 carries CVSS 10.0 on the vector AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H — the scope change is what pushes it to the maximum. The same advisory's copy on the 3.16.3 release page describes the score as "CVSS 9.8 critical". The National Vulnerability Database record, checked on 30 October 2026, published on 9 July 2026 and marked Deferred, carries the GitHub 10.0 as a secondary metric and no NVD-authored score of its own. We did not find an explanation for the 9.8 line, and this piece does not resolve it.

Adoption figures are thinner. Noma Labs described the project at the time as having more than 67,000 GitHub stars, roughly 10 million downloads and about 1 million active users; The Hacker News, as the Cloud Security Alliance summarised it, cited more than 66,500 stars. The download and user counts are Noma's own and are not independently verified. The repository's star count on 30 October 2026 was 80,198 — a check of the project's reach, not a measure of exposure, and no source has published how many deployments were actually reachable from a network.

A hand-built HTML card headed 'Established, and not', kicker 'RufRoot - evidence', subtitle 'What the advisory, the release notes and the research support, against what nobody has shown'. Two columns. The left column, headed 'Established', lists: CVE-2026-59726 affects npm ruflo below 3.16.3 and is patched in 3.16.3; the default compose file bound the MCP bridge to 0.0.0.0 on port 3001 and MongoDB to 27017 without authentication; POST /mcp and POST /mcp/:group reached 233 tools and terminal_execute with no credential, running as node, uid 1000; Noma Labs confirmed an eight-step chain live on AWS EC2 with out-of-band callbacks, including an AgentDB pattern injection; the advisory was published 1 July 2026 with CVSS 10.0 and the fix shipped within 24 hours of the 30 June disclosure; the archive record is confidence A, undisputed, no real harm recorded, GLOBAL, MCP and INFRA. The right column, headed 'Not established', lists: that anyone exploited the flaw in the wild; how many Ruflo deployments were actually network-reachable; that any instance's AgentDB was poisoned, since no audit results are published; whether 9.8 in the release notes or 10.0 in the advisory is the intended score; Noma's counts of 10M downloads and 1M active users, which no independent source confirms; that any operator received notice beyond the public advisory. A footer cites GHSA-c4hm-4h84-2cf3, the Ruflo 3.16.3 release notes, Noma Labs and SecurityWeek.

What is not established is the part most easily inflated. Neither Noma, the maintainer nor any outlet we read claims the flaw was exploited in the wild, and the archive records no real harm for this entry. Noma's proof of concept was run against a deployment the researchers controlled on EC2, which shows what a default install permits, not what any particular production instance suffered. No count of internet-facing Ruflo deployments has been published, and no operator has published the results of an AgentDB audit, so the population affected and the amount of poisoned memory in existence are both unknown. The Cloud Security Alliance's advice to treat exposed deployments as full compromises is a risk posture justified by zero-interaction exploitation; it is not evidence that a breach occurred.

Why the archive marks it a landmark

The archive's landmark flag rests on something more specific than a maximum score. A CVSS 10.0 remote code execution bug in a popular self-hosted service is a familiar genre, and the fix here arrived within 24 hours. What distinguishes RufRoot is that the exploit terminated in the AI's own learning pipeline, where the conventional remedy — upgrade, redeploy, close the port — leaves the injected instruction in place and still shaping answers. The Cloud Security Alliance note reads it as a case study in a pattern it had already documented across agent tooling, including a LangGraph checkpoint chain and an unauthenticated AutoGen Studio surface, and argues that persistent memory used to shape agent behaviour should be treated as a sensitive data store under integrity monitoring rather than as a cache.

That reframing is the practical takeaway. The visible artifact of this incident is a patched dependency and a published advisory; the invisible one is whatever pattern an operator's AgentDB may still contain, and there is no published tool or signature that tells a legitimate learned policy from a planted one. For the organisations that ran the default compose file behind a reachable network, the useful response is the one both Noma and the advisory give: assume the keys were read, rotate them, and inspect the memory before trusting the answers it produces.