再次打破沙盒绕过n8n的CVE-2026-27577补丁

admin 2026-08-09 04:56:30 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档披露了n8n工作流自动化平台中一个沙箱逃逸漏洞,该漏洞绕过了CVE-2026-27577的补丁。攻击者通过利用箭头函数中的多态填充间隙和Reflect.get的字符串参数绕过属性拒绝列表,实现了远程代码执行。漏洞影响拥有工作流编辑权限的用户,可导致主机级别的RCE。建议用户立即更新n8n,并限制工作流编辑权限。 综合评分: 90 文章分类: 漏洞分析,代码审计,红队,漏洞预警,应用安全


cover_image

再次打破沙盒绕过 n8n 的 CVE-2026-27577 补丁

原创

骨哥说事 骨哥说事

骨哥说事

2026年7月28日 08:50 上海

在小说阅读器读本章

去阅读

| | | — | | 声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。 |

#

#

防走失:https://gugesay.com

背景

n8n 是全球最受欢迎的开源工作流自动化平台之一,拥有超过 20 万个 GitHub 星标,部署范围从独立开发者到企业 AI 流水线。它允许用户使用可视化工作流编辑器连接 API、数据库、LLM 和云服务。每个工作流的核心是一个表达式求值器——一个 JavaScript 沙箱,允许用户直接在节点参数中编写动态逻辑,如 ={{ $input.first().json.name }}。这个沙箱就是攻击面。

先前研究 – 模式化的不完整修复

n8n 长期以来一直存在沙箱逃逸的历史。这个链条始于 CVE-2025-68613(2025 年底):研究人员发现 (function(){ return this })() 泄露了真正的 Node.js process 对象,从而实现了完整的 RCE。n8n 通过 ThisSanitizer 进行了修补,该工具将所有函数表达式中的 this 绑定到一个空对象。 然后是 CVE-2026-25049(2026年2月,CVSS 9.9):一个利用模板文字键的解构绕过—— { [\constr${'uct'}o]: c } ——完全绕过了消毒器。 再次修补。 然后是 CVE-2026-27577(2026 年 2 月 25 日,CVSS 9.4):研究人员发现 SpreadElement 内的 {...process} 从未被 VariablePolyfill 重写,从而暴露了真正的全局 process 对象。 n8n 的修复在 expression-sandboxing.ts 中添加了一个 visitSpreadElement 钩子,将裸标识符展开参数替换为安全的条件语句。 每次:一个补丁。 每次:又有新的进入方式。 我们寻找下一个机会。

研究 – 寻找残存漏洞

我们克隆了 n8n 的 HEAD 版本(版本 2.31.0,提交 2fc48bdf),并专注于 packages/workflow/src/expression-sandboxing.ts 和 @n8n/tournament 的 VariablePolyfill.ts 中的表达式沙箱。 CVE-2026-27577 的修复对于其声明的情况是清晰的。visitSpreadElement 正确重写了 {...process}

但仔细阅读 VariablePolyfill.ts揭示了一些该修复未能解决的问题:jsVariablePolyfill 函数将自由标识符(如 processReflectglobalThis)重写为安全的数据上下文查找——但仅限于特定的 AST 父节点类型。当父节点类型是 ArrowFunctionExpression 时,switch 语句会遇到显式的 break(无操作)。 这是设计使然:箭头函数 签名 或 主体位置 中的标识符不会被多态填充(polyfilled)。 这意味着 () => process ——一个返回 process 的箭头函数—— 完全绕过了重写器。通过 (() => process)() 立即调用它,会在基于 new Function 的求值中产生真正的 Node.js process 对象。

第二部分:n8n 的属性拒绝列表(getBuiltinModulebindingdlopen 等)通过 visitMemberExpression 强制实施,该钩子检查静态属性名称。Reflect.get(obj, 'getBuiltinModule') 传递一个字符串参数——而不是属性名称——因此拒绝列表永远不会触发。Reflect 本身可以通过相同的方式访问:(() => Reflect)()

将两者结合: 你可以获得 process.getBuiltinModule 的实时句柄,并调用 child_process.execSync

有效载荷

={{ ((R,p)=>R.get(p,'getBuiltinModule'))((()=>Reflect)(),(()=>process)())('child_process').execSync('id').toString() }}

逐步说明:

  • (()=>Reflect)() – 箭头函数捕获真正的 Reflect,绕过多态填充
  • (()=>process)() – 箭头函数捕获真正的 process,绕过多态填充
  • R.get(p,'getBuiltinModule') – Reflect.get 绕过 MemberExpression 拒绝列表
  • ('child_process').execSync('id') – 主机操作系统命令执行

概念验证

我们通过两种方式验证:

  1. 使用与已发布版本一起提供的相同 n8n-workflow 包。

  2. 在本地启动 npx n8n,将该表达式放置在 Set 节点参数中,并执行工作流——输出字段返回了主机的 id 命令结果。 两种方式均在 n8n 2.30.4 上验证,该版本是在 CVE-2026-27577 修复线之后发布的。

影响

任何拥有工作流创建或编辑权限的经过身份验证的用户都可以实现主机级别的 RCE,并以 n8n 进程用户身份运行。在云托管环境中,这意味着:

  • 读取 process.env 以获取 API 密钥、OAuth 令牌、N8N_ENCRYPTION_KEY
  • 解密 n8n 保管库中存储的每个凭据
  • 横向移动到可从 n8n 主机访问的内部服务、数据库和云元数据端点

漏洞利用需要拥有工作流编辑权限(PR:L)的登录。预期的 CVSS 评分与 CVE-2026-27577 在同一区间(约 9.4,严重)。

时间线

2026 年 7 月 14 日 – 在 n8n HEAD 和 2.30.4 版本上识别出沙箱逃逸残留漏洞 2026 年 7 月 15 日 – 通过 n8n 漏洞披露计划报告 2026 年 7 月 15 日 – 报告 ID 发出:th_01KXJHJJ3WEK3R84HHHHF4F50K2026 年 7 月 22 日 – n8n 确认:漏洞已接受,修复已部署,CVE 编号待分配。

安全公告链接: https://github.com/n8n-io/n8n/security/advisories/GHSA-gv7g-jm28-cr3m

本次漏洞的不同之处

多数 n8n 沙箱研究每次只针对一个 gadget。此绕过需要两个独立的重写器盲点协同工作:箭头函数多态填充间隙(结构性的)和 Reflect.get 字符串参数拒绝列表间隙(语义上的)。单独任何一个都不够。两者都未被测试覆盖。 CVE-2026-27577 的修复在 visitSpreadElement 中添加了 15 行代码。它完全堵住了展开操作的向量。

但它没有提出一个更广泛的问题:多态填充器还跳过了哪些其他 AST 父节点类型? 回答这个问题正是发现此绕过漏洞的原因。

负责任的披露我们在发布任何内容之前直接向 n8n 的 VDP 报告。未测试任何在线实例。所有研究均在运行已发布的 npm 包的本地自托管实例上进行。我们等待了确认和修复后才撰写本文。

修复n8n 已确认修复已部署。如果您是自托管用户,请立即更新。 作为短期缓解措施:设置 N8N_EXPRESSION_ENGINE=vm 或将工作流创建权限仅限制给完全信任的用户。如果您运行了受影响的版本且允许不受信任的用户编辑工作流,请轮换 N8N_ENCRYPTION_KEY 和所有存储的凭据。

  • END –

感谢阅读,如果觉得还不错的话,动动手指给个三连吧~


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:骨哥说事 骨哥说事 骨哥说事《再次打破沙盒绕过 n8n 的 CVE-2026-27577 补丁》

评论:0   参与:  0