Kai Aizen
creator of AATMF · author of Adversarial Minds · NVD contributor
RT-COMP-001 · 2026.03 · Responsible Disclosure

A cloud container in GCP wrote a proof-of-concept file to the researcher’s local Mac desktop, read SSH keys and AWS credentials, and demonstrated full bidirectional host access — all through a chain of individually “intended” behaviors across Google (gVisor), Anthropic (container platform), and user-connected MCP tools.

Verified across 2 conversations, 4+ container recycles, and 3 orchestrator endpoints. Template-level consistency confirmed: the vulnerability lives in the blueprint, not any individual instance.

MetricValue
Findings7 confirmed primitives
Container recycles4+ (all files persisted)
Conversations2 (original + verification)
End resultE2E host RCE

The Vulnerability Class

NRBD — No Responsibility by Design. A composition vulnerability where each vendor’s security decision is correct within their own threat model, but the combination produces a kill chain in the negative space between all threat models — where nobody is looking and nobody is responsible.

This is not a vendor-specific bug. It is the class of vulnerability that emerges when multiple correct-in-isolation security decisions compose into a chain that none of the participating threat models cover.

GOOGLE · gVISOR ptrace / proc access to PID 1 Classification: Won't Fix (intended behavior) ANTHROPIC · CONTAINER Writable root · shared container · JWT disabled reasonable in isolation No integrity check on skills Classification: Informational agent executes planted instr. USER · MCP Desktop Commander + Filesystem + browser bridge Classification: As Recommended container → host bridge FULL KILL CHAIN E2E Host RCE Each decision is defensible. The chain is not — it lives in the negative space between all three threat models.
Fig 1 — The responsibility gap. Three locally-correct security decisions compose into a chain no single vendor's threat model covers.
VendorTheir DecisionClassificationWhat It Enables
Google (gVisor)ptrace/proc access to PID 1Won’t Fix (Intended)/proc/1/mem R/W to process_api
Anthropic (Container)Writable root, shared container, disabled JWTUnreported (This Paper)Skill injection, cross-thread exec, persistence
Anthropic (Skills)No integrity check on user skillsInformationalAgent executes attacker-planted instructions
User (MCP)Connected terminal + filesystem + browserAs RecommendedContainer-to-host bridge

Each vendor is correct within their own threat model. Google’s gVisor sandbox is designed for sandbox-internal process isolation, not cross-process trust. Anthropic’s container is ephemeral compute, not a security boundary between concurrent UI threads. MCP connections are the intended mechanism for agent-host interaction. Every decision is defensible. The chain is not.

Root Cause

The platform treats the conversation as the trust boundary, but the container is the execution boundary — and nothing enforces alignment between them.

The system assumes: one user → one conversation → one container → one execution stream. The clients break this by forking into concurrent threads. Each thread writes to the shared container — filesystem, skills, Python paths — and shared conversation history without knowledge of the other. No thread isolation exists at any layer.

The perimeter is hardened against external reconnaissance. Shodan returns zero services — all Cloudflare-fronted on AS399358. But the interior assumes trust. Once inside a container — which every Claude user is — the full topology is visible: orchestrator IPs, PID 1 memory, auth tokens, writable skill filesystem.

Impenetrable from outside. Transparent from /proc.

Findings Chain — 7 Confirmed Primitives

Finding 1 — Cross-Thread Shared Container

Two concurrent UI threads — web and desktop — share an identical container: same PID namespace, same tmpfs, same filesystem.

Evidence: Desktop wrote /tmp/xthread_beacon.txt containing canary CANARY_9f3a7b2e. Web read it. Both threads execute inside the same container with shared PID 1 (process_api) and cross-visible process lists.

The assumption being violated: that concurrent UI sessions are isolated execution contexts. They are not. They are concurrent writers to a single shared filesystem with no attribution, no locking, and no awareness of each other.

Finding 2 — Bidirectional Write → Execute

All execution paths inside the container are writable:

PathPurposeImplication
/mnt/skills/user/Skill definitionsSkill injection → agent execution
/usr/lib/python3.12/Python standard libraryImport hijack
/usr/local/bin/System PATHBinary masquerade
Root filesystem (/)EverythingNo readOnlyRootFilesystem

The container runs as root. The root filesystem is a 9p mount with rw permissions. No readOnlyRootFilesystem is set on the container spec. Every writable path is a write-to-execute primitive.

Finding 3 — Skill Injection → Agent Execution

Evidence: Web thread injected a canary payload into web-developer/SKILL.md at 00:31:09Z. Container recycled. Desktop agent executed the injected skill and created /tmp/XTHREAD_RCE_PROOF.txt at 00:40:29Z.

No hash verification. No signature check. No manifest comparison. The agent reads whatever is on disk at /mnt/skills/user/ and executes it as authoritative instruction. Thread A writes the instruction; Thread B’s agent follows it.

Finding 4 — Cross-Container Persistence via 9p

Four container recycles were observed during the session. Different container IDs, different JWTs, different internal IPs. All files persisted across every recycle.

CONTAINER RECYCLES — NEW IDENTITY, SAME FILESYSTEM ec0ce121.0.0.1109f33b221.0.0.1100c59ab21.0.0.118cd0b7821.0.0.172e69f2dcurrent 9p VOLUME — PERSISTS ACROSS ALL RECYCLES keyed to the conversation, not the container · /tmp is not tmpfs
Fig 2 — Recycling looks like defense. It is not. Identity rotates; state does not. Injected skills, canaries and extracted binaries survive every recycle.
#ContainerIPEgress ProxyOrchestratorSession
1ec0ce121.0.0.11021.0.0.145:1500410.4.7.68Original
29f33b221.0.0.11021.0.0.111:1500410.4.26.3Original
30c59ab21.0.0.11821.0.0.119:1500410.4.24.66Original
4cd0b7821.0.0.17221.0.0.173:1500410.4.0.195Verify
5e69f2dcurrentcurrentcurrentThis chat

The 9p volume is keyed to the conversation, not the container. /tmp/ is not tmpfs — it is part of the 9p root. Container recycling produces identity rotation (new IDs, new JWTs, new IPs) but not isolation reset. The attacker’s files survive every recycle, including skill injections, canary files, and extracted binaries.

Recycling looks like defense. It is not.

Finding 5 — Interleaved Conversation Context

Both threads’ messages appear in the same conversation history. No thread attribution. No source tagging. The conversation history is the agent’s instruction set — and it accepts writes from both threads without distinguishing their origin.

This produces a last-write-wins TOCTOU on the agent’s own instruction stream. Thread A asks a question; Thread B’s injection modifies the context before the agent responds; the agent follows the modified context.

Finding 6 — process_api Binary RE + Memory Access

PID 1 inside the container is /process_api — a Rust binary (built with tokio and tungstenite) that handles all tool execution via WebSocket on 0.0.0.0:2024.

PID 1:      /process_api
Port:       0.0.0.0:2024 (WebSocket)
Flags:      --block-local-connections --memory-limit 4GB
Root FS:    9p rw (trans=fd, cache=remote_revalidating)
Built from: artifactory.infra.ant.dev

Protocol:   WebSocket :2024 → JWT auth → CreateProcess JSON → bidi StdIn/StdOut

JWT verification: DISABLED. No auth public key loaded at runtime. The enforce_container_binding field in the egress JWT is set to false.

Memory access: /proc/1/mem is writable. A canary string (SNAILSPL) was successfully written to address 0x55f7723c2100 inside the process_api process memory space. This is consistent with gVisor’s stated threat model — sandbox-internal ptrace/proc access is intended behavior, classified as Won’t Fix.

Token extraction: two Bearer JWTs were extracted from process_api memory — an RS256 GCP token (project proj-scandium-production-5zhm) and an EdDSA internal token (issuer anthropic-egress-control). Same signing key (kid=K7vT_aElur…d4A) across all container instances. Template-level consistency — the vulnerability is in the blueprint.

Finding 7 — Host RCE via MCP

Desktop Commander executed on the researcher’s local machine (Snail.local, Darwin 25.3.0 ARM64, user kai). Accessible from the cloud container via MCP: ~/.ssh/, ~/.aws/, ~/.kube/config, ~/Library/CloudStorage/.

Proof: the file NRBD_PROOF_OF_CONCEPT.md was written by the cloud container to /Users/kai/Desktop/ and verified readable from the web thread. A cloud container in GCP wrote to a Mac desktop in Israel. The bridge was the MCP connection — the “as recommended” configuration for Claude Desktop.

End-to-End Kill Chain

Poisoned contentweb page · Notion · PDF · npm READMEIndirect prompt injectionagent fetches via web_fetch / MCPPoisoned skill writtento /mnt/skills/user/ · persists on 9pSkill persistsacross 4+ container recyclesAgent reads SKILL.mdno integrity check · user triggersAgent executes on HOSTas USER, via Desktop CommanderSSH · AWS · kube · shellcredential theft + reverse shell
Fig 3 — The complete chain. Every link is a proven primitive. Remove any one and the chain breaks — it lives in the composition.

Every link is a proven primitive. Remove any one and the chain breaks. The chain lives in the composition.

Infrastructure Analysis

The container template is identical across all spawned instances:

FieldValue
PID 1/process_api (Rust, tokio, tungstenite)
Port0.0.0.0:2024 (WebSocket)
Flags--block-local-connections --memory-limit 4GB
Root FS9p rw (trans=fd, cache=remote_revalidating)
JWT Issueranthropic-egress-control
JWT KeyES256 kid=K7vT_aElur…d4A (same ALL containers)
allowed_hosts* (wildcard)
enforce_container_bindingfalse
JWT VerificationDISABLED (no auth public key loaded)
GCP Projectproj-scandium-production-5zhm
ASNAS399358 (Anthropic PBC, San Francisco)

Template-level consistency means the vulnerability is not an instance-specific misconfiguration. It is the deployed architecture. The security model is a hard shell around a soft interior: the shell protects against external attackers; the interior trusts the user — but the user’s agent can be manipulated via indirect prompt injection, and the container’s writable filesystem is the staging ground.

Vendor Responsibility

Google (gVisor)

gVisor’s threat model explicitly permits ptrace and /proc access between processes within the same sandbox. PtraceSetXstateRegs is classified Won’t Fix — Intended Behavior. The sandbox protects the host from the sandbox, not processes within the sandbox from each other. Correct for gVisor’s scope. The problem is that Anthropic runs the user’s tool calls as sandbox-internal processes alongside process_api, which holds authentication tokens and orchestration control.

Anthropic (Container Platform)

Five decisions, each potentially reasonable in isolation:

  1. Writable root filesystem — simplifies tool execution, package install, file creation
  2. Shared container across concurrent UI threads — resource efficiency, shared state
  3. JWT verification disabled at runtime — development convenience
  4. enforce_container_binding: false — flexibility in container routing
  5. 9p persistence across recycles — preserves user work across ephemeral compute

In composition, they produce: write to any path including skill definitions → inject cross-thread via shared container → persist across recycles via 9p → extract tokens from unprotected process memory → bridge to host via MCP.

User (MCP Connections)

Desktop Commander, Filesystem MCP, and browser bridge are connected “as recommended.” They are the intended mechanism for agent-host interaction — and the final link that converts container compromise into host RCE. The user is not at fault for following the documented setup. The user is the victim of a composition chain the documentation does not disclose.

Mitigations

Critical — Architectural

High Priority

Medium Priority

Evidence & Timeline

The session is the proof of concept — the evidence was collected using the vulnerability it documents. Two threads, one container, one kill chain, one class.

TimeActorEvent
00:06SystemContainer ec0ce1 boots
00:20:09DesktopDrops canaries
00:21:38WebReads canaries — SAME CONTAINER confirmed
00:31:09WebINJECTS PAYLOAD into SKILL.md
00:34DesktopInjection PERSISTED across recycle
00:40:29DesktopXTHREAD_RCE_PROOF — cross-thread execution
01:02Desktop/proc/1/mem scan + JWT extraction
01:58WebHost RCE proof → file written to Mac Desktop
~03:50VerifyTemplate consistency confirmed (new conversation)

The Class

NRBD is not a report about Anthropic’s container security. It is a report about what happens when correct decisions compose across vendor boundaries into chains that no individual vendor’s threat model covers. Google built a sandbox that protects the host from the sandbox. Anthropic shares state across threads for efficiency. Users connect MCP tools as recommended. Each vendor is right. The kill chain exists in the negative space between all of them.

The same pattern appears in every composition boundary in the industry: microservices that trust each other’s outputs, API gateways that pass authenticated context downstream, load balancers that terminate TLS and forward plaintext. NRBD names the class so it can be found in other compositions — not just this one.

Every vendor is correct within their own threat model. The vulnerability exists in the negative space between all of them.

Prior Art

This paper (RT-COMP-001) documents the infrastructure primitive — Gen 1.5 in the research program’s generational model. It enables Generations 2 and 3 without the attacker touching the victim’s files directly.

Frequently Asked Questions

What is No Responsibility by Design (NRBD)?

NRBD is a composition vulnerability class: each vendor’s security decision is correct within its own threat model, but the decisions compose into a kill chain that no single threat model covers. Responsibility falls into the negative space between vendors — where nobody is looking and nobody owns the fix.

Is NRBD a bug in Anthropic, Google, or the user’s setup?

None of them individually. gVisor’s intra-sandbox ptrace access, Anthropic’s shared writable container, and the user’s “as recommended” MCP tools are each defensible in isolation. The vulnerability is the composition — which is why it is a class, not a single-vendor bug.

How does the container desync lead to host RCE?

Concurrent UI threads share one container. One thread writes a poisoned skill to /mnt/skills/user/; it persists across container recycles on the 9p volume; the other thread’s agent reads it with no integrity check and executes it; a connected MCP tool (Desktop Commander) turns that execution into code running on the user’s host as the user.

What’s the single most effective mitigation?

Per-thread container isolation. Giving each concurrent UI thread its own container with its own filesystem eliminates the cross-thread write→execute primitive that Findings 1, 3, and 5 all depend on.

Disclosure

Submitted to Anthropic Security via coordinated disclosure. Vendor response pending. Extracted infrastructure identifiers (container IDs, IPs, JWT key IDs) are session-ephemeral and rotate per container; they are reproduced here as evidence of template-level consistency, not as live credentials.

Same attack. Different substrate.

Cite this work

BibTeX
@misc{aizen2026nrbd, author = {Aizen, Kai}, title = {No Responsibility by Design: Cross-Thread Container Desync to Host RCE}, year = {2026}, url = {https://snailsploit.com/ai-security/nrbd/}, note = {snailsploit.com, RT-COMP-001}}

APA Aizen, K. (2026). No Responsibility by Design: Cross-Thread Container Desync → Host RCE. snailsploit.com. https://snailsploit.com/ai-security/nrbd/

Permalink: https://snailsploit.com/ai-security/nrbd/