1. 项目概述一个被低估的自动化工具高危漏洞最近在安全圈里一个关于n8n的漏洞编号CVE-2025-68613引起了我的注意。n8n这个在开发者圈子里越来越火的低代码/无代码工作流自动化工具竟然爆出了一个表达式注入漏洞并且能一路打到远程代码执行。这可不是小事。很多团队包括一些初创公司和内部效率工具团队都喜欢用n8n来串联各种API、处理数据因为它图形化界面友好节点丰富。但大家往往容易忽略这种“拖拉拽”就能构建复杂逻辑的背后其表达式引擎的安全性直接关系到整个系统的命脉。这个漏洞的核心简单来说就是攻击者能够向n8n工作流中某些本应只处理数据的节点注入恶意的JavaScript表达式。由于n8n底层基于Node.js这些表达式在特定上下文沙箱逃逸后中被执行最终可能导致攻击者在服务器上运行任意命令也就是我们常说的RCE。我之所以花时间深入研究它是因为这类漏洞的利用链非常典型从应用层一个看似“功能点”的输入穿透层层逻辑最终抵达系统底层。理解它不仅能帮助我们加固n8n更能举一反三审视其他基于表达式或模板引擎的Node.js应用。对于安全研究人员、DevOps工程师、以及任何在生产环境部署了n8n的团队来说理解CVE-2025-68613的来龙去脉至关重要。这不仅仅是打一个补丁那么简单更是重新审视“低代码平台安全性”的契机。接下来我会带你从漏洞原理、环境搭建、手工复现一路讲到深度利用和立体防御把这条攻击链掰开揉碎了讲清楚。2. 漏洞原理深度拆解表达式注入如何演变为RCE要理解CVE-2025-68613我们不能把它看成一个孤立的点而是一条有逻辑递进关系的攻击链。这条链大致可以分为三个关键阶段入口点的寻找与利用、表达式沙箱的突破、以及最终RCE的达成。我们一层层来看。2.1 n8n表达式引擎与漏洞入口点n8n的强大之处在于其表达式引擎。在几乎每个节点中你都可以看到一个小的“fx”按钮点击后可以输入类似{{ $json.itemId }}或{{ $now.format(YYYY-MM-DD) }}这样的表达式。这些表达式在节点执行时会被动态求值从而允许工作流实现动态逻辑。引擎底层使用了类似vm2或自定义的沙箱环境来隔离这些表达式防止它们访问危险的全局对象如require、process。漏洞的入口就藏在一些接收并处理用户输入且其输出会被后续节点“表达式化”的地方。一个典型的场景是“HTTP Request”节点。假设一个工作流设计是接收用户通过HTTP POST提交的JSON数据然后通过一个“Function”节点或“Set”节点将数据中的某个字段如userInput直接拼接到一个表达式字符串中并交给表达式引擎去执行。例如一个危险的设计可能是// 在Function节点中危险地将用户输入拼接进表达式 const userData $input.first().json.userInput; // 假设用户输入是 }}; console.log(process.mainModule); // const dynamicExpression {{ ${userData} }}; // 然后试图将这个dynamicExpression在另一个上下文中求值如果n8n对userInput的净化不彻底攻击者输入的闭合符}}可以提前结束原有的表达式上下文并注入自己的JavaScript代码。但关键在于早期的n8n版本中某些路径下对这类输入的过滤存在缺陷或者在某些节点配置如“Code”节点的旧版本模式中提供了更直接的代码执行能力为注入打开了窗口。2.2 从表达式注入到沙箱逃逸即使成功注入了JS代码这些代码默认也应在n8n构建的沙箱中运行。n8n使用的沙箱旨在限制对Node.js核心模块和宿主环境的访问。然而沙箱逃逸是这类漏洞的经典环节。攻击者会利用JavaScript原型链污染、沙箱内置对象的方法暴露、或者上下文传递中的缺陷来获取一个“跳出”沙箱的桥梁。在CVE-2025-68613相关的利用链中攻击者可能利用了沙箱环境对外部某些特定函数或对象的引用。例如通过this.constructor.constructor这样的原型链技巧有可能获取到外层“非沙箱”环境的Function构造函数从而构造一个可以执行任意代码的函数。又或者发现沙箱内可以通过某些特殊路径访问到process对象或require函数。一旦逃逸成功攻击者就获得了在Node.js应用主进程上下文执行代码的能力这与直接在应用代码中写eval()几乎无异。注意具体的沙箱逃逸向量Payload会因n8n的具体版本和配置而异。在漏洞公开初期利用代码往往依赖于某个未公开的细节。这也是为什么及时更新到已修复版本至关重要。2.3 实现远程代码执行的关键跳板沙箱逃逸后攻击者执行的代码仍在Node.js的V8引擎进程中。要实现真正的RCE例如执行系统命令ls、whoami或反弹shell就需要调用Node.js的child_process模块。由于逃逸后已处于主进程上下文直接使用require(child_process)成为可能。一个典型的最终Payload结构可能是这样的// 假设这是成功注入并逃逸后执行的代码 const { execSync } require(child_process); const output execSync(id).toString(); // 然后需要将结果输出或外传例如通过另一个HTTP请求发送到攻击者服务器至此从一次看似普通的数据提交到最终在服务器上执行任意命令的完整攻击链路就打通了。其危害等级之所以是“高危”甚至“严重”正是因为这条链路在默认或常见配置下是可实现的且n8n通常作为后端服务部署拥有较高的权限。3. 漏洞复现环境搭建与验证纸上得来终觉浅绝知此事要躬行。要真正理解一个漏洞最好的办法就是亲手在可控环境中复现它。警告以下所有操作仅限在您个人完全控制的、隔离的实验室环境如本地虚拟机或Docker容器中进行严禁对任何非授权系统进行测试。3.1 搭建存在漏洞的n8n环境为了复现我们需要一个包含CVE-2025-68613漏洞的n8n版本。根据漏洞时间线该漏洞影响某个特定版本范围。我们可以使用Docker快速拉起一个易受攻击的n8n实例这是最干净、最方便的方式。首先确保你的实验机安装了Docker和Docker Compose。然后创建一个docker-compose.yml文件。这里的关键是指定一个包含漏洞的n8n镜像标签。例如我们可以使用一个稍旧但稳定的版本注意此处仅为示例实际复现应使用漏洞公告中明确指出的受影响版本version: 3.8 services: n8n: # 使用一个已知在漏洞影响范围内的版本例如 n8nio/n8n:0.240.0 image: n8nio/n8n:0.240.0 container_name: vulnerable-n8n restart: unless-stopped ports: - 5678:5678 environment: - N8N_PROTOCOLhttp - N8N_HOSTlocalhost - N8N_PORT5678 - N8N_EDITOR_BASE_URLhttp://localhost:5678/ - WEBHOOK_URLhttp://localhost:5678/ - GENERIC_TIMEZONEAsia/Shanghai - N8N_ENCRYPTION_KEYsuper-secret-key-change-me-please - DB_TYPEsqlite - DB_SQLITE_DATABASE/home/node/.n8n/database.sqlite volumes: - n8n_data:/home/node/.n8n volumes: n8n_data:使用命令docker-compose up -d启动容器。稍等片刻访问http://localhost:5678你应该能看到n8n的初始化界面按照指引完成管理员账户创建即可。实操心得在实验室环境中我强烈建议将n8n的数据卷n8n_data挂载出来这样即使容器销毁工作流数据也能保留方便反复测试。另外那个N8N_ENCRYPTION_KEY环境变量在生产环境中必须设置为强随机值在这里我们仅作演示。3.2 构造一个易受攻击的示例工作流漏洞的触发往往需要配合一个设计不当的工作流。我们创建一个最简单的PoC工作流在n8n编辑器中创建一个新的工作流。添加一个“Webhook”节点用于接收外部HTTP请求作为触发点。添加一个“Function”节点或旧版的“Code”节点连接到Webhook节点之后。在Function节点中编写有问题的代码。例如我们模拟一个将用户输入直接用于动态表达式求值的场景// 危险示例未经验证和净化直接拼接用户输入 const userInput $input.first().json.payload; // 假设业务逻辑需要将用户输入作为表达式的一部分来求值某个变量 // 这是一种非常危险的模式 const dangerousTemplate {{ User said: ${userInput} }}; // 为了演示我们尝试用某种方式让这个“模板”被求值。 // 在真实漏洞中可能是通过设置一个节点参数该参数支持表达式且值来源于此处。 item.json.vulnerableField dangerousTemplate; return item;再添加一个“Set”节点尝试去读取或使用vulnerableField并配置其某个字段的值为表达式模式且表达式内容来自上一个节点的输出。这个工作流本身可能无法直接触发漏洞但它展示了漏洞产生的模式不可信数据流入了表达式求值上下文。真实的漏洞利用可能需要更精确地控制输入触发特定节点如某些社区节点或旧版核心节点的表达式解析逻辑。3.3 使用公开PoC进行验证在安全研究社区一个漏洞被披露后通常会有研究者发布概念验证代码。对于CVE-2025-68613我们可以在GitHub、Exploit-DB等平台搜索相关PoC。假设我们找到了一个利用脚本exploit_n8n_rce.py。在运行任何PoC前务必阅读代码确保你理解它的每一步在做什么避免它对你的实验环境或其他系统造成意外损害。一个负责任的PoC通常只执行无害命令如id或whoami并将结果回显。在实验环境中运行该PoCpython3 exploit_n8n_rce.py -u http://localhost:5678 -c “id”如果漏洞存在且环境配置正确你可能会在PoC的输出中看到服务器执行id命令后的结果例如uid1000(node) gid1000(node) groups1000(node)。这证实了RCE的可能性。重要警告复现过程中你的n8n实例不应连接任何真实的外部服务如生产数据库、邮件服务器、内部API因为执行的任意命令可能会对这些依赖项造成影响。始终在完全隔离的网络中操作。4. 攻击链路的全手工分析与利用深化仅仅运行一个自动化PoC脚本是不够的。作为安全从业者我们需要手工拆解整个攻击链理解每一个环节这样才能更好地防御和发现类似问题。4.1 手动探测与注入点识别首先我们需要手动寻找可能的注入点。这需要对n8n的工作流设计有基本了解。我们可以从以下几个方面入手寻找接受用户输入的节点Webhook, HTTP Request (用于接收), 表单触发或任何可以配置从外部获取数据的节点。寻找执行动态代码或表达式的节点Function节点、Code节点、以及大量核心节点和社区节点中那些标有“fx”按钮、支持表达式输入的参数框。测试输入净化向这些输入点提交包含表达式语法片段的测试载荷如{{、}}、${、}观察系统的反应。是直接报错、被过滤、还是原样输出到了某个可能被求值的地方例如在Webhook接收的数据中尝试提交{ test: hello {{ 7*7 }} world }然后在工作流的后续节点中仔细观察是否有地方将test字段的值作为表达式求值并得到了49。如果得到了49这就是一个明确的表达式注入信号。4.2 沙箱逃逸载荷的构造与调试如果确认了注入点下一步就是尝试突破沙箱。这需要一些JavaScript和Node.js沙箱机制的知识。我们可以尝试一些经典的沙箱逃逸测试载荷原型链攻击尝试通过this.constructor.constructor(return process)()来获取process对象。查找沙箱暴露的接口在表达式上下文中尝试打印this、Object.getOwnPropertyNames(this)看看沙箱环境提供了哪些对象和方法其中是否有可利用的。利用n8n内置函数或对象研究n8n表达式引擎的帮助文档看是否有内置函数如$if、$min、$get等或对象如$json,$node存在非预期的行为或可以用于访问外部原型。这个过程需要反复试验和调试。我们可以利用Function节点的console.log如果沙箱允许输出到n8n的执行日志来调试我们的Payload或者将尝试的结果赋值给一个变量并在工作流末尾输出以观察每一步的执行结果。4.3 实现稳定RCE的多种路径成功逃逸沙箱后实现RCE也有多种路径直接使用child_process这是最直接的方式前提是能成功require。const { exec } require(child_process); exec(touch /tmp/pwned, (error, stdout, stderr) {});利用Node.js的VM模块如果主进程上下文可用且require(vm)可行有时可以通过VM模块创建另一个不那么受限的上下文来执行代码。写入恶意模块并加载如果可以写文件比如通过某些方式将Payload写入/tmp/evil.js然后通过require(/tmp/evil.js)来加载执行。利用已有模块的功能有时直接执行命令会被监控可以尝试利用已加载模块的现有功能来实现恶意操作例如使用fs模块读写敏感文件。在手工测试时应从无害命令开始如echo test、whoami、id确认通道畅通后再谨慎测试其他操作。同时要思考如何将命令执行的结果回传例如通过DNS外带、HTTP请求将结果发送到攻击者控制的服务器或者直接写入一个可通过Web访问的静态文件。5. 影响范围与严重性评估CVE-2025-68613不是一个可以轻描淡写的小问题。我们来系统评估一下它的影响。直接影响权限提升攻击者从普通工作流调用者或未授权访问者提升至在n8n服务所在服务器上执行任意命令的权限。n8n通常以node用户或某个特定服务账户运行这意味着攻击者至少拥有该账户的所有权限。数据泄露可以读取n8n的数据库包含所有工作流配置、存储的凭证、处理过的数据、服务器上的环境变量、配置文件以及其他应用数据。横向移动以n8n服务器为跳板攻击内网其他服务。如果n8n容器或主机配置不当甚至可能获取更高权限。持久化可以在服务器上安装后门、创建计划任务、写入SSH密钥等实现持久化访问。资源滥用利用服务器进行挖矿、发起DDoS攻击等。受影响版本根据漏洞公告该漏洞影响n8n的某个特定版本范围。所有在此范围内的部署无论是Docker、npm直接安装、还是云服务自托管版本均受影响。关键点在于许多用户可能并不频繁更新其n8n实例尤其是那些作为内部关键业务自动化组件的部署它们可能长期运行在一个旧版本上。部署模式加剧风险暴露在公网为了方便远程配置或触发很多用户将n8n的管理界面或Webhook监听端口直接暴露在互联网上仅依赖弱密码或默认密码保护这大大降低了攻击门槛。高权限运行为了能够调用各种系统命令或访问特定目录n8n有时会被赋予较高的运行权限这放大了漏洞成功利用后的破坏力。作为集成中枢n8n的核心价值是集成因此它通常保存了大量第三方服务的API密钥、数据库凭证、企业内部系统账号等敏感信息。一旦被攻破损失远不止一台服务器。6. 立体化防御体系构建亡羊补牢为时未晚。针对CVE-2025-68613这类漏洞我们不能只依赖官方补丁必须建立一个从外到内、从代码到运维的立体化防御体系。6.1 紧急缓解与官方补丁应用这是最直接、最有效的第一步。立即升级前往n8n官方GitHub仓库或公告确认修复CVE-2025-68613的安全版本号例如 n8n0.2xx.x。立即将你的n8n实例升级到该版本或更高版本。Docker用户更新docker-compose.yml中的镜像标签然后执行docker-compose pull docker-compose up -d。npm用户运行npm update n8n -g或在你项目目录下更新。临时缓解如果因故无法立即升级考虑以下措施网络隔离立即将n8n实例从公网撤下确保只能通过VPN或内部网络访问。强化认证启用并强制使用多因素认证检查并删除任何默认或弱密码账户。审查工作流临时禁用所有非必要、尤其是包含复杂Function/Code节点或接收外部输入的工作流。6.2 安全配置加固指南打补丁是修复漏洞加固配置是降低被攻击面。最小权限原则绝不以root身份运行n8n。在Docker中使用user指令指定非root用户。在宿主机上严格限制n8n数据卷和配置文件的访问权限。如果n8n需要调用系统命令考虑通过一个具有严格权限限制的代理服务或API来进行而不是直接赋予n8n高权限。网络层面隔离使用反向代理如Nginx前置n8n并配置严格的访问控制列表仅允许可信IP段访问管理界面。将Webhook触发端点与管理界面在不同端口或路径上隔离并对Webhook实施签名验证如果支持。在防火墙规则中限制n8n容器或主机对外的出站连接仅允许访问其必需集成的服务。凭证与秘密管理切勿将API密钥、密码等硬编码在工作流中。务必使用n8n的“凭证”功能它提供加密存储。定期轮转这些凭证。考虑使用外部的密钥管理服务来提供动态凭证。审计与日志确保n8n的日志级别足够记录详细的操作和错误信息。将日志集中收集到SIEM系统并设置告警规则例如针对大量失败的登录尝试、异常的工作流执行模式、或Function节点中执行了可疑字符串。6.3 安全开发与运维最佳实践从源头和流程上杜绝风险。工作流设计安全输入验证与净化在任何用户输入被用于表达式、代码或系统命令之前必须进行严格的验证和净化。对于表达式上下文要过滤或转义{{、}}、$等特殊字符。避免动态代码执行除非绝对必要否则避免在Function节点中使用eval()或new Function()。优先使用n8n内置节点和表达式函数来完成逻辑。代码审查对团队内创建的复杂工作流特别是包含自定义代码的应建立同行审查机制。依赖与版本管理订阅n8n的安全公告如GitHub Security Advisories。建立定期更新机制不要长期运行过旧版本。同样关注n8n所依赖的Node.js版本的安全更新。纵深防御在主机层面部署HIDS监控异常进程创建和文件操作。考虑在容器环境使用Seccomp、AppArmor等安全配置文件限制容器的能力。对n8n实例进行定期的渗透测试和安全评估模拟攻击者寻找新的弱点。7. 排查、检测与应急响应即使做好了防御也需要有发现入侵和快速响应的能力。7.1 我是否已经遭受攻击排查清单如果你的n8n实例尚未升级或者怀疑已被入侵请立即检查以下迹象日志中的异常条目检查n8n应用日志寻找包含child_process、spawn、exec、eval、Function等关键词的异常执行记录。查看是否有来自异常IP地址的大量请求尤其是对特定工作流或Webhook的访问。关注执行失败但参数可疑的日志。系统层面的异常检查服务器上是否出现了非预期的进程、计划任务crontab或服务。检查/tmp、/dev/shm等目录是否有可疑的可执行文件或脚本。使用netstat或ss命令查看是否有未知的外连连接。n8n内部的异常审计工作流列表是否有你不认识的、新创建的、或被修改过的工作流特别关注那些包含Function/Code节点的工作流。检查“凭证”列表是否有凭证被异常使用或更新查看执行历史是否有大量失败或耗时异常的执行记录7.2 入侵发生后的应急响应步骤如果确认被入侵保持冷静按步骤处理立即隔离断开受影响服务器或容器的网络连接防止攻击者持续控制或横向移动。取证备份在关机前尽可能完整地备份系统日志、n8n的数据库文件、整个应用目录。这用于后续分析和法律证据。评估影响根据排查结果初步判断数据泄露范围哪些凭证、哪些工作流数据被访问。清除后门基于取证分析找到攻击者植入的持久化后门恶意文件、计划任务、账户等并清除。对于生产系统更安全的做法是直接废弃当前环境从干净的备份或镜像重建。恢复与加固使用干净的镜像和已修复的n8n版本重建服务。从可靠的备份中恢复工作流数据注意确认备份未被污染。强制重置所有存储在n8n内的凭证因为它们可能已泄露。实施前面章节提到的所有加固措施。复盘与通知分析攻击根本原因完善安全流程。如果涉及用户数据泄露需根据相关法律法规进行通知。7.3 长期监控策略建立主动监控防患于未然工作流变更监控通过n8n的API或日志监控工作流的创建、更新和删除操作并设置告警。异常表达式检测可以在反向代理层或通过自定义中间件对传入n8n的请求体进行轻量级扫描匹配表达式注入的常见模式。资源使用监控监控n8n进程的CPU和内存使用情况突然的异常飙升可能意味着正在执行挖矿等恶意代码。定期安全扫描使用漏洞扫描工具定期扫描你的n8n实例和其宿主机环境。CVE-2025-68613给我们敲响了一记警钟即使是像n8n这样优秀的、旨在提升效率的工具如果使用不当或维护疏忽也会成为安全链上最脆弱的一环。安全不是一个功能而是一个贯穿设计、开发、部署、运维全生命周期的过程。对于每一位n8n的使用者和运维者来说理解其运行原理遵循安全最佳实践保持组件的更新是享受其便利性时必须承担的职责。