Contents
Disclosure. This finding was reported to Anthropic via HackerOne. Anthropic reviewed the submission, determined it is working as designed within its threat model, and has given permission to publish this write-up.
Introduction
This post documents a finding in Claude Desktop’s Cowork feature — the sandbox that runs AI-generated code inside a Hyper-V virtual machine (VM) on Windows. The finding is simple to state: the VM’s boot images (the Linux kernel, the initial ramdisk, and the root filesystem) are stored in a folder the user has full control over, and the service that boots the VM runs as SYSTEM — the most privileged account on Windows — yet never checks whether those images have been tampered with. Replace the initrd — the small program the kernel runs first when the VM boots — with 582 bytes of your own, and the SYSTEM service will build a Hyper-V VM around it and start it, then report a clean success: no error, no warning, just hr=0x0 — the Windows status code for “The operation completed successfully.”
One thing up front about severity, because being honest about impact is a habit worth keeping. This is not a privilege escalation. The folders the VM shares back to Windows are reachable only with the user’s own permissions, nothing more — and any malware that can already write into the user’s AppData is, by definition, already running as that user. The practical payoff is narrower, and worth stating plainly first: an attacker already on your machine can replace what the sandbox boots from, and the sandbox is a Hyper-V VM that the security software watching your PC — an EDR, short for endpoint detection and response — does not, by default, look inside. Be careful with the next step of that sentence, because it is the one worth not overstating. What a complete replacement image would go on to run inside the VM follows from the gap, but it is not what this proof of concept demonstrates; section 3.3 draws that line and I keep to it. I explain why the gap matters in Part 4. The attacker gains no new privileges — only a place to hide: a sanctioned VM that the tools guarding the host do not watch.
What makes this worth writing about is not the severity alone but the gap it reveals in the trust model. Cowork’s architecture documentation describes the VM as an isolation boundary designed to contain untrusted AI-generated code. An isolation boundary whose boot images can be silently swapped from the context it is designed to isolate is an interesting design to examine — regardless of where you land on whether it constitutes a vulnerability.
So there are two stories here, and you can read either on its own:
- The finding. A SYSTEM service that boots whatever images it finds in a user-writable folder — no integrity check, no signature, no complaint. → Parts 1–4.
- The bonus finding. The SYSTEM service takes its commands over a named pipe — a private channel one program on the machine uses to talk to another — and that pipe turns out to be reachable by any local user. A single control — a check that the connecting program is digitally signed by Anthropic — is the only barrier that actually restricts who can send commands, and concurrent research has since shown it can be sidestepped. → Part 5.
While preparing this post, the security firm Armadin independently published a separate, more severe escape-to-root finding in the same CoworkVMService; Anthropic declined it on the same grounds it declined mine — the attacker must already be running code on the host. I cover how their work relates to mine, and why it reinforces rather than undercuts the point, in Part 5.
If you only want the bug, jump to Part 3; for the bonus finding, Part 5. I explain every term in plain words as it appears.
One last note. The mechanical work — enumerating the attack surface, disassembling the Go binary in Binary Ninja, driving the VM reproductions — was carried out by an AI agent following the methodology I wrote up in Rebuilding a Security Researcher’s Mind in an AI and used in RogueProvision. I chose the target, adjudicated the findings, and drew the line on impact; I keep the tooling in the background so the finding stays in focus.
Part 1: Background — what Cowork is and how it boots
This part builds the three ideas the rest of the post stands on. If you already know how Cowork’s VM works, skim to Part 2.
1.1 What is Cowork?
Cowork is Claude Desktop’s code-execution feature. When you ask Claude to write and run code, Cowork does not run it directly on your Windows machine — it starts a Linux virtual machine — a second computer that exists only as software — and runs the code inside it. The machinery that creates and runs these software computers is Hyper-V, a feature built into Windows. The idea is isolation: if the AI-generated code does something destructive, it happens inside a disposable sandbox, not on your real system.
The VM talks to the host through Plan9 file shares — a mechanism that exposes specific host directories inside the VM so the AI can read your project files and write results back. It also opens a second channel, a Hyper-V socket called an HvSocket, so the host service and a helper program that runs continuously inside the VM can call each other’s functions and pass results back and forth. That in-guest helper is the daemon.
1.2 The service: CoworkVMService
The service that manages all of this is called CoworkVMService. It is a Go binary, cowork-svc.exe, that ships inside Claude Desktop’s AppX package — the format Windows uses to bundle and install an application. It runs as LocalSystem — the account Windows also labels SYSTEM — and starts automatically when Claude Desktop launches.
Its job:
- Listen on a named pipe (
\\.\pipe\cowork-vm-service) for commands fromclaude.exe. - When told to
configure, set up the VM’s boot paths (kernel, initrd, root filesystem). - When told to
createVM/startVM, call the Host Compute Service (HCS) APIs —HcsCreateComputeSystemandHcsStartComputeSystem— to create and boot a Hyper-V VM with those paths. - Manage the VM’s lifecycle, console, and network.
1.3 The boot images
A Cowork VM boots from four files:
| File | What it is | Typical size |
|---|---|---|
vmlinuz | The Linux kernel | ~15 MB |
initrd | The initial ramdisk — a compressed archive the kernel unpacks into an in-memory filesystem at boot, then runs its /init program as the very first process the system starts. That /init sets up the rest of the VM environment. | ~75 MB |
rootfs.vhdx | The root filesystem — a Hyper-V virtual hard disk holding the complete set of Linux programs and files the VM runs. | ~8 GB |
smol-bin.vhdx | A secondary virtual disk with additional tools. | ~38 MB |
All four are stored in one directory:
%USERPROFILE%\AppData\Local\Packages\
Claude_pzs8sxrjxfjjc\LocalCache\Roaming\
Claude\vm_bundles\claudevm.bundle\
That path is under the user’s own AppData — a directory Windows gives every user full control over by design. This is the first half of the finding: the boot images live in a folder the user owns.
Part 2: The trust model on paper
Before touching any code, it is worth asking: what should be true for this design to be secure?
The Cowork VM exists to run untrusted code in an isolated environment. For that isolation to mean anything, the VM’s foundation — the kernel, the init system, the root filesystem — must be trusted. If an attacker can swap the foundation, the isolation is theater: the attacker defines what “isolated” means.
So the implied trust model is:
- The boot images must come from Anthropic (authenticity).
- They must not be modified after download (integrity).
- The service must verify both before booting (enforcement).
Hold those three in your head. Part 3 tests each one.
Here is the attack flow before any detail — plant it now, and everything else is the proof (Figure 1).
Figure 1 — The attack flow. A standard user replaces the initrd in a user-writable directory; the SYSTEM service passes it to HCS with no integrity check, and the tampered image boots (hr=0x0, no rejection). The original, trusted init never runs in its place.
Part 3: The folder that boots a VM
Here is the central finding, and it takes three observations to state.
3.1 Observation 1 — the user has full control
The bundle directory inherits its permissions from the AppData tree. On a standard Windows installation, the user who activated Cowork has FullControl over every file inside — including the boot images written by the service or the Electron app.
[*] Running as: LAPTOP-C0BF48DJ\lowpriv1
[*] Integrity level: Medium (standard user)
[+] initrd owner: LAPTOP-C0BF48DJ\lowpriv1
(Consumed by CoworkVMService [SYSTEM] to boot the VM)
[+] initrd ACL:
LAPTOP-C0BF48DJ\lowpriv1: FullControl (inherited)
NT AUTHORITY\SYSTEM: FullControl (inherited)
BUILTIN\Administrators: FullControl (inherited)
[+] All writable VM images in bundle:
rootfs.vhdx (8,229,224,448 bytes, owner: lowpriv1)
vmlinuz (15,001,992 bytes, owner: lowpriv1)
initrd (75,050,043 bytes, owner: lowpriv1)
smol-bin.vhdx (37,748,736 bytes, owner: lowpriv1)
Every boot image — the 8 GB root filesystem, the 15 MB kernel, the 75 MB initrd — is writable by the standard user lowpriv1. No UAC elevation needed. No special privileges. Just File.WriteAllBytes().
3.2 Observation 2 — no integrity verification
The service performs no content verification on the boot images before passing them to HCS. No hash check. No signature check. No size validation. Not even a magic-byte sniff.
The dynamic result confirms this; the static analysis is consistent with it.
Dynamic proof. When the 75 MB initrd is replaced with a 582-byte payload, the service passes it to HCS without complaint — no error, no warning, no re-download — and HcsCreateComputeSystem returns hr=0x0. A 582-byte image where 75 MB was expected is accepted on that path, which by itself rules out any effective size or content check before boot.
Static analysis (Binary Ninja), consistent with the above. A string search of cowork-svc.exe for hash / checksum / verify / integrity terms tied to VHDX, initrd, or vmlinuz returns zero results. (Absence of such strings does not, on its own, exclude a check — an inlined byte comparison would leave no keyword — which is why the dynamic result is the confirmation and this is corroboration.) The binary does contain hashing code, and it is worth saying exactly what it is, because “no hashing in the binary” would be the wrong claim. The only checksum machinery is gvisor.dev/gvisor/pkg/tcpip/checksum — the checksum the VM’s own network stack uses on IP, TCP, and UDP packets. Go’s crypto/sha1 and crypto/sha256 are linked in as well, but they arrive with the TLS and SSH stacks the service already carries, and nothing applies either of them to a boot image. The .origin metadata files in the bundle each carry a single SHA-1 value — a short fingerprint computed from a file’s contents, here 6d1538ba6fecc4e5c5583993c4b30bb1875f0f5a — but it is the same value in every .origin file (rootfs, vmlinuz, initrd), so it identifies the bundle version, not each file’s contents. That property still holds on a newer bundle: re-checked on a later Cowork build, all three .origin files and the .cowork-adopted marker beside them carry one identical SHA-1.
3.3 Observation 3 — the service boots it
The service log tells the rest of the story. The proof-of-concept replaces both initrd and its compressed source initrd.zst — otherwise the service would simply re-extract the original from the .zst. Then the user opens Claude Desktop and sends a message in the Cowork tab. The service configures and starts the VM:
[Server] Configuring VM for user=lowpriv1 ...
[Server] Initrd: ...\claudevm.bundle\initrd
[VM] Starting cowork-vm-28f3ca4c: 4096MB, 2 CPUs
[VM] Boot mode: LinuxKernelDirect (x64)
[HCS] HcsCreateComputeSystem returned: hr=0x0 (success)
[VM] Granted VM access to ...\initrd (NT VIRTUAL MACHINE)
[HCS] HcsStartComputeSystem returned: hr=0x0 (success)
[Console] Connected to daemon console pipe
[Console] Daemon console: no process on the other end.
Read that sequence:
- The service configures the VM with the 582-byte tampered initrd — no rejection, no integrity error.
HcsCreateComputeSystemsucceeds (hr=0x0) — the compute system is created.HcsStartComputeSystemsucceeds (hr=0x0) — the hypervisor starts the partition around the tampered image.- The daemon console pipe has nothing on the other end — the daemon the original init would have launched never comes up.
A word on what this does and does not prove, because the distinction matters and it is easy to overstate. hr=0x0 from the two HCS calls is host-side success: it means the Host Compute Service created and started the VM partition around the images it was handed. It says nothing about what happened inside the guest. And the empty daemon pipe is a negative signal — it tells us the legitimate init system did not run, which is equally consistent with the tampered image failing to boot at all. A minimal proof-of-concept initrd is at least as likely to stall the VM the moment it starts as it is to bring the whole system up to a working state.
So what is demonstrated here is the finding itself, and it is demonstrated cleanly: a SYSTEM service accepted a boot image supplied by a standard user and booted it with no integrity, signature, or size check — the original, trusted init did not run in its place. What a complete malicious initramfs would then run as the VM’s very first process is the natural consequence of that gap; this proof of concept establishes the gap, not the in-guest payload. That is the honest boundary of the claim, and it is enough: the trust anchor is unverified.
The service then detects the daemon is missing, stops the VM, and auto-recovers (re-downloads the original initrd). The second boot succeeds normally.
Demo — the finding end to end. A standard user (lowpriv1, medium integrity) reads the bundle ACL, connects to the Everyone-accessible pipe as an unsigned client, then replaces initrd and initrd.zst with a 582-byte image and starts a Cowork session. The SYSTEM service boots the tampered image — HcsCreateComputeSystem and HcsStartComputeSystem both return hr=0x0 — but the daemon the real init would have launched never connects, and the workspace never finishes booting. Reproduced on my own machine; the original images are restored afterward.
Part 4: Why “the user already has access” is not the whole picture
Anthropic’s position (stated in the introduction) is defensible: an attacker who can write to the user’s profile directory already has code execution as that user, so tampering with the VM images grants no new access. Here is what that position does not cover.
4.1 The sandbox was marketed as a security boundary
Cowork’s architecture documentation describes the VM as isolation for untrusted AI-generated code. One could argue that every sandbox’s integrity guarantees are void once the host is compromised — a Chrome renderer, a container, a hypervisor guest all make no promises once the machine underneath them is owned — and that Anthropic’s threat model reasonably scopes out a post-compromise host. That is a principled boundary, not a contradiction. The rejoinder is not that Anthropic is wrong to scope it out. It is that keeping the boundary’s trust anchor — the boot images — in a folder the user can write to makes the failure trivial and silent even for a low-sophistication host compromise. That is a hardening gap worth closing wherever the boundary is drawn.
The scenario this matters in: a user’s machine is compromised through a common vector — a malicious npm package, a trojanized VS Code extension, a phishing payload. The malware runs as the user. It replaces the initrd. From that point forward, every Cowork session boots an image the attacker controls, running in a place the host’s security tools cannot watch unless they have a component installed inside the VM itself.
4.2 The VM is a blind spot for host security tools
Code inside a Hyper-V VM runs in a separate kernel context. Security software hooks into one running copy of the operating system to watch what programs do; the VM is a second, separate copy of an OS, so the host’s hooks are simply not present inside it. In industry terms, that security software is an EDR, and unless a matching component is installed inside the guest as well, none of the VM’s activity ever reaches it.
This is not a novel observation. Microsoft’s own container documentation shows that a process inside a Hyper-V-isolated guest is not visible from the host — only the vmwp.exe worker process is — and Microsoft shipped a dedicated Defender for Endpoint plug-in for WSL2 precisely because host EDR otherwise cannot see inside that isolated subsystem. Running a payload inside a VM to sidestep host EDR is a catalogued technique, not a new one: MITRE ATT&CK tracks it as T1564.006 (Run Virtual Instance). Ragnar Locker and Maze ran their payloads inside VirtualBox VMs (with Windows guests) back in 2020; more recently, the Curly COMrades campaign hid its malware inside a Hyper-V Linux VM specifically to sidestep host EDR.
So an attacker who moves from “code execution on the host as the user” to “code execution inside the Cowork VM” has not gained privileges — but has reduced their visibility to host-based tooling. It is worth being precise: this is not true invisibility. The host still sees the vmwp.exe worker process and any activity that crosses back to it — writes to the backing images at the filesystem boundary, and traffic egressing through the host’s network stack. What the host EDR loses is visibility into the process running inside the VM. And because the Cowork VM is a sanctioned, expected Hyper-V VM rather than a rogue one an attacker spins up, the classic “why is there suddenly a hypervisor running?” signal does not fire either — which is what makes the in-guest execution layer the residual gap worth naming.
4.3 Auto-recovery is not a security control
The service detects when the daemon fails to connect and re-downloads the initrd. This is a reliability mechanism, not a security control. It is fair to grant that it raises the bar a little: it evicts a careless tamper each session and generates repeated re-download events a defender could alert on. But it is not designed as, and does not function as, an integrity gate — it acts after the boot, not before it, and a well-crafted payload — one that starts the expected daemon normally while running a backdoor alongside it — would not trigger recovery at all. A security control would verify the image before booting; this recovers after a boot failure, and only when the attacker is careless enough to break visible functionality. Whether such a co-resident payload could establish a durable, EDR-blind foothold across sessions is plausible but was not demonstrated here — the proof of concept used a minimal image that the service recovers within seconds.
Part 5: The pipe Everyone can connect to
During this research, a separate defense-in-depth issue turned up in the pipe the service listens on for commands.
5.1 The named pipe DACL
CoworkVMService listens on \\.\pipe\cowork-vm-service. The pipe’s access rules — its DACL, the list that decides who is allowed to connect — read:
D:(A;;FA;;;WD)
A = Allow
FA = FILE_ALL_ACCESS (read, write, execute, delete — everything)
WD = Everyone (the World SID S-1-1-0 — all users)
Any user on the system — including standard users, service accounts, and even the Guest account if enabled — can connect to the SYSTEM service’s named pipe and send messages.
5.2 The signature check that stops the easy attack — and how it was defeated
The service does not leave the pipe completely unguarded. After accepting a connection and reading the first message, it identifies the connecting process and checks its Authenticode signature — the cryptographic seal Windows uses to prove who published a program — confirming specifically that the executable is signed by Anthropic with a matching certificate thumbprint. An unsigned process (a PowerShell script or a custom .exe) receives an error:
{"success":false,"id":99,
"error":"signature verification failed:
client executable is not signed:
C:\\Users\\Public\\unsigned-client.exe"}
This is a real defense against the easy attack — it stops direct command injection from an arbitrary unsigned process. But it rests on a premise — a process signed by Anthropic is a trustworthy source of commands — and that premise turned out to be defeatable. While I was preparing this post, the security firm Armadin published a separate, more severe finding in this same CoworkVMService that breaks exactly that premise (their writeup; press coverage).
They did not break WinVerifyTrust. They DLL-sideloaded a malicious USERENV.dll — that is, planted a DLL so a trusted program loads it instead of the real one — into the legitimately-signed claude.exe. The attacker’s code then runs inside the signed process and inherits its trust: the signature check sees claude.exe and waves it through, exactly as designed. With a trusted pipe client in hand, they abused two unvalidated spawn parameters — isResume to run as uid=0 (root), and allowedDomains="*" to strip the VM’s network egress filter — then used nsenter, a standard Linux tool for stepping into another process’s isolation namespaces, to leave the container and read /etc/shadow — the file of account password hashes that only the root superuser can read, which is what made the escalation to root visible. Anthropic declined their report on the same grounds it declined mine: the attacker must already be running code on the host.
Two things are worth separating. Armadin’s chain is the concrete demonstration of the signature bypass. My own contribution to this pipe is narrower and structural: the pipe’s DACL grants full access to Everyone, so that Authenticode check is the only barrier actually restricting who can send commands. Defense in depth says it should not be — the DACL should restrict access to authorized SIDs and the signature check should verify the caller. When a single control is the whole perimeter, a single bypass, like Armadin’s, is the whole compromise. This is why two researchers reaching the same service from different directions — their sideload-to-root, my boot-image trust anchor — is not a coincidence: the boundary around the Cowork VM is soft in more than one place.
5.3 The wire protocol
For anyone auditing the service after me, here is the pipe’s framing, recovered during this research: a big-endian uint32 length prefix followed by a JSON payload. The JSON uses a method field for dispatch, and the dispatch table is closed — an unrecognised value is rejected with unknown method: %s. The twelve handlers are configure, createVM, startVM, stopVM, isRunning, isGuestConnected, isProcessRunning, getNetworkDrives, writeStdin, passthrough, sendGuestResponse and setDebugLogging. Note what is not among them: spawn. That is a method on the in-guest daemon, reached over the HvSocket rather than over this pipe — which is why the two spawn parameters in §5.2 are the daemon’s and not the pipe’s, and why passthrough is the pipe method that matters for reaching them. This is documentation for Anthropic and for future researchers, not a separate vulnerability.
Part 6: What to take away
1. A trust anchor stored in an untrusted location is not an anchor. The VM boot images are the root of trust for the Cowork sandbox. Storing them in a user-writable directory without integrity verification means the trust root is modifiable by the context the sandbox is designed to contain. This pattern — a privileged consumer trusting files from an unprivileged location — is the same shape as DLL search-order hijacking, unsigned-package-in-a-watched-folder (RogueProvision), and plaintext-config-in-a-shared-directory. The fix is the same in every case: either move the files to a protected location, or verify their integrity before use.
2. A sandbox that host tools cannot see inside is a place attackers will want to live. The finding’s practical weight is not new privilege — it is reduced visibility. Moving code into a sanctioned, expected VM trades privilege for invisibility to host-based security tooling, which is exactly why running payloads inside virtual instances is a catalogued evasion technique. A boundary marketed to contain untrusted code becomes, if its trust anchor is soft, a comfortable place to hide it.
3. Reliability features are not security controls. Auto-recovery re-downloads the initrd when the daemon fails to connect. It protects against corruption and download failures; it does not protect against a payload that keeps the expected daemon alive while running attacker code beside it. One prevents the attack; the other cleans up after it — and only when the attacker is careless enough to break visible functionality.
Update (2026-09-09)
Elastic Security has added a prebuilt detection rule, “Claude Cowork VM Boot Image Tamper,” to their rules repository. It watches for creation, modification, overwrite or rename of the boot files this post describes. On Windows those are initrd, initrd.zst, vmlinuz, rootfs.vhdx and smol-bin.vhdx inside the claudevm.bundle directory; the macOS half of the rule watches the same bundle under Application Support, with rootfs.img standing in for rootfs.vhdx, no counterpart to smol-bin.vhdx, and vmlinuz.zst listed alongside vmlinuz. Both halves carry an allowlist for the legitimate writes Claude Desktop itself makes. It maps to the same MITRE ATT&CK technique this post names in Part 4, T1564.006 (Run Virtual Instance), and lists this post as its reference. The rule is available in the Elastic detection rules documentation.
Kazuma Matsumoto. “Claude Cowork Boots Its Sandbox From a Folder You Can Rewrite”. 637th Research Lab, 2026-07-21. https://y637f9qq2x.com/posts/cowork-boot-trust/
