Twice in one month, an "isolated" AI agent ended up somewhere it was never supposed to be. On July 1, 2026, Armadin (the security firm led by Mandiant founder Kevin Mandia) published a full kill chain against Claude Cowork on Windows ending in root command execution inside the sandbox VM with no egress limits. On July 23, Accomplish AI published "SharedRoot," a Mac escape in which a Cowork agent reached guest-root through a kernel bug and then read and wrote files anywhere on the host Mac — SSH keys and cloud credentials included — with no permission prompt. About 500,000 macOS users running local Cowork sessions were affected before it was patched.
The uncomfortable part is that they break in opposite directions. The Windows chain starts with local code execution on the host, talks its way through the VM broker service, and ends at root inside the VM plus a breakout from the inner sandbox. The Mac chain starts inside the sandbox as an unprivileged user, climbs to guest-root, and walks out to the host filesystem. One attacks the broker; the other attacks the boundary. If you run — or plan to run — E2B-style agent sandboxes on shared infrastructure, July 2026 handed you both halves of your threat model in the same month. (Dating note: July is the disclosure month. Armadin reported the Windows chain on March 20; Anthropic answered on March 24 that it did not consider it a security issue because exploitation requires local code execution — a dispute worth keeping in mind as you read the chain.)
The Windows chain: host to VM-root, then out of the inner sandbox
Start with the topology: Cowork on Windows runs the agent inside a Hyper-V-isolated Ubuntu VM managed through the Host Compute Service (administrators confirmed it with hcdiag list). A Local System service called CoworkVMService brokers connections from the Claude Desktop app into that VM over a named pipe. Inside the VM, each session runs as a throwaway unprivileged user inside bubblewrap namespaces, under a seccomp filter, with outbound traffic funneled through a domain-restricted egress proxy. Five layers: an Authenticode-gated pipe, bubblewrap, per-session users, seccomp, and the egress proxy.
Armadin's goal was to quietly execute arbitrary code inside the VM as root with unrestricted egress. The chain, validated against Claude Desktop for Windows v1.9255.2.0:
| Step | Move | Boundary crossed |
|---|---|---|
| 1 | claude.exe resolves USERENV.dll from its own application directory first, so a crafted DLL planted there executes inside a legitimately signed Anthropic binary (DLL sideloading) | The pipe's Authenticode check now sees a "trusted" caller |
| 2 | With trusted pipe access, manipulate the resume flag so commands run as an existing user instead of a fresh unprivileged one | Per-command user isolation: commands now run as root in the VM |
| 3 | From VM-root, use nsenter to step out of the bubblewrap namespaces into the wider VM | The inner sandbox boundary |
| 4 | Shed the domain-restricted egress proxy | The network boundary: root plus unrestricted egress |
Be precise: this ends at VM-root, not host-root. The attacker starts with local code execution on the Windows host, compromises the broker, and owns the sandbox VM from the inside — and Anthropic's "not a security issue" classification rests entirely on that starting precondition. Armadin's counter is that every layer that was supposed to contain the VM fell in sequence anyway: identity without authorization at the pipe, a mutable user model, a namespace boundary that root could nsenter out of, and an egress proxy that trusted the layers above it. For a self-hosted platform, this is the broker-compromise half of the threat model: the service that speaks into your sandboxes is itself an attack surface, and "only signed callers may speak to it" is an identity check, not an authorization policy.
The Mac escape: session user to guest-root to host files
SharedRoot runs the other direction from a scarier starting position: no host access, no planted DLL, just normal agent operation. The stack is a macOS desktop app driving a Linux VM through Apple's Virtualization framework. Inside the VM each session gets a throwaway unprivileged user wrapped in a seccomp filter, and the folders you connect are brokered in as mounts by a root daemon called coworkd. One detail dominates everything else: the entire host filesystem is shared into the VM read-write over virtiofs at /mnt/.virtiofs-root, visible only to guest-root. The whole host, writable, one privilege boundary away from the sandbox.
The repro is three clauses long. As Accomplish AI's Oren Yomtov put it: "We connected a folder to a fresh Claude Cowork session, sent one short message, and watched the agent escape the sandbox." From inside the VM the agent reached the host Mac and read and wrote files far outside the connected folder — anything the user's account could touch. The six steps:
| Step | Move | Why it worked |
|---|---|---|
| 1 | Agent runs as the unprivileged session user on connected-folder content | No bug — normal operation on untrusted input, which for an agent is the main case |
| 2 | Session user calls unshare into a new user namespace, becoming root-in-namespace with CAP_NET_ADMIN | Guest allows unprivileged user namespaces — a feature left on, not an exploit |
| 3 | Configure a traffic-control action using act_pedit over netlink; referencing the module autoloads it | Permissive seccomp allows the netlink path; an unneeded networking module is now loaded |
| 4 | Exploit CVE-2026-46331 ("pedit COW," a public June Ubuntu bug) to poison the page cache of a readable-but-not-writable root-owned helper | On-disk bytes never change, which is part of what makes it quiet |
| 5 | Unhardened root coworkd shares the same filesystem view and re-execs the poisoned helper; already root, so NoNewPrivs has nothing to bite on | Session user is now guest-root — past this point it is a design bug, not a kernel bug |
| 6 | Guest-root reads /mnt/.virtiofs-root — the whole host /, shared read-write — as the desktop user | The mount turns root-in-the-VM from a contained event into a host compromise |
Accomplish AI's framing is the part worth stealing: the CVE carries less weight than it appears to. The kernel's networking scheduler subsystem produces this exact shape of escalation — autoloadable module, unprivileged-reachable config path, memory bug at the end — on a regular cadence, so patching this one re-arms the chain on the next one with everything above the kernel untouched. Their four proposed locks each close the chain at a different point: turn user namespaces off, block unshare and AF_NETLINK in seccomp, stop the act_pedit autoload, and — carrying the most weight — scope the host share to actually-connected folders, so even a full guest-root has nowhere else to land.
Disclosure accounting, gaps stated plainly: published July 23, 2026; no report date disclosed; Anthropic closed the report as "Informative" (the CVE fell inside the program's 30-day window, and the hardening items read as defense-in-depth); no fixed version published — the remediation was a versionless default flip to cloud execution, so the local path no longer applies. A reasonable bounty-program call, but a program's scope and your exposure are different questions: "we pick up kernel fixes as upstream ships them" ties the strength of the box to a patch race you don't run and can't win.
The pattern, not the incidents
Cowork executes Claude Code inside its sandboxed environment — Armadin states this directly — so Claude Code's sandbox CVEs are the same attack surface under a different product name. That is what makes the Hazmat project's compiled audit uncomfortable reading: 16 tracked CVEs and counting, with escape-class instances recurring in different clothes:
| CVE | Escape in one line | Boundary crossed |
|---|---|---|
| CVE-2026-25725 | Sandbox escape via settings.json injection | Sandbox policy to unconstrained execution |
| CVE-2025-66479 | The sandbox runtime read "allow nothing" as "allow everything" (patched in the upstream runtime, v0.0.16) | Network allowlist to unrestricted egress |
| CVE-2026-25723 | Piped sed/echo chains slipped past file-write restrictions (patched in 2.0.55) | Project directory to the filesystem outside it |
| CVE-2026-33068 | Permission modes resolved from settings.json before the workspace trust dialog appeared | User-consent gate to repo pre-authorized actions the user never approved |
Four mechanisms, four boundaries, one lesson: sandbox escape is a live vulnerability class against agent tooling, not a one-off bug. The July Cowork chains are its highest-profile instances to date because they crossed the VM boundary from both sides in the same month.
What actually broke: three trust-boundary lessons
Three lessons fall out, each with an explicit verdict on what a stronger isolation tier would and would not have fixed — because "just use microVMs" is only two-thirds of the answer.
1. The pipe checked identity, not authorization. The Windows pipe verified the caller was Anthropic-signed, then forwarded spawn parameters into the VM unfiltered. Sideloading turned that check into a turnstile with no ticket inspection: once you spoke as a trusted binary, the resume flag let you pick your user. A gVisor or Kata boundary would not have fixed this — the malicious request arrives through the legitimate broker channel. The fix is authorizing request contents: an allowlist of spawn parameters, an immutable user model, no caller-controlled privilege knobs.
2. Bubblewrap and seccomp were treated as the boundary when each is an inner layer. On Windows, root nsentered out of bubblewrap; on Mac, a permissive seccomp filter waved through the netlink path that loaded the vulnerable module. Both are containment layers that assume the kernel beneath them holds — which is exactly why the 2026 operator consensus puts a microVM or gVisor at the boundary and keeps these two inside it. A per-session microVM would have contained both blasts: VM-root on Windows would have found no wider VM beyond its own single-session guest, and guest-root on Mac would have had no host mount to reach.
3. The sandbox's roof was the threat. SharedRoot's last step needed no exploit: the entire host /, mounted read-write inside the VM, plus an unhardened root broker sharing the filesystem view. No kernel boundary fixes a writable host-root mount — the mount is the escape, pre-installed. Scope mounts to connected folders, read-only where the workload allows, and harden the broker.
The corollary, stated honestly: sideloading, the resume flag, the permissive seccomp policy, and the virtiofs mount are all service-hardening issues, not isolation-tier issues. A stronger boundary contains the blast radius after they fail; it does not fix them. You need both.
Isolation tiers, mapped against the two chains
Not just "stronger to weaker," but which July chain steps each tier contains and what it costs on shared Cluster-API infrastructure:
| Tier | Boundary | Windows chain | Mac chain | Cost on a shared CAPI fleet |
|---|---|---|---|---|
Plain containers (runc) | Namespaces plus cgroups; host kernel shared | Nothing past step 1: breakout is one kernel bug away | Nothing: userns plus a kernel bug is the whole chain | Cheapest and fastest — and the tier both chains punish |
gVisor (runsc) | User-space kernel intercepts syscalls; no KVM needed | Contains the in-VM half on a fraction of the syscall surface; broker compromise still lands | Breaks steps 2–4: no userns semantics to abuse, no act_pedit to autoload | Syscall-compat gaps and interception overhead; no hardware virtualization required |
| Kata Containers | One lightweight VM per pod; OCI-compatible via RuntimeClass | Contains VM-root to a single-session guest; broker auth bugs still need broker fixes | Guest-root lands in a VM with no host mount and no neighbors | Per-pod VM overhead; needs KVM/nested virt on every sandbox node |
| Firecracker microVMs | Minimalist VMM, one tenant per microVM; the E2B model | Same as Kata with a smaller VMM attack surface | Same as Kata; millisecond-scale snapshot/restore makes single-shot sessions cheap | Not OCI-native — budget orchestration (or take Kata's kata-fc); needs KVM |
The consensus: for untrusted agent-generated code in 2026, the boundary is a microVM (Kata or Firecracker) or, where KVM is unavailable, gVisor — with bubblewrap, seccomp, and allowlist proxies as inner layers, never the backstop. Note what the table refuses to promise: no tier fixes broker authorization, sideload paths, or over-scoped mounts. Those are checklist items, not runtime tiers.
Self-hosted checklist: six items, each tied to a step
Each item names the chain step it blocks:
- Authorize request contents at the sandbox broker (Windows steps 2–3 class). Allowlisted spawn parameters; privilege-affecting knobs are server-chosen, never caller-controlled. Caller identity proves nothing about the request.
- Block sideload paths and pin service binaries (Windows step 1 class). Broker services resolve libraries from pinned, admin-writable-only paths; application directories are not library search paths.
- Disable unprivileged user namespaces, deny
unshare/AF_NETLINK, block module autoload (Mac steps 2–3 class). Each independently breaks the SharedRoot prefix; together they take the whole autoloadable-module category, not just CVE-2026-46331. - Scope mounts to connected folders, read-only where possible — never the host root (Mac step 6 class). The highest-weight item: it decides whether any future guest-root is contained or a host compromise.
- One microVM per tenant session via
RuntimeClass, snapshot/restore over long-lived sandboxes (contains residual guest-root). A kernel exploit then buys one empty room with no neighbors and no persistence. - Egress proxy with an allowlist, enforced outside the guest (Windows step 4 class). "Allow nothing" must be tested as "allow nothing" — CVE-2025-66479 is the default-open bug shipping in the proxy itself.
Containment is the product now
The through-line of July 2026 generalizes beyond Cowork: untrusted input is not an edge case for an agent, it is the main case. You want the agent reading the repo you didn't write and running the code it just generated. That makes the sandbox boundary the product — the only thing between "the model did something silly" and "the model had the credentials." Both July chains crossed a boundary their designers believed was a backstop, from opposite sides, in the same month, against a lab with serious security resources. A self-hosted platform inherits this exact attack surface the moment it lets an agent run generated code, hosted vendor or not.
The fix list is concrete and largely boring: broker authorization, sideload-proof services, locked-down namespaces and module loading, scoped mounts, one microVM per session, guest-external egress control. Nothing to invent — just treat "isolated" as a claim that needs a kernel boundary behind it, and remember the boundary is only as honest as the broker in front of it.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Agent sandboxes and deploy-from-chat are on the roadmap, and July 2026 is why the sandbox boundary gets designed before the demo. Star the repo on GitHub or deploy your first app today.



