通过数据二极管发送日志、警报和遥测数据

了解详情
我们利用人工智能进行网站翻译,虽然我们力求准确,但不一定总是 100%精确。感谢您的理解。

CVE-2026-25049:n8n 中表达式Sandbox 导致远程代码执行

作者: Loc Nguyen,渗透测试团队负责人
分享此贴

2026年2月,一款被广泛采用的开源工作流自动化平台n8n中被公开披露了一个关键的沙箱逃逸漏洞。该漏洞编号为CVE-2026-25049,可使经过身份验证的用户绕过表达式评估沙箱,并在主机服务器上执行任意系统命令,从而实现完整的远程代码执行,其CVSS v3.1评分为9.9。

OPSWAT 研究员计划的一部分,我们的研究员对CVE-2026-25049进行了全面的技术分析,深入探讨了其根本原因、利用机制以及对组织的影响。

本文详细剖析了该漏洞——从 n8n 的表达式处理架构及其分层安全控制机制,到能够同时突破全部五层防御的具体技术手段。

CVE-2026-25049 是 n8n 中一个严重的表达式沙箱逃逸漏洞,归类于 CWE-913:对动态管理代码资源的控制不当。 该漏洞影响 n8n 1.123.17 之前的所有版本,以及 2.0.0 至 2.5.1 版本,已在 1.123.17 和 2.5.2 版本中修复。该漏洞允许具有工作流创建权限的经过身份验证的用户构造恶意 JavaScript 表达式,从而绕过平台的表达式沙箱,最终在主机服务器上实现任意命令执行。

图1:CVE-2026-25049(来源:NVD)

该漏洞的危害性尤为严重,因为 n8n 在组织基础设施中通常占据着特权地位。作为工作流自动化枢纽,n8n 通常直接访问内部 API、数据库、凭证存储库以及第三方服务。一旦 n8n 实例遭到入侵,不仅会使自动化服务器暴露在外,还会成为入侵所有关联系统的跳板,从而极大地扩大了攻击的影响范围。

CVE-2026-25049 并非独立发现的漏洞,而是对 CVE-2025-68613 补丁的绕过手段,后者是 n8n 表达式评估器中较早发现的一个沙箱逃逸漏洞。 尽管在最初发现漏洞后已实施了多层防御措施,但净化器在处理 JavaScript 抽象语法树(AST)节点类型时存在根本性缺陷,导致原有的攻击类型通过不同的语法途径卷土重来。这种模式——即补丁仅针对具体的利用技术而非底层设计缺陷——凸显了保障动态代码评估环境安全所面临的持久挑战。

该漏洞于2026年2月4日与另外十份针对n8n的安全公告一同披露,并已由多个安全研究团队进行了独立分析。

关于 n8n

n8n 是一个开源的工作流自动化平台,可帮助组织连接系统、自动化流程,并在数百种服务之间构建集成。凭借广泛的采用和超过 1,300 种可用的集成方案,n8n 已成为该领域最受欢迎的工具之一,被开发团队和企业广泛用于自动化各类任务,从内部工具到业务关键型数据管道无所不包。

图 2:n8n 工作流示例。

该平台同时支持自托管和云部署,不仅提供可视化工作流构建器,还直接支持 JavaScript 以实现自定义逻辑。n8n 采用基于节点的架构:工作流由相互连接的节点组成,这些节点在触发器、操作和函数步骤之间以 JSON 格式传递数据。 执行模式可切换为手动模式用于测试,或切换为生产模式进行实时部署。这种架构结合对 JavaScript 表达式的原生支持,以及对内部 API 和凭证存储的访问权限,使 n8n 成为一款强大的自动化工具——但也使其在出现漏洞时成为高价值的目标。

技术背景

抽象语法树

抽象语法树(AST)是解析器生成的源代码的一种分层表示形式。与将代码视为扁平字符串不同,AST 将其分解为结构化的节点,这些节点分别代表其语法元素——变量声明、函数表达式、成员表达式、标识符和字面量。

图3:编译器的各个阶段。

了解 AST 节点类型对于本次分析至关重要,因为 n8n 的安全控制是在 AST 层级运行的。清理器会检查特定的节点类型,以检测并阻止危险模式。该漏洞利用了这样一个事实:相同的语义操作(访问对象的属性)可能会根据所使用的 JavaScript 语法产生完全不同的 AST 节点类型。

图 4:JavaScript 中的部分 AST 节点。

例如,表达式 `obj.constructor` 会生成一个`MemberExpression`节点,而 `const{ constructor } = obj` 则会生成一个 包含 `ObjectPattern`(其中包含一个 `Property` 节点)的`VariableDeclaration`。虽然两者都提取了相同的属性,但它们的抽象语法树(AST)结构却截然不同——而 n8n 的验证器仅会检查第一种模式。

n8n 表达式评估与安全架构

n8n 的架构分为五个层:前端、命令行界面 (CLI)、Core、工作流和节点。工作流层负责表达式的求值,而这正是与该漏洞相关的组件。

图5:n8n 流程概览。

n8n 允许用户在工作流节点参数中嵌入 JavaScript 表达式,以动态操作数据。这些表达式在服务器端通过一个名为 Tournament 的库进行求值,该库会在执行前将表达式解析为抽象语法树(AST)。数据净化钩子会直接注入到 Tournament 的创建过程中,在构建 AST 时执行检查,从而在任何代码执行之前拦截危险模式。

图6:初始锦标赛创建。

由于这些表达式与 n8n 服务器运行在同一个 Node.js 进程中,该平台实现了五层独立的安全防护机制,旨在防止不可信代码逃离沙箱。

第1层 - 全局上下文覆盖

在评估任何表达式之前,n8n 会通过覆盖危险的全局对象和函数来创建一个受限的执行环境。诸如 document、window、globalThis、eval、setTimeout、setInterval 和 Function 等属性会被替换为空对象,从而阻止直接访问这些功能。

图 7:启动全局上下文。
图 8:覆盖所有危险函数。

这种方法存在一个固有的局限性:如果攻击者能够逃离受限上下文并访问真正的全局进程对象,那么被覆盖的属性就变得无关紧要了。

第 2 层 - 正则表达式验证

在表达式到达求值器之前,n8n 会对原始表达式字符串进行正则表达式检查,以阻止对构造函数属性的访问——这是获取 Function 构造函数并实现代码执行的常见途径。

图 9:正则表达式验证。

正则表达式/\.\s*constructor/gm可检测对.constructor 的点表示法访问。然而,这种检查存在根本性的局限性,因为 JavaScript 提供了多种语法路径来访问同一属性,而并非所有路径都涉及点表示法。

第 3 层 - AST 运行时检查器 – PrototypeSanitizer

最复杂的防御机制在 AST 构建过程中发挥作用。PrototypeSanitizer 钩子会遍历 AST,并检查 MemberExpression 节点,以确定被访问的属性是否出现在不安全对象属性的黑名单中——包括 constructor、__proto__、prototype、mainModule 和 binding。

图10:消毒器原型。

该净化器处理三种情况:点表示法(obj.prop),此时它会检查标识符名称;静态方括号表示法(obj['prop']),此时它会检查字符串字面量值;以及动态表达式,这些表达式会被封装在运行时净化器函数中。如图 10 所示,仅检查 MemberExpression 节点——不会遍历解构模式。

第 4 层 - AST 运行时检查器 FunctionThisSanitizer

作为针对 CVE-2025-68613 的直接补丁,FunctionThisSanitizer 会拦截立即调用函数表达式(IIFE),并将其重写为将 `this` 绑定到一个空对象。这可以防止原始漏洞利用中采用的技术——即通过(function(){ return this })()利用未绑定的 `this` 引用泄露全局进程对象。

图 11:GlobalThisSanitizer 函数

关键在于,该清理器仅处理 FunctionExpression 节点。对于任何非 FunctionExpression 的被调用者(包括箭头函数),它都会显式地提前返回。

在执行上下文中,已移除或覆盖了 eval、Function 和 process.mainModule 等危险属性,从而即使绕过了前层,也无法直接访问代码执行原语。

脆弱性分析

根本原因

CVE-2026-25049 的根本原因在于所有五个安全层都存在一个共同的假设:每一层都假设属性访问将通过点表示法(obj.constructor)或方括号表示法(obj['constructor'])进行。但没有一层考虑到 JavaScript 的解构语法。

JavaScript 的解构允许通过一种截然不同的抽象语法树(AST)结构从对象中提取属性:

// Traditional access - produces MemberExpression node
obj.constructor; // Blocked by regex, AST sanitizer, and runtime checks

// Destructuring - produces ObjectPattern → Property node
const { constructor } = obj; // Not checked by any layer

尽管这两种模式都能达到相同的效果,但它们生成的抽象语法树(AST)表示却截然不同。PrototypeSanitizer 仅检查 MemberExpression 节点,而正则表达式仅匹配 .constructor 模式。解构赋值会生成 VariableDeclaration → ObjectPattern → Property 节点,而这些节点均不在任何清理器的遍历范围内。

此外,n8n 对箭头函数的处理方式也加剧了这一问题。FunctionThisSanitizer 仅拦截 FunctionExpression 节点,并将它们重写为将 this 绑定到安全上下文中。箭头函数会生成 ArrowFunctionExpression AST 节点,而该净化器并不处理这些节点。由于箭头函数从其外围作用域继承 this(词法 this),因此它们允许访问未经净化的全局上下文。

这两个疏漏结合在一起,导致了完全的绕过:解构操作提取了 Function 构造函数,却未触发任何属性访问检查;而箭头函数则提供了一个执行上下文,而 FunctionThisSanitizer 并未拦截该上下文。

剥削

该漏洞利用技术结合了这两个漏洞,同时突破了所有安全层。我们的同事追踪到了以下攻击路径:

步骤 1 - 箭头函数输入。有效载荷以一个包裹在立即调用函数表达式(IIFE)中的箭头函数开头:

={{(() => {
// 箭头函数—— 绕过 FunctionThisSanitizer
...
})()}}

该 FunctionThisSanitizer 会检查被调用者是否为 FunctionExpression;由于这是一个 ArrowFunctionExpression,因此该净化器会在不重写调用操作的情况下提前返回。箭头函数在执行时能够完全访问真实的全局上下文。

步骤 2 - 解构绕过。在箭头函数内部,解构操作会从箭头函数实例中提取出 Function 构造函数:

const { constructor } = () => {};

由于这是一次解构赋值,AST 中包含的是 ObjectPattern 节点,而非 MemberExpression。正则表达式检查未发现 .constructor 模式,且 PrototypeSanitizer 从未检查过属性名称。此时,攻击者已持有对 Function 构造函数的引用。

步骤 3 - 动态代码执行。获取 Function 构造函数后,攻击者构建并执行任意代码:

return constructor(
'return process.mainModule.require("child_process").execSync("whoami").toString()',
)();

该动态构建的函数在沙箱环境之外运行,并能完全访问 Node.js 进程对象,从而能够在主机上执行任意系统命令。

图12:最终攻击载荷

概念验证

为了展示该漏洞在实际环境中的影响,我们的研究员在受控的实验室环境中重现了该漏洞利用过程。攻击场景包括部署一个存在漏洞的 n8n 实例,并导入一个在节点参数中包含恶意有效载荷的工作流——该攻击专门针对允许表达式评估的“编辑字段”节点。

图 13:管理员导入恶意工作流。

一旦工作流被触发,有效载荷便会绕过全部五个安全过滤层,并在主机服务器上执行反向shell,从而使攻击者获得完整的命令执行权限。

图 14:通过工作流触发器执行有效载荷。

Webhook 漏洞升级为未认证远程代码执行

当结合 n8n 的 webhook 功能时,该漏洞的严重性会显著增加。n8n 允许工作流将 HTTP 端点作为 webhook 公开,并提供可配置的身份验证选项,包括承载令牌、基本身份验证,以及——至关重要的是——完全不进行身份验证。 拥有工作流创建权限的攻击者可以配置一个认证方式为“none”的公共 webhook,将 RCE 有效载荷嵌入到关联的节点中,并激活该工作流。此时,来自互联网任何位置的对 webhook URL 的 HTTP 请求都会触发主机服务器上的任意命令执行。

该权限提升路径将CVE-2026-25049从一个经过身份验证的内部漏洞,转变为一个实质上无需身份验证且面向整个互联网的攻击途径。

缓解

n8n 团队通过在数据净化函数中实现适当的运行时类型检查,并扩展抽象语法树(AST)的覆盖范围以处理解构模式,已在 1.123.17 和 2.5.2 版本中修复了 CVE-2026-25049 漏洞。运行受影响版本的组织应立即进行升级。

如果无法立即升级,管理员应将工作流的创建和编辑权限仅授予完全受信任的用户,并在操作系统权限和网络访问均受限制的强化环境中部署 n8n。

鉴于该漏洞的严重性及其易受攻击的特性——尤其是与公共 Webhook 结合使用时——组织还应审核现有工作流中是否存在可疑表达式,监控源自 n8n 进程的异常系统命令执行,并检查 Webhook 配置中是否存在未经过身份验证的暴露端点。

利用OPSWAT进行缓解

通过利用OPSWAT ,企业能够快速识别其基础设施中存在漏洞的 n8n 组件,并在漏洞遭到利用之前采取应对措施。作为 MetaDefender® 平台的基础技术OPSWAT 可全面盘点企业环境中正在使用的所有软件组件、库及依赖项。

图 15:在OPSWAT 中检测 CVE-2026-25049

如图 15 所示MetaDefender 对包含 n8n 依赖项的 package.json 文件进行了扫描,并自动将 CVE-2026-25049 标记为“严重”级别,同时显示了受影响的版本范围以及建议的修复版本。这使安全团队能够在其部署环境中快速识别该漏洞并确定其优先级。

OPSWAT 已集成于MetaDefender 和MetaDefender Software Chain™ 中,可帮助安全团队:

  • 快速定位 存在安全隐患的组件——立即识别受沙箱逃逸和代码执行漏洞影响的开源依赖项,以便迅速进行修复或移除。
  • 确保主动进行补丁修复和更新——持续监控开源组件,以发现过时或存在安全隐患的软件包,从而及时进行更新并降低风险。
  • 确保合规与报告——随着相关框架对软件供应链透明度的要求日益严格,需满足监管要求。

参考资料

通过OPSWAT 了解最新信息!

立即注册,即可收到公司的最新动态、 故事、活动信息等。