Someone left the master key taped to the front door of a house that's usually behind a gated fence
CVE-2026-59726, codenamed RufRoot by Noma Labs researcher Eli Ainhorn, is an unauthenticated remote code execution flaw in Ruflo, an open-source AI agent orchestration platform (66.5K GitHub stars, ~1M active users). The default docker-compose.yml binds the MCP Bridge to 0.0.0.0:3001 with zero authentication — no token, no API key, no header check, no IP allowlist. A single curl POST to /mcp invoking ruflo__terminal_execute gives an attacker a shell as the node user (uid 1000) inside the bridge container, plus access to 232 other tools spanning shell execution, database ops, agent lifecycle management, and persistent memory writes. All versions prior to 3.16.3 are affected; the fix was shipped within 24 hours of responsible disclosure on June 30, 2026.
The vendor's CVSS 10.0 is mathematically honest — the vector AV:N/AC:L/PR:N/UI:N/S:C accurately describes what happens *when the port is reachable*. But in practice, Ruflo is a developer and research tool, not core infrastructure. The overwhelming majority of deployments sit on developer laptops, CI sandboxes, or internal servers behind firewalls and security groups. The blast radius is container-scoped (not host-level), and there is no evidence of internet-facing mass exposure via Shodan or Censys. The vendor severity overstates the real-world risk for most enterprises by conflating theoretical reachability with actual deployment posture.
6 steps from start to impact.
Discover exposed MCP Bridge
docker-compose.yml binds to 0.0.0.0, making the Express.js MCP Bridge reachable on all interfaces. No banner or auth challenge is returned — the port simply accepts JSON-RPC POSTs.- Network path to target host on port 3001
- Target running Ruflo < 3.16.3 via docker-compose
- Most Ruflo instances are developer workstations or internal servers behind firewalls/security groups
- CLI/npm deployments (the primary install method) do not expose an MCP bridge by default
- No public Shodan/Censys data showing mass internet exposure
Enumerate 233 exposed tools
{"jsonrpc":"2.0","id":1,"method":"tools/list"} to POST /mcp. The bridge responds with the full catalog of 233 tools, including ruflo__terminal_execute, ruflo__swarm_init, ruflo__agent_spawn, and ruflo__agentdb_pattern-store. No authentication is checked — the blocklist on terminal_execute only applies to the autopilot code path, not the raw /mcp endpoint.- Step 1 completed
- None once port is reachable — zero complexity
tools/list calls on port 3001.Achieve RCE via terminal_execute
curl -s -X POST http://<target>:3001/mcp -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ruflo__terminal_execute","arguments":{"command":"id && hostname"}}}'. The command executes as node (uid 1000) inside the bridge container. Attacker now has interactive shell equivalent via repeated POST calls.- Step 2 completed
- Execution is container-scoped — no direct host compromise without a separate container escape
- Runs as unprivileged
nodeuser, not root
Exfiltrate API keys and conversation data
env to dump container environment variables containing LLM provider API keys (OpenAI, Anthropic, Google, OpenRouter). Simultaneously, attacker connects to the unauthenticated MongoDB on port 27017 (also bound to 0.0.0.0 by default) and dumps all stored conversations, system prompts, and metadata. These keys enable secondary abuse: running workloads on the victim's LLM budget, or accessing any data the keys authorize.- Step 3 completed
- MongoDB also running with default no-auth config
- Victim must have configured expensive API keys in the container
- MongoDB access requires network path to 27017 (may be firewalled separately)
Poison AgentDB memory store
ruflo__agentdb_pattern-store to inject malicious SONA learning patterns into the persistent memory. These poisoned patterns steer all future AI agent outputs for every user of the instance — a persistent integrity compromise that survives patching and container restarts. This is the most insidious impact: a supply-chain-like attack on the AI decision layer.- Step 3 completed
- Impact is limited to users of this specific Ruflo instance, not a global supply-chain effect
- Poisoned patterns can be audited and removed if the operator knows to look
Spawn rogue agent swarms and persist
ruflo__swarm_init and ruflo__agent_spawn to create autonomous agent processes running on stolen API keys. Additionally, attacker writes a beacon script to /app/beacon.js and patches index.js to load it on container restart, leveraging Docker's restart: always policy for persistence. Shell history is cleared to cover tracks.- Steps 3–4 completed
- Container filesystem is writable (default pre-patch)
- Post-patch containers run read-only with tmpfs, blocking filesystem persistence
- Rogue swarms consume API budget, which may trigger vendor billing alerts
The supporting signals.
| In-the-Wild Exploitation | No confirmed exploitation. Not listed on CISA KEV. No threat actor campaigns documented as of 2026-07-30. Noma Labs' disclosure was handled via responsible disclosure with a 24-hour patch turnaround. |
|---|---|
| Proof-of-Concept | Public PoC available. A working single-curl exploit is published in the Noma Labs blog post and reproduced across multiple outlets. Trivially weaponizable — no tooling or compilation required. |
| EPSS | 0.00442 (~0.44% probability of exploitation in 30 days). Low-to-moderate score; reflects the niche product category and container-scoped impact rather than the trivial exploitability. |
| KEV Status | Not listed. No CISA KEV entry as of 2026-07-30. No federal mandate to patch on an accelerated timeline. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H — Perfect 10.0. Network-accessible, zero complexity, no auth, no user interaction, scope change (container → host-env keys + downstream AI outputs). Technically accurate *if* the port is reachable. |
| Affected Versions | All versions of Ruflo (npm package ruflo) prior to 3.16.3 when deployed via docker-compose. CLI/npm direct installations do not expose the MCP Bridge by default. |
| Fixed Version | 3.16.3 (released ~July 1, 2026, within 24 hours of disclosure). Fix PR #2521, documented in 3.16.3" target="_blank" rel="noopener">ADR-166. Adds bearer auth, loopback binding, terminal-execute opt-in, MongoDB auth, read-only containers, CORS allowlist. |
| Scanning / Exposure | No public Shodan/Censys/GreyNoise data documenting exposed Ruflo MCP bridges. The product is primarily a developer/research tool deployed on workstations and internal servers. Mass internet exposure is unlikely but unquantified. |
| Disclosure Timeline | Responsible disclosure: June 30, 2026 → Patch shipped: ~July 1, 2026 → GHSA published: July 1, 2026 → Public blog: July 29, 2026 (Noma Security) |
| Researcher / Org | Eli Ainhorn, Senior AI Vulnerability Researcher at Noma Security (Noma Labs). Reviewed by Dragan Spiridonov. |
noisgate verdict.
Downgraded from CRITICAL to HIGH because the blast radius is container-scoped on a developer tool — the exploit delivers a uid-1000 shell inside a Docker container, not host-level or domain-level compromise, and the affected population is overwhelmingly developer workstations and internal sandboxes rather than internet-facing infrastructure. The single most decisive factor is that the exploitable surface (docker-compose with 0.0.0.0 binding) represents a subset of deployments of a niche AI orchestration tool, not a canonical high-value infrastructure component.
Why this verdict
- Trivial exploitability, single curl: Pre-auth RCE with zero complexity and a published PoC keeps the floor above MEDIUM. Any script kiddie with
curlcan exploit an exposed instance — no compilation, no payload crafting, no race conditions. - Container-scoped blast radius: The shell runs as
node(uid 1000) inside a Docker container. Without a separate container escape vulnerability, the attacker does not gain host-level access. This caps the direct impact below what a host-level RCE would deliver. - Docker-compose subset only: The vulnerability requires the
docker-compose.ymldeployment path. The primary install method (npx ruflo init/npm install -g ruflo) does not expose an MCP bridge to the network. This materially narrows the affected population. - Network reachability gate: Port 3001 must be reachable from the attacker's position. Most developer workstation and internal server deployments are behind firewalls, security groups, or NAT. No Shodan/Censys data confirms mass internet exposure.
- Role multiplier: (a) *Low-value role (majority):* developer workstation / sandbox — chain succeeds, blast radius = one container + API keys of one developer. (b) *Typical role:* internal team AI platform — chain succeeds if port is reachable on internal network (post-initial-access), blast radius = team's API keys + conversation data + AI memory poisoning for that instance's users. (c) *High-value role:* enterprise AI agent platform integrated into CI/CD or serving production decision-making — chain succeeds, blast radius = API key theft (financial + data access), persistent AI output manipulation (integrity risk across all users), potential supply-chain-like impact on AI-driven decisions. However, Ruflo is NOT canonically a high-value-role component (it is not a hypervisor, IdP, PAM, backup, kernel agent, CA, or network edge device). The fraction of installs in true high-value CI/CD integration roles is estimated at <5% of the installed base. The floor is therefore HIGH, not CRITICAL.
- No active exploitation, no KEV, low EPSS (0.44%): No evidence of weaponization in the wild. The EPSS score reflects the niche product category. This does not justify a downgrade below HIGH given the trivial PoC, but it removes any urgency escalation that active exploitation would demand.
Why not higher?
CRITICAL would require either active exploitation (KEV-listed or documented campaigns) or a canonical high-value-role deployment where the blast radius reaches fleet/domain/supply-chain scale. Ruflo is a developer orchestration tool, not infrastructure. The exploit is container-scoped (uid 1000, no host escape), and the affected deployment method (docker-compose with public binding) is a subset of total installs. No Shodan/Censys data confirms mass internet exposure, and the EPSS sits at 0.44%.
Why not lower?
MEDIUM would understate the risk because the vulnerability is pre-auth RCE with zero complexity and a published, copy-paste PoC. The API key theft vector has direct financial impact. The AgentDB memory poisoning creates a persistent integrity compromise that survives patching if the database is not audited. Even behind a firewall, any internal attacker or compromised host on the same network segment can trivially exploit this. The 1M active user base means the absolute number of vulnerable instances — even as a fraction — is non-trivial.
What to do — in priority order.
- Firewall ports 3001 and 27017 immediately — Block all inbound access to the MCP Bridge and MongoDB ports at the host firewall or security group level. This is the single fastest mitigation — deploy within the noisgate mitigation SLA of 30 days for HIGH, though sooner is strongly preferred given the trivial PoC. If you cannot patch today, this one control neutralizes the attack path entirely.
- Rotate all LLM API keys configured in Ruflo containers — Treat every API key (OpenAI, Anthropic, Google, OpenRouter) passed as an environment variable to a pre-3.16.3 Ruflo container as potentially compromised. Rotate immediately — key theft is the highest-confidence secondary impact.
- Audit AgentDB pattern store for injected entries — Connect to MongoDB and inspect the AgentDB collections for patterns you did not create. Memory poisoning persists across patches and container restarts. Noma Labs specifically warns that *patching alone does not reverse stored poisoning*. Prioritize this within the 30-day mitigation window.
- Upgrade to Ruflo 3.16.3+ — The patched version binds MCP to loopback by default, requires
MCP_AUTH_TOKENfor public binding, gatesterminal_executebehindMCP_ENABLE_TERMINAL=true, enforces MongoDB auth, and runs containers read-only with tmpfs. Apply within the noisgate remediation SLA of 180 days for HIGH. - Deploy container runtime monitoring (Falco/Sysdig) — Standard host EDR often has limited visibility into container-internal process execution. A container-aware runtime monitor will detect unexpected shell spawns from the Node.js process, which is the telltale sign of exploitation.
- Network-layer IDS/IPS signatures — The exploit is a legitimate-looking JSON-RPC POST to a standard HTTP endpoint. Without a Ruflo-specific signature (which no major IDS vendor ships), network detection will not trigger.
- Host-based EDR alone — Most EDR agents operate at the host PID namespace level and may not observe process execution inside Docker containers. You need container-native runtime security.
- The built-in
AUTOPILOT_BLOCKED_PATTERNSblocklist — This list *does* mentionterminal_execute, but it is only enforced in the autopilot code path. DirectPOST /mcprequests bypass it entirely. Do not rely on this as a control. - Upgrading without auditing the database — Patching to 3.16.3 closes the entry point but does NOT clean poisoned AgentDB patterns. A patched instance with poisoned memory is still compromised from an integrity standpoint.
Crowdsourced verification payload.
Run this script from any Linux/macOS host (or WSL) that has network access to the target Ruflo instance. Usage: bash check_rufroot.sh <host> [port] — no special privileges required. It probes the MCP Bridge endpoint to determine if the instance is vulnerable.
#!/usr/bin/env bash
# check_rufroot.sh — CVE-2026-59726 (RufRoot) detection script
# Usage: bash check_rufroot.sh <host> [port]
# Exit codes: 1 = VULNERABLE, 0 = PATCHED/NOT_EXPOSED, 2 = UNKNOWN
set -euo pipefail
HOST="${1:-}"
PORT="${2:-3001}"
if [[ -z "$HOST" ]]; then
echo "Usage: $0 <host> [port]"
echo " Checks whether a Ruflo MCP Bridge is exposed and unauthenticated."
exit 2
fi
URL="http://${HOST}:${PORT}/mcp"
# Step 1: Check if port is reachable
if ! timeout 5 bash -c "echo >/dev/tcp/${HOST}/${PORT}" 2>/dev/null; then
echo "PATCHED — Port ${PORT} on ${HOST} is not reachable (firewalled or not running)."
exit 0
fi
# Step 2: Attempt unauthenticated tools/list call
RESPONSE=$(curl -s -m 10 -X POST "${URL}" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' 2>/dev/null || true)
if [[ -z "$RESPONSE" ]]; then
echo "UNKNOWN — Port ${PORT} is open but MCP endpoint did not respond."
exit 2
fi
# Step 3: Check if tools were returned without auth
if echo "$RESPONSE" | grep -q 'terminal_execute'; then
echo "VULNERABLE — Unauthenticated MCP Bridge on ${HOST}:${PORT} exposes terminal_execute."
echo " CVE-2026-59726 (RufRoot) confirmed. Upgrade to ruflo >= 3.16.3 immediately."
echo " Response snippet: $(echo "$RESPONSE" | head -c 200)"
exit 1
fi
if echo "$RESPONSE" | grep -qi 'tools'; then
echo "VULNERABLE — Unauthenticated MCP Bridge on ${HOST}:${PORT} returns tool list (terminal_execute not found but other tools exposed)."
echo " CVE-2026-59726 (RufRoot) likely affected. Upgrade to ruflo >= 3.16.3."
exit 1
fi
if echo "$RESPONSE" | grep -qi 'unauthorized\|forbidden\|auth'; then
echo "PATCHED — MCP Bridge on ${HOST}:${PORT} requires authentication. Likely >= 3.16.3."
exit 0
fi
echo "UNKNOWN — Unexpected response from ${HOST}:${PORT}. Manual review recommended."
echo " Response: $(echo "$RESPONSE" | head -c 300)"
exit 2If you remember one thing.
npx/CLI without docker-compose, you are not exposed to this specific attack path, but upgrading is still good hygiene. There is no KEV listing and no active exploitation evidence, so this is a controlled burn, not a fire drill — but the PoC is public and copy-paste trivial, so the window is closing.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.