home intel cve-2026-7272-matlab-mcp-server-path-traversal
CVE Analysis 2026-04-28 · 7 min read

CVE-2026-7272: Path Traversal in matlab-mcp-server MCP Interface

Unsanitized scriptPath argument in matlab-mcp-server's generate_matlab_code/execute_matlab_code allows remote path traversal via the MCP interface. Affects all commits through ab88f6b9bf5f36f7.

#path-traversal#arbitrary-file-access#code-execution#mcp-server#input-validation
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-7272 · Vulnerability
ATTACKERCloudVULNERABILITYCVE-2026-7272HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-7272 is a path traversal vulnerability in WilliamCloudQi/matlab-mcp-server, a Model Context Protocol (MCP) server that exposes MATLAB code generation and execution capabilities to LLM tool-use pipelines. The vulnerability exists in the generate_matlab_code and execute_matlab_code tool handlers within src/index.ts. An attacker-controlled scriptPath argument is passed directly into filesystem operations without canonicalization or prefix enforcement, permitting traversal outside the intended working directory.

The affected revision range is all commits up to and including ab88f6b9bf5f36f725e8628029f7f6dd0d9913ca. Because MCP servers are typically invoked by orchestration layers (Claude Desktop, LangChain agents, custom API gateways) that may accept tool arguments from untrusted sources, the attack surface is meaningful in multi-tenant or cloud-hosted deployments. CVSS 7.3 HIGH reflects network accessibility, low complexity, and partial integrity/confidentiality impact.

Root cause: The scriptPath parameter supplied by the MCP caller is concatenated into filesystem read/write operations without path canonicalization or enforcement of a trusted root prefix, allowing ../-sequence traversal to arbitrary filesystem locations accessible to the server process.

Affected Component

The server registers MCP tools through the standard @modelcontextprotocol/sdk server.tool() registration API. Two tools are affected:

  • generate_matlab_code — writes LLM-generated MATLAB source to a caller-specified path
  • execute_matlab_code — reads and shells out to MATLAB with a caller-specified script path

Both tools share the same scriptPath input schema field, and neither applies sanitization before passing the value to Node.js fs APIs or child process invocation. The server process inherits the filesystem permissions of the user running the MCP host, which in cloud deployments is frequently a service account with broad read access.

Root Cause Analysis

The following pseudocode is reconstructed from the TypeScript source structure at the affected commit. Variable names and control flow reflect the actual MCP tool registration pattern used in the repository:

// Reconstructed from src/index.ts @ ab88f6b9
// TypeScript represented as pseudocode for clarity

function register_tools(server: MCPServer) {

    // Tool 1: generate_matlab_code
    server.tool("generate_matlab_code", {
        scriptPath: z.string(),   // attacker-controlled: no format restriction
        code:       z.string(),
    }, async (args) => {

        // BUG: args.scriptPath accepted verbatim — no canonicalization,
        //      no prefix check against a trusted workdir.
        //      "../../../etc/cron.d/pwned" is a valid value here.
        const resolved = args.scriptPath;  // BUG: should be path.resolve() + prefix assert

        await fs.writeFile(resolved, args.code, "utf8");  // arbitrary write
        return { content: [{ type: "text", text: `Written: ${resolved}` }] };
    });

    // Tool 2: execute_matlab_code
    server.tool("execute_matlab_code", {
        scriptPath: z.string(),   // attacker-controlled
    }, async (args) => {

        // BUG: same unsanitized path fed directly to child_process.exec
        //      traversal to any readable .m file (or non-.m) outside workdir
        const resolved = args.scriptPath;  // BUG: missing path.resolve() + bounds check

        const result = await exec(`matlab -batch "run('${resolved}')"`, {
            cwd: process.cwd()
        });
        // Secondary BUG: resolved path embedded in shell string without escaping —
        // combinable with traversal for argument injection.
        return { content: [{ type: "text", text: result.stdout }] };
    });
}

Two independent weaknesses compound here. First, args.scriptPath is never passed through path.resolve() followed by a startsWith(WORK_DIR) guard. Second, in execute_matlab_code, the raw path is interpolated into a shell command string, opening a secondary shell-injection vector when the path contains shell metacharacters — though the primary CVE tracks the traversal primitive.

Exploitation Mechanics

EXPLOIT CHAIN — CVE-2026-7272 Path Traversal:

1. Attacker controls or compromises an MCP client (LLM agent, API gateway,
   Claude Desktop plugin) that communicates with the target matlab-mcp-server
   instance over stdio or SSE transport.

2. Invoke generate_matlab_code with traversal payload:
     scriptPath = "../../../../../../tmp/.bashrc_inject"
     code       = "malicious content / MATLAB stub"
   Server resolves path naively → writes attacker content outside workdir.

3. Alternatively, invoke execute_matlab_code with read-back exfil:
     scriptPath = "../../../../../../etc/passwd"
   matlab -batch run() will fail, but stderr/stdout leaks file content
   or confirms path existence via error message differentiation.

4. For shell injection (secondary vector), combine traversal with metachar:
     scriptPath = "'; cat /etc/shadow > /tmp/out.txt; echo '"
   Injected into: matlab -batch "run('')"
   Executes as shell command via child_process.exec shell=true semantics.

5. In cloud/container deployments where MATLAB is absent, exec() error path
   still reflects resolved path in error.message — blind traversal oracle
   confirming directory structure.

Memory Layout

This is a logic/path-traversal vulnerability rather than a memory corruption primitive, so heap state diagrams do not apply. The relevant runtime state is the Node.js process working directory and the path resolution chain:

FILESYSTEM STATE — INTENDED vs ACTUAL resolution:

INTENDED constraint (never implemented):
  WORK_DIR  = /opt/matlab-mcp/workspace/
  scriptPath input = "scripts/myModel.m"
  resolved  = /opt/matlab-mcp/workspace/scripts/myModel.m  ✓ in-bounds

ACTUAL behavior (vulnerable):
  WORK_DIR  = /opt/matlab-mcp/workspace/
  scriptPath input = "../../etc/passwd"
  path.join(process.cwd(), "../../etc/passwd")
            = /opt/matlab-mcp/workspace/../../etc/passwd
            = /opt/etc/passwd   (or / depending on cwd depth)

  scriptPath input = "../../../../../root/.ssh/authorized_keys"
  resolved  = /root/.ssh/authorized_keys   ← arbitrary write primitive
              if server runs as root (common in containerized cloud deployments)

WRITE PRIMITIVE IMPACT:
  [ /opt/matlab-mcp/workspace/ ]  ← intended write boundary
  [ /tmp/                      ]  ← reachable: drop payloads
  [ /root/.ssh/                ]  ← reachable: SSH key injection
  [ /etc/cron.d/               ]  ← reachable: persistence
  [ /proc/self/fd/             ]  ← reachable: fd manipulation

Patch Analysis

The fix requires two independent changes: path canonicalization with prefix enforcement for the traversal primitive, and shell-argument escaping for the injection vector.

// BEFORE (vulnerable) — src/index.ts:
async function handle_generate_matlab_code(args) {
    const scriptPath = args.scriptPath;             // raw, no validation
    await fs.writeFile(scriptPath, args.code);
}

async function handle_execute_matlab_code(args) {
    const scriptPath = args.scriptPath;             // raw, no validation
    await exec(`matlab -batch "run('${scriptPath}')"`);  // also injectable
}


// AFTER (patched — recommended fix):
const WORK_DIR = path.resolve(process.env.MATLAB_WORK_DIR ?? "./workspace");

function safe_resolve(userPath: string): string {
    // Canonicalize: collapses all ../ sequences
    const resolved = path.resolve(WORK_DIR, userPath);

    // Enforce prefix: reject anything outside WORK_DIR
    if (!resolved.startsWith(WORK_DIR + path.sep)) {
        throw new McpError(
            ErrorCode.InvalidParams,
            `Path traversal detected: '${userPath}' escapes working directory`
        );
    }
    return resolved;
}

async function handle_generate_matlab_code(args) {
    const scriptPath = safe_resolve(args.scriptPath);  // bounded
    await fs.writeFile(scriptPath, args.code);
}

async function handle_execute_matlab_code(args) {
    const scriptPath = safe_resolve(args.scriptPath);  // bounded

    // Use execFile with argument array — no shell interpolation
    await execFile("matlab", ["-batch", `run('${scriptPath}')`], {
        cwd: WORK_DIR,
        shell: false   // explicit: no shell metachar expansion
    });
}

The critical subtlety in the patch is that path.resolve() alone is insufficient — the subsequent startsWith(WORK_DIR + path.sep) guard is mandatory. Without the separator suffix, a WORK_DIR of /workspace would permit access to /workspace-other/secret via prefix match. The path.sep append closes that edge case.

Detection and Indicators

In environments with MCP server access logging, look for scriptPath values containing the following patterns in tool invocation records:

DETECTION SIGNATURES:

MCP tool call log patterns (JSON-RPC method: tools/call):
  params.arguments.scriptPath  CONTAINS  "../"
  params.arguments.scriptPath  CONTAINS  "..\"     (Windows)
  params.arguments.scriptPath  MATCHES   ^/         (absolute path)
  params.arguments.scriptPath  CONTAINS  "%2e%2e"  (URL-encoded)

Process audit (auditd / eBPF):
  openat(AT_FDCWD, "/etc/passwd", O_RDONLY)       parent=node
  openat(AT_FDCWD, "/root/.ssh/authorized_keys")  parent=node
  write to path outside /opt/matlab-mcp/workspace/ from node process

Network (if using SSE transport):
  POST /mcp  body contains scriptPath with ../ sequences
  Anomalous file paths in MCP server stdout/stderr reflected in response

SIEM rule (Sigma-style pseudocode):
  process.name == "node"
  AND cmdline CONTAINS "index.js"
  AND file.path NOT STARTSWITH "/opt/matlab-mcp/workspace/"
  AND event.type IN (file_create, file_write, file_read)

Remediation

Immediate mitigations for deployments that cannot patch immediately:

  • Run the MCP server process as a dedicated low-privilege user with filesystem access scoped via chroot or container filesystem restrictions — reduces traversal blast radius to the chroot boundary.
  • Apply a seccomp profile or Landlock LSM rules restricting openat/write syscalls to the workspace directory subtree.
  • If using SSE/HTTP transport, place a reverse proxy in front that rejects requests where scriptPath matches \.\./ or begins with /.
  • Disable execute_matlab_code entirely if MATLAB execution is not required — the write primitive in generate_matlab_code is the higher-severity vector in most deployments.

Permanent fix: apply the safe_resolve() guard shown in the patch analysis to all tool handlers that accept filesystem paths. Pin to a commit after the maintainer confirms the fix, or vendor the fix locally given the project's non-response to the responsible disclosure issue.

CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →