从一个Schema名到RCE:n8n原型污染漏洞(CVE-2026-33696)分析"

admin 2026-10-16 05:54:35 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

从一个 Schema 名到 RCE:n8n 原型污染漏洞(CVE-2026-33696)分析

Ots安全

2026年9月30日 20:31
广东

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

威胁简报

恶意软件

漏洞攻击

引子

一个自动化工作流平台,最危险的地方往往不是某个复杂的 0day,而是把用户随手填的一串字符串,直接当成了对象的属性名。n8n 的 GSuiteAdmin 节点就栽在这件事上:攻击者只要在\"自定义字段\"里把 schema 名写成 __proto__,就能把任意字符串写进 JavaScript 的全局对象原型,进而借助一个下游的 Git 节点,把\"原型污染\"升级成\"远程代码执行\"。

更刺眼的是:这条链只需要一个能编辑工作流的账号,一次 HTTP 请求,就能以 n8n 进程用户的身份执行命令——而 n8n 进程手里,握着实例里所有凭据的加密密钥。也就是说,这里的 RCE 不是一块落脚点,而是一整串钥匙环。

一、漏洞速览

项目 内容
CVE 编号 CVE-2026-33696
GitHub 公告 GHSA-mxrg-77hm-89hv
漏洞类型 CWE-1321 对象原型属性可控修改(Prototype Pollution)
CVSS v4.0 9.4(Critical);v3.1 8.8(High)
受影响组件 n8n 的 GSuiteAdmin 节点与 XML 节点(公告同时覆盖两者)
影响版本 < 1.123.27;2.0.0-rc.0 起至 < 2.13.3;以及 2.14.0
修复版本 1.123.27 / 2.13.3 / 2.14.1
利用前提 已认证、具备创建工作流/修改工作流权限的用户
攻击向量 网络(AV:N);复杂度低;所需权限低;无需用户交互

二、作者是怎么挖到的:一个朴素的方法论

Simon Koeck 在审计 n8n 的节点实现时,给自己定了一条很简单的检索规则:凡是有\"用户提供的字符串,被当作普通对象的属性键、且未先检查 __proto__ / constructor / prototype\"的地方,都值得看一眼。

他在 n8n-nodes-base 包里 grep 这类模式,GSuiteAdmin 节点立刻\"脱颖而出\"。该节点的\"自定义字段(Custom Fields)\"在创建/更新用户时,允许用户配置 schema 名(schemaName)、字段名(fieldName)、值(value)——这三个字段全部来自工作流配置,也就是说,任何能编辑工作流的攻击者都能完全控制它们。


三、漏洞根因:把用户输入当对象键

GSuiteAdmin 节点在用户创建(约第 520–521 行)与更新(约第 802–803 行)操作中,用了完全相同的一段代码来处理自定义 schema 字段:

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)});

整个 bug 就藏在这两行。当 schemaName 是 \"__proto__\" 时:

  1. customSchemas[\"__proto__\"]

会触发 __proto__ 的 getter,返回 Object.prototype。它不是 nullish,于是 ??= 的赋值成了空操作;
2. (Object.prototype)[fieldName] = value

把攻击者可控的字符串直接写到了全局对象原型上。

此后创建的每一个普通对象,都会继承这个被污染的属性。


四、从污染到 RCE 的利用链

光是污染本身就已经够糟(它会通过 TypeORM 把整个 n8n 实例打崩,详见第六节),但它还能借由作者此前在 XML 节点报告里发现的同一个 gadget,升级成完整的 RCE。链路是这样串起来的:

  1. simple-git 创建普通 env 对象

:当 Git 节点调用 .env() 时,simple-git 会分配一个 {} 来装环境变量,这个对象继承自 Object.prototype;
2. Node.js 的 spawn() 会继承被污染的属性

:在构造子进程环境时,Node 会遍历 env 对象的属性——包括从被污染原型上继承来的那些;
3. git 尊重 GIT_SSH_COMMAND

:当 git 遇到一个 SSH 风格的 URL,它会把 GIT_SSH_COMMAND 当作 shell 命令来启动。如果我们污染了 Object.prototype.GIT_SSH_COMMAND,它就会一路传导进 git 子进程并执行。

所以完整的攻击就是:Webhook → GSuiteAdmin(污染)→ Git(RCE)。


五、PoC 逻辑骨架(公开披露的复现思路)

原文的复现工作流由三个节点组成:

  • Webhook 节点

:监听 POST /rce;
* GSuiteAdmin 节点

:资源选 User、操作选 Create,把自定义字段的 schema 名、字段名、值都设为从 Webhook 请求体读取的表达式;
* Git 节点

:操作选 Clone,指向一个 SSH 风格的 URL。

原文披露的那一关键请求,其核心是把三个字段分别设为:

{  \"schemaName\":\"__proto__\",  \"fieldName\":\"GIT_SSH_COMMAND\",  \"value\":\"<攻击者期望执行的命令>\"}

说明(合规边界):按照原文披露的技术事实,上述三个键名(__proto__、GIT_SSH_COMMAND)与触发逻辑是该漏洞利用的核心,予以保留;但不直接粘贴可一键执行的恶意命令载荷。GSuiteAdmin 节点会在真正发起 Google API 调用时才失败——而此时污染早已发生,所以执行日志里看到一个\"失败的 GSuiteAdmin 节点\"并不等于攻击没成功。随后 Git 节点以被污染的 GIT_SSH_COMMAND 执行 git clone,命令以 n8n 进程用户的身份落地。


六、附带的 DoS 副作用

即便不接 Git 这条 RCE 链,污染本身也具备破坏性。在 Object.prototype 被写入杂质属性之后,TypeORM 的 buildWhere 函数会通过 for...in 遍历把那些继承来的额外属性也一并收进去,于是在每一条数据库查询上都抛出 EntityPropertyNotFoundError:

结果就是 n8n 后台界面卡死、所有工作流执行失败,实例必须完整重启才能恢复。换句话说,一个手法笨拙的攻击者,甚至能\"顺手\"把你整座平台搞瘫。


七、影响范围

  • 远程代码执行

:以 n8n 进程用户身份,在所有部署形态(自托管、worker 模式、Cloud)上均可触发;
* 凭据全面失窃

:n8n 进程持有实例内所有已存凭据的加密密钥;
* 拒绝服务(DoS)

:TypeORM 的崩溃循环让实例在重启前完全不可用。


八、修复与防御(含本文增补的运维要点)

官方的修复思路很直接:在把用户字符串当作对象键之前,先拒绝危险的属性名。对 schemaName 做一份针对 __proto__ / constructor / prototype 的黑名单检查,或者直接把 customSchemas 改成 Object.create(null),就能从根上堵住污染。n8n 代码库里其实已经有一个带原型污染防护的 deepMerge 工具函数,只是 GSuiteAdmin 节点此前没用上它。

以下为本文在原文基础上的原创增量(结合第三方分析):

  1. 版本陷阱:2.14.0 仍然漏洞。

修复版本是 2.14.1 / 2.13.3 / 1.123.27——\"我们跑的是 2.14 系列\"并不能证明安全,务必核对具体版本字符串。
2. 公告同时覆盖 XML 节点,但多数文章只提 GSuiteAdmin。

厂商给出的临时缓解 NODES_EXCLUDE=n8n-nodes-base.xml 只禁用了 XML 节点,GSuiteAdmin 仍然可达,因此该缓解只是权宜之计,并未触及根本。
3. 补丁落地后必须重启。

包更新不会自动清除一个已经在运行中进程里被污染的对象原型,重启前污染一直有效。
4. 最小化编辑权限。

在无法立即升级时,把\"创建工作流/修改工作流\"的权限只开放给完全可信的用户。
5. 实例内痕迹排查(第三方给出的思路)。

工作流定义持久化在数据库中,一次尝试会留下痕迹,可针对 workflow_entity 表里 nodes 字段包含 __proto__ / constructor / prototype 的记录做检索(Postgres 与 SQLite 的表名/字段名需自行适配;其中 constructor 命中会较\"吵\",需结合上下文判断)。

参考地址

  1. 原文(Simon Koeck):https://simonkoeck.com/writeups/n8n-gsuiteadmin-prototype-pollution-rce
  2. GitHub Security Advisory GHSA-mxrg-77hm-89hv:https://github.com/advisories/GHSA-mxrg-77hm-89hv
  3. NVD CVE-2026-33696:https://nvd.nist.gov/vuln/detail/CVE-2026-33696
  4. n8n 官方安全公告:https://github.com/n8n-io/n8n/security/advisories/GHSA-mxrg-77hm-89hv
  5. 第三方解读(DEV Community,含 XML 节点/2.14.0/重启/SQL 排查):https://dev.to/secbyjasonmiller/one-proto-away-from-losing-every-credential-in-your-n8n-instance-1m5p
  6. 同文转载(worldprogramming.org):https://www.worldprogramming.org/posts/cve-2026-33696-from-a-schema-name-to-rce-in-n8n-miyg96

参考资料:CVE-2026-33696 / GHSA-mxrg-77hm-89hv 官方公告;NVD 条目;Simon Koeck 原文;社区二次分析。

END

公众号内容都来自国外等平台- 搜索的内容通过结合编写 -

公众号 | AnQuan7 (Ots安全)


iOS27.0.1续航实测出炉:省电是误会,救人是真的" 网络安全文章

iOS27.0.1续航实测出炉:省电是误会,救人是真的"

iOS27.0.1为bug修复包而非省电包,稳定续航与iOS27原版持平。其真正修复了18Pro系列待机异常跳电问题,对无此问题的机型无感知提升。电池健康低于80%的老机型续航弱系硬件老化,换版本无效。iOS27大版本对13/14老机型因调度优化反而小幅更省电。建议按自身机型情况决定是否升级。
评论:0   参与:  0