{"slug": "beltdown-escaping-the-claude-code-sandbox", "title": "Beltdown: Escaping the Claude Code sandbox", "summary": "Anthropic fixed a Claude Code sandbox escape in version 2.1.247 after researchers at Accomplish showed that an untrusted repository could run commands on a Mac outside the macOS Seatbelt sandbox with no permission prompt. The attack chained an unhardened `git ls-files` call, a missing Seatbelt rule blocking renames of nested `.git` folders, the harness's use of the Bash tool's last working directory, and skill auto-loading to trigger a file index refresh that executed a poisoned `core.fsmonitor` value from `.git/config`. Anthropic triaged the report on July 13, 2026, shipped partial hardening in 2.1.223 on August 6, 2026, and completed the fix in 2.1.247 on August 26, 2026, so every git command the harness runs now blanks `core.fsmonitor`.", "body_md": "An untrusted repository opened in Claude Code can **escape the macOS sandbox** and run commands on your computer as your privileged user. You never get the permission prompt.\n\nWe turned the sandbox on and set permission mode to the strictest “don’t ask”. Then we opened a repository in Claude Code and sent one short message. A command from that repo ran on our Mac anyway, outside the sandbox, with no permission prompt. It can also work through an indirect prompt injection.\n\nCounterintuitively, the sandbox is why there was no permission prompt. When you turn it on, Claude sandboxes the agent’s commands using Seatbelt, so it stops asking before it runs them.\n\nFollowing our report, Anthropic fixed this in Claude Code 2.1.247.\n\n## How the escape works\n\nThe harness runs its own git commands outside the sandbox. It runs them in the background to index the repo.\n\nGit has a config setting, `core.fsmonitor`, that it reads from `.git/config` and runs as a shell command whenever it looks at the working tree.\n\nSo a rogue or compromised agent that wants out has to set `core.fsmonitor` in a `.git/config` file, then get the harness to run a git command that reads it. That git command is already outside the sandbox.\n\nBut four defenses get in the way:\n\n| The defense | The bypass | \n|---|---|\n| The git commands are hardened. The ones Claude Code runs outside the sandbox pass flags that blank out `core.fsmonitor` and other such dangerous options, so the harness doesn’t execute them. | We found a git call that was left unhardened, the one that refreshes the file index: `git ls-files` . | \n| The `.git` folder is protected. Claude’s file tools refuse to write inside a`.git` folder, and Seatbelt blocks bash from writing there. A fresh clone won’t bring a`.git/config` along either, git never copies the remote’s config on purpose. Renaming another folder into`.git` is blocked too. | But only for the `.git` in the project’s root folder. The Seatbelt profile rule that blocks renaming a nested`.git` folder is missing. So a setup script can build a git folder under a different name, write`core.fsmonitor` into its config, and rename it to`.git` inside a subfolder. | \n| The harness runs git in the project’s root folder, where `.git` is clean. | It actually runs in whatever directory the Bash tool used last. So the script changes the working directory to the subfolder with the poisoned `.git` . | \n| The harness doesn’t automatically re-run that git command. | An attacker can force it by abusing skill auto-loading. The script’s last line tells Claude to read a build report in that same subfolder. When Claude reads a file, it checks that folder for skills and loads what it finds. Loading a skill auto-triggers a file index refresh. That refresh is the unhardened git command, running in the subfolder with the poisoned `.git` . So the moment Claude reads the report, the rest of the chain runs, with no user interaction. | \n\nChained together, the setup script plants the poisoned `.git` in an unprotected nested path, tells Claude to read one file in that folder, and the file index refresh runs the payload.\n\n## Disclosure timeline\n\n| Date | What happened | \n|---|---|\n| Jul 13, 2026 | Reported to Anthropic. Triaged the same day. | \n| Aug 6, 2026 | First hardening shipped in 2.1.223. It missed some of the git calls, so the escape moved to another one. We sent them the calls that were left. | \n| Aug 26, 2026 | Full fix in 2.1.247. | \n\nAnthropic triaged our report quickly, and now every git command the harness runs blanks `core.fsmonitor`, so a repo’s config can’t run anything.\n\n## How we built Accomplish\n\nWe put the whole agent in a VM. Bash, git, every process it starts, all of it is in there. Real credentials never enter the guest, the agent only holds placeholders. Its network traffic goes out through a proxy on the host that the agent can’t reconfigure.\n\nA poisoned `core.fsmonitor` still runs, it just runs inside the VM, not on your Mac.", "url": "https://wpnews.pro/news/beltdown-escaping-the-claude-code-sandbox", "canonical_source": "https://accomplish.ai/blog/beltdown-escaping-the-claude-code-sandbox/", "published_at": "2026-08-21 00:00:00+00:00", "updated_at": "2026-09-10 21:43:19.028959+00:00", "lang": "en", "topics": ["ai-safety", "ai-agents", "ai-tools", "developer-tools"], "entities": ["Anthropic", "Claude Code", "Accomplish", "macOS", "Seatbelt", "git", "Claude Code 2.1.247", "Claude Code 2.1.223"], "alternates": {"html": "https://wpnews.pro/news/beltdown-escaping-the-claude-code-sandbox", "markdown": "https://wpnews.pro/news/beltdown-escaping-the-claude-code-sandbox.md", "text": "https://wpnews.pro/news/beltdown-escaping-the-claude-code-sandbox.txt", "jsonld": "https://wpnews.pro/news/beltdown-escaping-the-claude-code-sandbox.jsonld"}}