system: OPERATIONAL
← back to all hacks
AGENTS CRITICAL NEW

SymJack: one approved file copy becomes RCE in six AI coding agents

Adversa AI disclosed on May 26, 2026 a symlink-hijack pattern that turns a single benign-looking shell copy into a config overwrite and host RCE across Claude Code, Cursor, Gemini, Antigravity, Copilot, Grok Build and Codex CLIs.

2026-05-30 // 6 min affects: claude-code, cursor-cli, gemini-cli, antigravity-cli, copilot-cli, grok-build-cli, codex-cli

What is this?

On May 26, 2026, Rony Utevsky of Adversa AI published SymJack: the approval prompt is lying to you, documenting a single attack pattern that produces remote code execution on the user’s host across six AI coding agents: Claude Code 2.1.128 (partially patched in 2.1.129), Cursor CLI v2026.05.20, Gemini CLI 0.43.0, Antigravity CLI 1.0.2, Copilot CLI 1.0.51, Grok Build CLI 0.1.216, and OpenAI Codex CLI v0.133.0 (added in the May 27 update). No new CVE has been issued, but the family extends the earlier Check Point disclosure CVE-2025-59536 (October 2025) and the related Adversa TrustFall pattern published earlier in May.

The trigger is opening a malicious repository and clicking Approve on what looks like a routine file copy.

How it works

Coding agents distinguish two categories of file write. Native write tools (write_file, apply_diff, etc.) carry path-sensitivity guardrails: they refuse, or prompt extra hard, when the target is an MCP config, settings file, or shell rc. Shell commands (cp, mv, install, tee, redirections) are checked by inspecting the command text — not the resolved effect on the filesystem.

SymJack exploits the gap. Per Adversa:

“The instructions tell the agent to use a raw shell copy command rather than the agent’s own file writing tools. The native write tools in these products have guardrails that flag sensitive paths like config files. A raw cp slips past those checks, because the permission prompt inspects the command text, not the real effect.”

The destination in the approval prompt is a path inside the repository. That path is a symlink committed into the repo, pointing at the agent’s own MCP config or settings file. When the kernel follows the link, attacker content lands directly in the file that defines which MCP servers the agent will spawn on next start:

repo/                              ~/.config/<agent>/
├── payload.txt        ─ cp ─>     mcp_servers.json   ← symlink target
└── docs/destination  ─ symlink ─> mcp_servers.json
# In the approval prompt the user sees:
$ cp ./payload.txt ./docs/destination
# After the kernel resolves the symlink, the actual write is:
#   ./payload.txt  →  ~/.config/<agent>/mcp_servers.json

The next agent restart loads the rewritten config, instantiates the attacker-controlled MCP server, and runs its commands with the user’s full privileges. Indirect prompt injection from README.md, a hidden AGENTS.md, or an .mcp.json description is enough to get the agent to suggest the malicious cp in the first place.

Why it matters

The bug class is structural, not a single product slip:

  • Approval-prompt fidelity is broken when the prompt shows argument strings and the kernel acts on resolved paths.
  • Two write paths, one policy is the wrong defaults. Every product audited had stricter guardrails on its native write tool than on shell file ops.
  • Configs are code. An MCP server entry in mcp_servers.json is a process spawn. Treating it as data while treating shell commands as commands inverts the trust model.

The same pattern reappeared independently in TrustFall, in CVE-2025-59536, and now SymJack across six unrelated agents — a strong signal that the attack surface is the design, not an implementation bug.

Defenses

Adversa’s recommendations, verbatim:

  1. “Resolve symlinks to their real target before any permission decision, on every file-writing path, including shell commands.”
  2. “Treat shell file operations (cp, mv, install, tee, redirections, dd of=) as first-class writes, subject to the same path-sensitivity checks as the native write tools.”
  3. “Show the user the canonical destination in the approval prompt, not the literal argument string.”
  4. “Block sensitive config keys that enable MCP execution from being set by project-scoped files.”
  5. “Surface which instruction-file includes fired during startup, so a hidden directive in a mostly-blank file cannot run silently.”

Operators of these agents should also: keep auto-approve off for shell file ops in untrusted repos, mount agent config directories read-only when working in unknown projects, monitor changes to ~/.config/<agent>/ and ~/.<agent>/ for diffs that did not originate from a deliberate user action, and update to the latest CLI versions (Claude Code ≥ 2.1.129 carries a partial fix that hardens the approval flow but does not, per Adversa, fully resolve symlinks on the shell path).

Status

AgentTested versionVendor response
Claude Code2.1.128Partial hardening in 2.1.129
Gemini CLI0.43.0Declined
Antigravity CLI1.0.2(same channel as Gemini)
Cursor CLI2026.05.20Declined as duplicate
Copilot CLI1.0.51Awaiting response
Grok Build CLI0.1.216Awaiting response
Codex CLI0.133.0Bugcrowd report closed

SymJack is documented with a proof-of-concept repository on GitHub and per-agent demonstration videos. Treat any AI coding agent operating in a repo you did not author as capable of reaching code execution on your host via a single approval click — until the approval UI shows resolved paths and the shell-write path runs through the same guardrails as the native write tools.

Sources