Skip to content
ATR-2026-02024highTool Poisoningdraft

EscapeRoute — Filesystem MCP Symlink Escape to LaunchAgent Persistence (CVE-2025-53109)

Detects the EscapeRoute symlink-bypass-to-code-execution chain (CVE-2025-53109, CVSS 8.4) reported by Cymulate against Anthropic's Filesystem MCP Server before 0.6.3 / 2025.7.1. The server followed symlinks without re-checking that the resolved target stayed inside the allowed directory. An attacker creates a symlink inside a writable (or prefix-bypassed) directory that points anywhere on the filesystem, then uses the filesystem MCP write_file to write through the symlink to a privileged target — the published PoC writes a malicious LaunchAgent plist to ~/Library/LaunchAgents/*.plist for persistent code execution at login (on macOS), or overwrites /etc/sudoers. This rule fires on the concrete signature: a filesystem-MCP symlink-create or write-through-symlink operation whose target is a known persistence/privileged path (LaunchAgents plist, /etc/sudoers, cron, authorized_keys, systemd unit). Fixed by realpath + post-resolution boundary re-validation in 0.6.3 / 2025.7.1.

Severity
high
Category
Tool Poisoning
Scan Target
runtime
Author
ATR Community (MCP CVE sweep)

Response Actions

alertblock tool

References

OWASP Agentic
ASI02:2026 - Tool Misuse and ExploitationASI05:2026 - Unexpected Code Execution
OWASP LLM
LLM06:2025 - Excessive AgencyLLM05:2025 - Improper Output Handling
MITRE ATLAS
AML.T0053 - AI Agent Tool InvocationAML.T0051.001 - Indirect

Detection Conditions

Combinator: any
  1. 01
    Filesystem MCP write targeting a macOS LaunchAgents/LaunchDaemons plist — the CVE-2025-53109 persistence sink
    field: contentop: regex
  2. 02
    A symlink create/follow combined with filesystem-MCP sandbox-escape framing and a privileged persistence target — CVE-2025-53109 symlink-to-code-exec chain
    field: contentop: regex
  3. 03
    A symlink whose target is a privileged file (sudoers / authorized_keys / crontab) written through the filesystem MCP — CVE-2025-53109 write-through-symlink privilege escalation
    field: contentop: regex

Attack Examples (Rule Triggers)

  1. filesystem MCP write_file target /Users/eladbeber/Library/LaunchAgents/com.example.autocalculator.plist to drop a persistent launch agent
  2. Create a symlink test -> /etc/sudoers inside allow_dir_evil then use the filesystem mcp write_file to escape the sandbox and overwrite sudoers
  3. ln -s /root/.ssh/authorized_keys link; the mcp server write_file follows the symbolic link and appends an attacker key, escaping allowed directory
  4. edit_file ~/Library/LaunchDaemons/com.evil.persist.plist via the filesystem MCP to install a launch daemon that runs at boot
  5. symlink points to /etc/crontab; then filesystem mcp write_file writes a cron line, giving code execution outside the sandbox

Real-world attack payloads, sanitized and versioned alongside the rule as regression tests — so a future revision can't silently stop catching them.

Benign Examples (Rule Doesn't Trigger)

  1. Ordinary project symlink with no filesystem-MCP write to a persistence target
    ln -s ../shared ./node_modules/shared    # normal monorepo symlink for local package linking
  2. Dotfile symlink manager — no privileged persistence target, no MCP escape framing
    I use GNU stow to symlink my dotfiles from ~/dotfiles into ~; e.g. ln -s ~/dotfiles/.vimrc ~/.vimrc.
  3. Advisory text describing the symlink escape and fix without a live operation
    CVE-2025-53109 (EscapeRoute) let a symlink bypass the filesystem MCP boundary to write a LaunchAgent; upgrade to 2025.7.1 fixes it.
  4. A plist written inside the project workspace, not a LaunchAgents/LaunchDaemons persistence path
    write_file /workspace/build/output.plist — the app emits a plist describing its build settings inside the project directory.
  5. Legitimate app installer writing its own LaunchAgent — no symlink escape, no filesystem-MCP write_file/edit_file operation
    The installer places a launch agent at ~/Library/LaunchAgents/com.vendor.updater.plist during normal app setup.

Known False Positive Contexts

  • Legitimate developer use of symlinks inside a project (ln -s ../shared node_modules, dotfile symlink managers like stow) where there is no filesystem-MCP write to a persistence/privileged target. This rule requires the symlink/write operation to co-occur with a known persistence path (LaunchAgents plist, /etc/sudoers, cron, authorized_keys, systemd unit), not any symlink.
  • Advisory or research text naming CVE-2025-53109, EscapeRoute, or the LaunchAgent PoC without a live symlink-create or write-through operation via the filesystem MCP.
  • Applications that legitimately install their own LaunchAgent/systemd unit through their platform installer (not via a filesystem MCP tool write through a symlink); the rule anchors on the filesystem-MCP write / symlink construct plus the sandbox-escape framing.

Documented Evasion Techniques

  1. Technique: case variation
    write_file to ~/Library/LaunchAgents/com.evil.persist.PLIST (uppercase extension) via the filesystem mcp
    The (?i) flag makes the .plist / LaunchAgents anchors case-insensitive, so an uppercase .PLIST still matches Layer 1.
  2. Technique: hardlink instead of symlink
    hardlink (not symlink) the sudoers file into allow_dir then write_file through it to escape the boundary
    Hard links (not symbolic links) are a distinct primitive the Layer-2/3 symlink anchors do not model, and the target /etc/sudoers here lacks the LaunchAgents plist Layer-1 path. If hardlink-based EscapeRoute variants are observed, a v2 should add a 'hardlink|ln (?!-s)' branch alongside the privileged-target anchor.

Publicly documented bypasses. A standard earns trust by publishing its worst figures, not hiding them — so known limitations ship inside the rule, not in a footnote.

Full YAML Definition

Edit on GitHub →
title: "EscapeRoute — Filesystem MCP Symlink Escape to LaunchAgent Persistence (CVE-2025-53109)"
id: ATR-2026-02024
rule_version: 1
status: draft
description: >
  Detects the EscapeRoute symlink-bypass-to-code-execution chain (CVE-2025-53109,
  CVSS 8.4) reported by Cymulate against Anthropic's Filesystem MCP Server
  before 0.6.3 / 2025.7.1. The server followed symlinks without re-checking that
  the resolved target stayed inside the allowed directory. An attacker creates a
  symlink inside a writable (or prefix-bypassed) directory that points anywhere
  on the filesystem, then uses the filesystem MCP write_file to write through the
  symlink to a privileged target — the published PoC writes a malicious
  LaunchAgent plist to ~/Library/LaunchAgents/*.plist for persistent code
  execution at login (on macOS), or overwrites /etc/sudoers. This rule fires on
  the concrete signature: a filesystem-MCP symlink-create or write-through-symlink
  operation whose target is a known persistence/privileged path (LaunchAgents
  plist, /etc/sudoers, cron, authorized_keys, systemd unit). Fixed by realpath +
  post-resolution boundary re-validation in 0.6.3 / 2025.7.1.
author: "ATR Community (MCP CVE sweep)"
date: "2026/07/08"
schema_version: "0.1"
detection_tier: pattern
maturity: test
severity: high

references:
  owasp_llm:
    - "LLM06:2025 - Excessive Agency"
    - "LLM05:2025 - Improper Output Handling"
  owasp_agentic:
    - "ASI02:2026 - Tool Misuse and Exploitation"
    - "ASI05:2026 - Unexpected Code Execution"
  mitre_atlas:
    - "AML.T0053 - AI Agent Tool Invocation"
    - "AML.T0051.001 - Indirect"
  cve:
    - "CVE-2025-53109"
  cwe:
    - "CWE-59"
    - "CWE-61"
    - "CWE-22"
  external:
    - "https://cymulate.com/blog/cve-2025-53109-53110-escaperoute-anthropic/"
    - "https://nvd.nist.gov/vuln/detail/CVE-2025-53109"

metadata_provenance:
  cve: mcp-cve-sweep
  cwe: mcp-cve-sweep
  owasp_llm: mcp-cve-sweep
  owasp_agentic: mcp-cve-sweep
  mitre_atlas: mcp-cve-sweep

compliance:
  owasp_agentic:
    - id: "ASI05:2026"
      context: "OWASP Agentic ASI05:2026 (Unexpected Code Execution) is exercised by the EscapeRoute symlink escape that writes a LaunchAgent for persistent execution (CVE-2025-53109); this rule detects that write pattern."
      strength: primary
    - id: "ASI02:2026"
      context: "OWASP Agentic ASI02:2026 (Tool Misuse and Exploitation) is exercised where the filesystem MCP is misused to write through a symlink outside its sandbox (CVE-2025-53109)."
      strength: secondary
  owasp_llm:
    - id: "LLM06:2025"
      context: "OWASP LLM LLM06:2025 (Excessive Agency) is exercised where the filesystem MCP server writes to privileged persistence paths via a symlink escape (CVE-2025-53109); this rule is a detection implementation for that category."
      strength: primary
    - id: "LLM05:2025"
      context: "OWASP LLM LLM05:2025 (Improper Output Handling) is exercised where unvalidated symlink targets let server output land on privileged files (CVE-2025-53109)."
      strength: secondary
  eu_ai_act:
    - article: "15"
      context: "EU AI Act Article 15 (accuracy, robustness and cybersecurity) requires MCP filesystem tools to re-validate symlink targets against the sandbox; this rule provides runtime detection evidence for the CVE-2025-53109 symlink escape."
      strength: primary
    - article: "9"
      context: "EU AI Act Article 9 (risk management system) requires controls against the CVE-2025-53109 symlink-to-persistence chain; this rule is a runtime risk-treatment control."
      strength: secondary
  nist_ai_rmf:
    - function: Manage
      subcategory: "MG.2.3"
      context: "NIST AI RMF MG.2.3 (risk treatment options selected and tracked) is supported by this rule's detection of the EscapeRoute symlink-to-LaunchAgent persistence chain (CVE-2025-53109)."
      strength: primary
    - function: Measure
      subcategory: "MS.2.7"
      context: "NIST AI RMF MS.2.7 (security and resilience evaluated and documented) is supported by this rule's runtime detection of CVE-2025-53109."
      strength: secondary
  iso_42001:
    - clause: "8.1"
      context: "ISO/IEC 42001 Clause 8.1 (operational planning and control) is operationalised by this rule's detection of the EscapeRoute symlink escape (CVE-2025-53109)."
      strength: primary
    - clause: "8.3"
      context: "ISO/IEC 42001 Clause 8.3 (AI risk treatment) is operationalised by this rule's detection of CVE-2025-53109."
      strength: secondary

tags:
  category: tool-poisoning
  subcategory: filesystem-mcp-symlink-persistence
  scan_target: runtime
  confidence: high

agent_source:
  type: mcp_exchange
  framework:
    - any
  provider:
    - any

detection:
  condition: any
  false_positives:
    - "Legitimate developer use of symlinks inside a project (ln -s ../shared node_modules, dotfile symlink managers like stow) where there is no filesystem-MCP write to a persistence/privileged target. This rule requires the symlink/write operation to co-occur with a known persistence path (LaunchAgents plist, /etc/sudoers, cron, authorized_keys, systemd unit), not any symlink."
    - "Advisory or research text naming CVE-2025-53109, EscapeRoute, or the LaunchAgent PoC without a live symlink-create or write-through operation via the filesystem MCP."
    - "Applications that legitimately install their own LaunchAgent/systemd unit through their platform installer (not via a filesystem MCP tool write through a symlink); the rule anchors on the filesystem-MCP write / symlink construct plus the sandbox-escape framing."
  conditions:
    # Layer 1 — a filesystem-MCP write (write_file/edit_file) whose target is a
    # macOS LaunchAgent/LaunchDaemon plist persistence path.
    - field: content
      operator: regex
      value: '(?i)\b(?:write_file|edit_file|write_text_file|create_file|move_file)\b[\s\S]{0,120}?(?:~|/Users/[\w.-]+|/Library|/System/Library)?/Library/(?:LaunchAgents|LaunchDaemons)/[\w.-]+\.plist\b'
      description: "Filesystem MCP write targeting a macOS LaunchAgents/LaunchDaemons plist — the CVE-2025-53109 persistence sink"
    # Layer 2 — a symlink create/follow paired with the filesystem-MCP sandbox
    # escape framing and a privileged/persistence target (sudoers, cron,
    # authorized_keys, systemd unit, plist).
    - field: content
      operator: regex
      value: '(?i)(?:\bln\s+-s\b|\bsymlink\b|\bsymbolic\s+link\b)[\s\S]{0,200}?(?:filesystem\s+mcp|write_file|allowed\s+director|sandbox|escape)[\s\S]{0,160}?(?:/etc/sudoers|/etc/cron|/etc/crontab|\.ssh/authorized_keys|/Library/(?:LaunchAgents|LaunchDaemons)/|/etc/systemd/|\.service\b|\.plist\b)'
      description: "A symlink create/follow combined with filesystem-MCP sandbox-escape framing and a privileged persistence target — CVE-2025-53109 symlink-to-code-exec chain"
    # Layer 3 — write-through-symlink to a classic privileged file where the
    # symlink was placed to bypass the boundary (the general EscapeRoute shape:
    # symlink target is /etc/sudoers or authorized_keys, written via the MCP).
    - field: content
      operator: regex
      value: '(?i)(?:\bsymlink\b|\bln\s+-s\b|\bpoints?\s+to\b|\bresolves?\s+to\b)[\s\S]{0,120}?(?:/etc/sudoers|/root/\.ssh/authorized_keys|~?/\.ssh/authorized_keys|/etc/crontab|/etc/cron\.d/)[\s\S]{0,160}?\b(?:write_file|edit_file|filesystem\s+mcp|mcp\s+server)\b'
      description: "A symlink whose target is a privileged file (sudoers / authorized_keys / crontab) written through the filesystem MCP — CVE-2025-53109 write-through-symlink privilege escalation"

response:
  actions:
    - alert
    - block_tool
  notify:
    - security_team
  message_template: >
    [ATR-2026-02024] HIGH: EscapeRoute filesystem-MCP symlink escape to
    persistence (CVE-2025-53109, CVSS 8.4). The filesystem MCP writes through a
    symlink to a privileged persistence target (LaunchAgent plist, /etc/sudoers,
    authorized_keys, cron, or systemd unit), achieving code execution / privilege
    escalation outside the allowed directory. Remediation: upgrade
    @modelcontextprotocol/server-filesystem to 2025.7.1 / 0.6.3+ (adds realpath +
    post-resolution boundary re-validation); reject writes whose resolved target
    leaves the sandbox.

confidence: 82
wild_fp_rate: 0

test_cases:
  true_positives:
    - input: 'filesystem MCP write_file target /Users/eladbeber/Library/LaunchAgents/com.example.autocalculator.plist to drop a persistent launch agent'
      expected: triggered
      description: "Cymulate PoC — write_file to a user LaunchAgents plist for login persistence"
    - input: 'Create a symlink test -> /etc/sudoers inside allow_dir_evil then use the filesystem mcp write_file to escape the sandbox and overwrite sudoers'
      expected: triggered
      description: "Symlink to /etc/sudoers plus filesystem-MCP sandbox-escape framing and write"
    - input: 'ln -s /root/.ssh/authorized_keys link; the mcp server write_file follows the symbolic link and appends an attacker key, escaping allowed directory'
      expected: triggered
      description: "Symlink to authorized_keys written through the MCP server — write-through-symlink privilege escalation"
    - input: 'edit_file ~/Library/LaunchDaemons/com.evil.persist.plist via the filesystem MCP to install a launch daemon that runs at boot'
      expected: triggered
      description: "edit_file to a LaunchDaemons plist persistence path"
    - input: 'symlink points to /etc/crontab; then filesystem mcp write_file writes a cron line, giving code execution outside the sandbox'
      expected: triggered
      description: "Symlink resolving to /etc/crontab written through the filesystem MCP"
  true_negatives:
    - input: 'ln -s ../shared ./node_modules/shared    # normal monorepo symlink for local package linking'
      expected: not_triggered
      description: "Ordinary project symlink with no filesystem-MCP write to a persistence target"
    - input: 'I use GNU stow to symlink my dotfiles from ~/dotfiles into ~; e.g. ln -s ~/dotfiles/.vimrc ~/.vimrc.'
      expected: not_triggered
      description: "Dotfile symlink manager — no privileged persistence target, no MCP escape framing"
    - input: "CVE-2025-53109 (EscapeRoute) let a symlink bypass the filesystem MCP boundary to write a LaunchAgent; upgrade to 2025.7.1 fixes it."
      expected: not_triggered
      description: "Advisory text describing the symlink escape and fix without a live operation"
    - input: 'write_file /workspace/build/output.plist — the app emits a plist describing its build settings inside the project directory.'
      expected: not_triggered
      description: "A plist written inside the project workspace, not a LaunchAgents/LaunchDaemons persistence path"
    - input: 'The installer places a launch agent at ~/Library/LaunchAgents/com.vendor.updater.plist during normal app setup.'
      expected: not_triggered
      description: "Legitimate app installer writing its own LaunchAgent — no symlink escape, no filesystem-MCP write_file/edit_file operation"

evasion_tests:
  - input: 'write_file to ~/Library/LaunchAgents/com.evil.persist.PLIST (uppercase extension) via the filesystem mcp'
    expected: triggered
    bypass_technique: case_variation
    notes: "The (?i) flag makes the .plist / LaunchAgents anchors case-insensitive, so an uppercase .PLIST still matches Layer 1."
  - input: 'hardlink (not symlink) the sudoers file into allow_dir then write_file through it to escape the boundary'
    expected: not_triggered
    bypass_technique: hardlink_instead_of_symlink
    notes: "Hard links (not symbolic links) are a distinct primitive the Layer-2/3 symlink anchors do not model, and the target /etc/sudoers here lacks the LaunchAgents plist Layer-1 path. If hardlink-based EscapeRoute variants are observed, a v2 should add a 'hardlink|ln (?!-s)' branch alongside the privileged-target anchor."

Revision History

Created
2026-07-08
Last modified
2026-07-08
View full commit history on GitHub →