开源、可自托管的分析平台 OpenPanel 被披露一个 CVSS 9.9 的沙箱逃逸漏洞 CVE-2026-93985。问题位于 @openpanel/js-runtime 的 Webhook 模板校验器:它只检查点号形式的成员访问,未拦截方括号形式的计算属性,攻击者借此触达 Function 构造器,在承载全部租户的共享 Worker 进程里执行任意代码。GitHub 安全公告 GHSA-6f7h-cvp6-w9w5 附有完整可复现 PoC,官方尚未发布修复版本。
事件概述
漏洞影响 OpenPanel 的 js-runtime 组件至提交 bad75bdd,CVE 于 9 月 19 日由 VulnCheck 与 NVD 分配。触发前提是攻击者拥有目标项目的 write 权限(requireProjectAccess),并非未认证或管理员权限。目前没有在野利用报告,也未进入 CISA KEV 目录。OpenPanel 被广泛用于自托管站点分析,共享 Worker 架构使单个项目权限的滥用影响面远超该项目自身。
技术细节
校验逻辑的缺口在 packages/js-runtime/src/validate.ts:第 235 至 248 行的白名单只检查非计算形式的 Identifier,第 254 至 262 行的 NewExpression 检查也只看 Identifier 类型的 callee。因此 payload.constructor 这类点号访问会被拒绝,但 payload[‘constructor’][‘constructor’] 这类计算成员表达式完全未经检查,直接通过校验。
模板在 packages/js-runtime/src/execute.ts 第 14 至 23 行经宿主 new Function 执行时,模板里的 payload[‘constructor’][‘constructor’](‘return process’)() 会在运行时解析为全局 Function 构造器,编译并执行恶意字符串。更关键的是注册表逻辑(packages/integrations/src/registry.ts):模板只在保存时校验,投递执行时不再校验。
影响评估
9.9 的高分来自 S:C(作用域改变):逃逸后攻击者获得的是 Worker 进程的权限,而该共享 Worker 同时持有 PostgreSQL、Redis 与 ClickHouse 凭据,并服务所有租户,跨租户边界被彻底打破。一个只拥有单个项目写权限的普通用户,即可读取其他租户数据或横向渗透至后端数据存储。攻击复杂度低、无需用户交互,官方 PoC 已在 Node v22 上演示 root 级命令执行。
修复建议
- 官方暂无补丁。先收缩写权限,只把项目 write 授予可信成员,并审计现有 Webhook 模板。
- 在 PostgreSQL 中排查可疑模板:SELECT id, project_id, template_body FROM webhook_templates WHERE template_body LIKE ‘%constructor%’ OR template_body LIKE ‘%Function%’;
- 用 systemd 覆盖项为 Worker 降权:NoNewPrivileges=true、ProtectSystem=strict、PrivateTmp=true、DynamicUser=yes,避免其以 root 运行。
- 限制 Worker 到数据库与缓存的网络出口,监控 Node 进程的非预期子进程创建。
- 持续关注上游补丁,发布后立即升级。