FrontMCP plugin-codecall 沙箱逃逸CVE-2026-67531
FrontMCP 是一个TypeScript优先、面向Model Context Protocol (MCP,Anthropic 推出的 AI 上下文互通协议) 的生产级开源开发框架。
一、基本情况
FrontMCP 采用 Apache 2.0 协议开源,用来快速搭建 MCP 服务端,补齐 JS/TS 生态缺少成熟 MCP 开发框架的短板,生态起步较晚。

FrontMCP本地、云端部署代码完全一致,契合前端/Node.js 技术栈开发者习惯,开箱即用,省去协议封装、鉴权、传输重复开发工作。
栋科技漏洞库关注到 FrontMCP plugin-codecall 沙箱逃逸漏洞,该漏洞现在已经被追踪为CVE-2026-67531,漏洞的CVSS 3.X评分9.3。
二、漏洞分析
CVE-2026-67531是 FrontMCP 相关版本中的安全漏洞, FrontMCP 是一款面向 Model Context Protocol(MCP)的 TypeScript 框架。
由于@frontmcp/plugin-codecall 提供的 codecall:execute 工具在 enclave 沙箱中执行脚本,并通过 getTool 将工具元数据透传给脚本。
该缺陷使沙箱返回宿主 Zod 模式实例,并被 ECMAScript Proxy 不变量放大为安全膜穿透,
单次 MCP tools/call 即可令攻击者以服务端进程权限执行任意代码,
泄露 OAuth 客户端密钥、JWT_SECRET、会话密钥、数据库凭证与云实例元数据。
具体来说,codecall:execute 在 plugins/plugin-codecall/src/tools/execute.tool.ts 中构建 VM 环境,
getTool(name) 回调返回的 inputSchema/outputSchema 就是底层 Tool 的 rawInputSchema/rawOutputSchema,
即 Zod v4 的真实宿主实例。Zod v4 在该实例上挂载 _zod 作为不可配置、不可写的自身属性,
ECMAScript Proxy 不变量强制安全膜在拦截时把原始宿主对象交还给脚本,
脚本进而读取 _zod.constr.constructor(宿主 Function 构造器)并执行任意代码。
框架默认 DEFAULT_AUTH_OPTIONS 为 public 模式,未配置认证的服务器对未授权调用者同样开放该路径。
具体来说,codecall:在@frontmcp/plugin-cocodecall中执行,将实时主机对象交给沙盒脚本。
getTool()返回目标工具的真实Zod模式实例,Zod v4将_Zod定义为不可配置、不可写的自身属性,
因此ECMAScript代理不变量迫使沙箱膜交还原始主机对象。
从那里,一个脚本写道_zod.constr.constructor是宿主函数构造函数,在服务器进程中执行任意代码。
一个MCP工具/调用就足够了,在框架的默认身份验证模式下不需要凭据。
漏洞详情
plugins/plugin-cocodecall/src/tools/execute.tool.ts:156-172构建VM环境并向脚本公开工具元数据:
getTool: (name: string) => {
try {
const tools = this.scope.tools.getTools(true);
const tool = tools.find((t) => t.name === name || t.fullName === name);
if (!tool) return undefined;
return {
name: tool.name,
description: tool.metadata?.description,
inputSchema: tool.rawInputSchema,
outputSchema: tool.outputSchema,
};
} catch {
return undefined;
}
},
rawInputSchema和outputSchema是活动的Zod模式实例,而不是序列化数据。
它们作为自定义全局变量传递到encreat中,函数值在plugins/plugin-cocodecall/src/services/encreat.service.ts:139中明确允许:
// Allow functions in globals since we intentionally provide getTool, mcpLog, mcpNotify, and console
allowFunctionsInGlobals: true,
飞地将主机值封装在一个安全的代理中,该代理阻止构造函数、__proto__和原型。
该膜适用于桥接的闭包:
getTool.constructor、callTool.constractor和mcpLog.constructors都引发了“安全违规:对'构造函数'的访问被阻止”。
它不能适用于Zod模式。Zod 4.4.3将_Zod安装为一个自己的数据属性,具有可配置的:false、可写的:false,以及一个代理获取陷阱,
该陷阱为此类属性返回除目标自身值之外的任何值,并抛出TypeError。
因此,膜必须返回原始宿主对象,并且遏制在该跳跃处结束:
getTool("codecall:invoke").outputSchema -> proxied, .constructor blocked
getTool("codecall:invoke").outputSchema._zod -> RAW host object
_zod.constr是宿主ZodObject类。
它的构造函数是host Function在宿主领域编译代码,生成process、process.mainModule.require和儿童进程。
AST保护程序确实拒绝直接表单。
({}).constructor.constructor被拒绝使用NO_constructor_ACCESS,.prototype被拒绝使用DISALLOWED_IDENTIFIER。
这两个规则都匹配解析源中的标识符和静态成员名称,因此它们看不到在运行时组装的键:
const k = ["c","o","n","s","t","r","u","c","t","o","r"].join("");
文档化管道的第1层是词法denylist,因此它不会限制什么
第3层在运行时分发。这两层是为了相互支持。
有两个特性使其价格低廉。
插件自己的工具满足了对模式承载工具的要求,因此getTool(“codecall:invoke”)的工作不依赖于宿主应用程序注册的内容。
libs/sdk/src/AUTH/AUTH.register.ts:39中的DEFAULT_AUTH_OPTIONS是{mode:'public'},
在相邻的注释中被描述为“所有工具都打开的公共模式”,因此不配置AUTH的服务器为匿名调用者提供codecall:execute。
根据已发布的@frontmcp进行验证/plugin-codecall@1.5.6:
其捆绑的dist/index.js包含相同的inputSchema:tool.rawInputSchema、outputSchema:tool.outputSchema返回(大约在第2747行)
和相同的allowFunctionsInGlobals:true(大约在1383行)。
三、POC概念验证
1、POC代码
管理员已设置登录后刷新可查看目标:apps/e2e/demo-e2e-codecall/src/main.ts,此存储库中附带的e2e服务器。
它使用默认安全VM加载CodeCallPlugin.init({mode:'codecall_only',topK:10})
预设和认证:{模式:'public'}。没有什么被削弱。
POSITIVE
{"status":"ok","result":{"escaped":true,"pid":9831,"nodeVersion":"v24.18.0",
"cwd":"/root/xbow-final/targets/frontmcp/source","whoami":"root","hostname":"fg0x0",
"envVarCount":77,"secretsVisible":["GH_TOKEN","H1_API_TOKEN"],
"fileWritten":"-rw-r--r-- 1 root root 30 /tmp/frontmcp_rce_pos-1784963328.txt"}}
Checked from outside the server process:
-rw-r--r-- 1 root root 30 Jul 25 15:08 /tmp/frontmcp_rce_pos-1784963328.txt
host_rce_via_codecall_execute
BASELINE, the sandbox is real and the naive escape is refused
return ({}).constructor.constructor("return process")().pid;
{"status":"illegal_access","error":{"message":"AgentScript validation failed:
NO_CONSTRUCTOR_ACCESS (line 2): Access to .constructor property is not allowed ..."}}
BASELINE, the pidged closures are mempane-protected, only host data leaks
typeof getTool[k]
{"status":"runtime_error","error":{"message":"Security violation: Access to 'constructor'
is blocked. This property can be used for sandbox escape attacks."}}
ROOT CAUSE, containment is lost exactly at _zod
{"onProxiedSchema":"blocked: Security violation: Access to 'constructor' is blocked",
"onRaw_zod":"LEAKED host Object"}
zod 4.4.3 _zod own-property descriptor = {"configurable":false,"writable":false}
NEGATIVE CONTROL, execute.tool.ts:166-167 changed to expose inert JSON Schema
inputSchema: tool.rawInputSchema -> tool.getInputJsonSchema?.()
outputSchema: tool.outputSchema -> tool.getOutputJsonSchema?.()
Same exploit, fresh nonce so nothing is served from cache:
{"status":"runtime_error","error":{"message":"Cannot read properties of undefined (reading 'constr')"}}
ls: cannot access '/tmp/frontmcp_rce_neg-1784963334.txt': No such file or directory
No functional regression under the fix:
{"status":"ok","result":{"toolName":"users-list","schemaStillUsable":"object"}}
完整的记录,包括服务器启动和两次控制运行,都在poc/poc_evidence.txt中并且可以用poc/verify.sh重现。
一个针对实时服务器的纯curl复制,带有原始HTTP状态行,位于poc/repro_curl.sh并附加到poc/poc_evidence.txt:
### step 1 - initialize (no Authorization header)
HTTP/1.1 200 OK
mcp-session-id: ePu5MXdYY-mqVDMs...
### step 2 - tools/call codecall:execute (escape -> host command execution)
HTTP/1.1 200 OK
data: {"result":{"content":[{"type":"text","text":"{\"status\":\"ok\",\"result\":\"root\\nexploit_marker_observed\\n\"}"}],...}
### step 3 - host filesystem, checked outside the server process
-rw-r--r-- 1 root root 24 /tmp/frontmcp_rce_curl-1784963935.txt
exploit_marker_observed
2、潜在影响
(1)沙盒逃逸导致远程代码执行CWE-94,其中CWE-501作为底层信任边界违规。
一个codecall:execute调用在MCP服务器进程中运行任意代码:
上面的run以服务器用户的身份执行shell命令,读取进程环境并命名在那里找到的秘密,然后将文件写入主机文件系统。
(2)服务器保存的任何内容都会被公开,包括OAuth客户端机密、JWT_SECRET、会话保管库密钥、数据库凭据和云实例元数据。
前提条件是CodeCallPlugin已加载,并且调用者可以调用codecall:execute。
在默认auth:{mode:'public'}下,即任何未经身份验证的网络客户端。
配置了auth后,它是任何授权的客户端,其中包括插件要服务的LLM,
因此在工具输出或获取的内容中进行间接提示注入就足以触发它,而无需人类攻击者接触端点。
(3)此插件的记录状态与此相反。docs/frontmcp/plugins/codecall/security.mdx描述了“银行级安全”,
一种“零信任运行时”,以及其声明目的是“防止沙盒逃逸和系统访问”的块。
AST保护程序正在根据它可以看到的语法执行工作。
差距在于,一个实时主机对象图首先会越过边界,由JavaScript不变量而不是缺失的检查决定结果。
(4)狭义的修复方法是停止将模式实例传递到包围区中,
而是传递getInputJsonSchema()/getOutputJsonSchema(),这就是负控件的作用。
值得考虑的是:
只有结构上克隆或冻结的纯数据才能进入沙盒,因为任何携带不可配置属性的主机对象都会在设计上破坏代理膜,
AST防护的构造函数和原型规则也应在运行时对计算成员访问强制执行,而不仅仅是词法上。
四、影响范围
FrontMCP ≤ 1.5.6
五、修复建议
FrontMCP ≥ 1.5.7
六、参考链接
管理员已设置登录后刷新可查看