- 作者: 纯情
- 时间:
- 分类: 开源
Dify 沙箱逃逸漏洞 RCE 分析
前言
社区上还没有人分析过Dify的沙箱逃逸,n8n的分析了很多,今天来分析分析dify的,而且阿里云漏洞库给到了9.8的评分
漏洞描述
Dify是一款开源的LLM应用开发平台。在Dify的 代码节点 功能中,允许用户执行自定义的 JavaScript 代码。该漏洞是由于 Dify 沙箱(dify-sandbox)在执行用户提供的 JavaScript 代码时,未能在安全限制生效之前对用户代码进行充分的隔离。攻击者可以通过在 JavaScript 代码中重写全局函数(例如 parseInt),在沙箱安全限制(如 seccomp)生效之前执行任意系统命令,从而实现沙箱逃逸并获得宿主机 root 权限,进而获取敏感信息(如 API 密钥、内部服务器地址、K8s token)。
环境搭建
沙盒版本低于 0.2.11 就好了
在 docker 文件里面修改一下,这个不需要修改整个 dify
复现起来比较方便
只重启 sandbox
漏洞复现
POC
写一个代码执行的工作流
节点加入逃逸代码
成功
漏洞分析
根据 Huntr 披露的细节
漏洞的根源在于 Go 语言编写的 Runner (nodejs.go) 如何处理用户代码。它没有使用安全的沙箱上下文(如 vm.runInContext),而是采用了简单的字符串拼接
prescript.js
这样的顺序有什么问题呢?梳理清楚执行顺序
问题 1:parseInt 是 JavaScript 全局函数,可以被用户代码覆盖
问题 2:parseInt 在 difySeccomp 之前被调用
问题 3:用户代码拼接在 prescript.js 之后,会在 parseInt 调用前执行
nodejs.go
可以看到完全没有隔离,我记得当时有一个 jsp 的 webshell 就是利用这个原理免杀的
最终生成的 JS 脚本
当 Go 程序完成拼接后,Node.js 进程实际接收到的 final_script 长这样
在 JavaScript 中,函数声明具有最高的提升优先级,也就是在代码执行任何一行之前,JS 引擎会先扫描整个作用域,将所有 function xxx() {} 提升到内存顶部
所以在编译阶段,Node.js 解析整个拼接后的脚本,发现了用户代码中自定义的函数,将全局作用域中的 parseInt 引用指向用户的恶意函数,覆盖了原生的 parseInt
导致了漏洞
Payload 解析
JavaScript 函数声明提升机制
在 JavaScript 中,**函数声明(Function Declaration)**具有最高的提升优先级:
JavaScript 引擎在执行代码前,会先扫描整个作用域,将所有 function xxx() {} 声明提升到内存顶部
执行流程
总结
其实总的下来,就是一个这样的顺序,而这个漏洞最重要的就是执行顺序
漏洞修复
https://github.com/langgenius/dify-sandbox/commit/a0c74d335e380d0a51595cdc0ad975a064a6127b
漏洞的本质就是执行的顺序,安全边界 Seccomp 限制优先执行就好了
调整代码执行顺序:确保 difySeccomp(安全限制)在用户代码执行前调用,彻底避免沙箱生效前的 “窗口期”。例如,修改 prescript.js 与用户代码的拼接逻辑,让 difySeccomp 优先执行:
参考
https://athink.cn/securitynews/detail/?id=687dd9dd10b54e7878ef6d1b
https://huntr.com/bounties/f8dc17a3-5536-4944-a680-24070903cd2d
标签: NODE.JS, JavaScript, 安全漏洞, 代码执行, RCE, Dify, 沙箱逃逸