0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LiteLLMのSQLiとRCEチェーン、開示36時間後の実撃を検証した

0
Posted at

はじめに

複数のLLMプロバイダーを単一のOpenAI互換APIで束ねるAIゲートウェイ「LiteLLM」で、2026年4月から6月にかけて立て続けに2件の重大脆弱性が公表されました。

  • CVE-2026-42208(SQLインジェクション・CVSS 9.3)
  • CVE-2026-42271(MCP経由コマンドインジェクション・CVSS v4.0で8.7、他の脆弱性とチェーンするとCVSS 10.0相当)

どちらも「認証をすり抜けてデータベースやホストOSに直接触れる」タイプの脆弱性で、CVE-2026-42208は GitHub Security Advisoryの公開から約36時間後には実際の悪用が観測1されています。LiteLLMは本プロジェクトでも過去に2回取り上げた実績のあるOSSで、Qiita記事化の対象としても運用実態としても無視できないため、今回は一次情報(LiteLLM公式ブログ・GitHub Security Advisory・PoCリポジトリ)を確認しながら、脆弱性の中身と自分の環境が対象かどうかを確認するチェックスクリプトまでまとめます。

この記事で学べること

  • CVE-2026-42208(SQLi)とCVE-2026-42271(MCPコマンドインジェクション)の技術的な原因
  • 2つの脆弱性とStarletteの別CVEをチェーンすると何が起きるか
  • 自分のLiteLLM環境が対象バージョンかを確認するセルフチェックスクリプト
  • 実際にPoCの攻撃リクエストを読み解いて分かったこと(著者視点)

TL;DR

項目 CVE-2026-42208 CVE-2026-42271
種別 SQLインジェクション(事前認証) MCPプレビューエンドポイント経由コマンドインジェクション
CVSS 9.3 8.7(他CVEとチェーンで10.0相当)
影響バージョン v1.81.16 〜 v1.83.6 v1.74.2 〜 v1.83.6
修正バージョン v1.83.7以降 v1.83.7以降
悪用状況 開示から約36時間後に実攻撃を観測 CISA Known Exploited Vulnerabilitiesカタログに追加

対処は共通で v1.83.10-stable以降へのアップグレード(最新安定版)。緊急パッチが当てられない場合の暫定回避策も後述します。


CVE-2026-42208: Authorizationヘッダー1つでSQLiが刺さる

原因

LiteLLM Proxyは、APIキーの検証時にリクエストの Authorization: Bearer <key> ヘッダーの値を使ってPostgreSQLの LiteLLM_VerificationToken テーブルに対する SELECT クエリを組み立てます2。このとき、キー値を パラメータバインドせず文字列結合でクエリに埋め込んでいた のが根本原因です。

# 概念的なイメージ(実際の実装はLiteLLM側の内部コード)
query = f"SELECT * FROM LiteLLM_VerificationToken WHERE token = '{bearer_value}'"

シングルクオート1つでSQL文字列リテラルから脱出でき、以降は任意のSQL(UNION SELECT 等)を注入できます。攻撃者は認証済みである必要がなく、POST /chat/completions のような通常のLLM APIルートにこの細工したヘッダーを送るだけで到達可能でした3

悪用のタイムライン

Sysdig Threat Research Teamの観測によれば、GitHub Security Advisory(GHSA-r75f-5x8p-qvmc)の公開から 最初の悪用が確認されるまでの時間はおよそ36時間 でした1。攻撃者は無差別スキャンではなく、APIキー・プロバイダー認証情報・環境変数設定を保持する3テーブルを狙い撃ちにした「意図的で調整済み」の列挙を行っていたと報告されています。

対策

  • v1.83.7 以降へアップグレード(最新は v1.83.10-stable
  • 緊急パッチが当てられない場合、LiteLLMメンテナーは general_settings.disable_error_logs: true の設定を暫定回避策として案内しています3

CVE-2026-42271: MCPプレビューエンドポイントの野放しコマンド実行

原因

LiteLLMには、MCPサーバーを保存する前に接続テストする2つのプレビュー用エンドポイントがあります。

  • POST /mcp-rest/test/connection
  • POST /mcp-rest/test/tools/list

これらは呼び出し元から transport / command / args / env を含む MCPサーバー設定をそのまま受け取り、実際に起動を試みる 実装になっていました4。つまり、有効なLiteLLM APIキーさえあれば(権限レベルは問わない)、transport: "stdio" と任意のOSコマンドを指定するだけで、LiteLLMのホスト上で そのコマンドがそのまま実行される 状態でした。

公開されているPoC4では、次のようなリクエストで任意コマンドが実行できることが示されています。

curl -X POST -H "Authorization: Bearer sk-litellm-master-key" \
  http://localhost:4000/mcp-rest/test/tools/list \
  -d '{"transport":"stdio","command":"bash","args":["-c","id > /tmp/pwned"]}'

Starletteの別CVEとチェーンすると認証ごと消える

Horizon3.aiの調査では、CVE-2026-42271を Starlette 1.0.0以下の"BadHost"ホストヘッダー検証バイパス脆弱性(CVE-2026-48710) と組み合わせることで、LiteLLMの認証メカニズム自体を回避できることが示されました5。この場合、APIキーすら不要な 資格情報なしのリモートコード実行 に到達し、単体では8.7だったCVSSスコアはチェーン後に10.0(Critical)相当に引き上げられています。

対策

  • LiteLLM v1.83.7 以降 かつ Starlette 1.0.1 以降の両方にアップグレードする(片方だけでは不十分)
  • CISAは本脆弱性を「Known Exploited Vulnerabilities」カタログに追加しており、実際の悪用証拠が確認されています5

セルフチェックスクリプト

自分の環境が対象かどうかを、インストール済みバージョンと設定ファイルから機械的に確認するスクリプトを書きました。

#!/usr/bin/env python3
"""LiteLLM CVE-2026-42208 / CVE-2026-42271 セルフチェック"""
import importlib.metadata
import sys

# 下限は各CVEの影響開始バージョン、上限はどちらも共通の修正バージョン
VULNERABLE_RANGES = {
    "CVE-2026-42208 (SQLi, CVSS 9.3)": (1, 81, 16),
    "CVE-2026-42271 (MCP RCE, CVSS v4.0 8.7)": (1, 74, 2),
}
FIXED_VERSION = (1, 83, 7)


def parse_version(v: str) -> tuple:
    parts = (v.split("-")[0].split(".") + ["0", "0", "0"])[:3]
    return tuple(int(p) for p in parts)


def main() -> int:
    try:
        installed = importlib.metadata.version("litellm")
    except importlib.metadata.PackageNotFoundError:
        print("litellm がインストールされていません(対象外)")
        return 0

    current = parse_version(installed)
    print(f"インストール済みバージョン: {installed}")

    if current >= FIXED_VERSION:
        print("✅ 修正済みバージョン以降です(v1.83.7+)。")
        return 0

    hit = [name for name, lower in VULNERABLE_RANGES.items() if current >= lower]
    if hit:
        for name in hit:
            print(f"⚠️  {name} の影響範囲内です。")
    else:
        print("⚠️  各CVEの影響開始バージョンより古い可能性がありますが、v1.83.7未満のため念のため確認してください。")
    print("   → v1.83.10-stable 以降(本記事執筆時点の最新安定版)へのアップグレードを推奨します。")
    return 1


if __name__ == "__main__":
    sys.exit(main())
python3 litellm_cve_check.py

自分の手元の検証環境(litellm==1.83.10)で実行すると ✅ 修正済みバージョン以降です が返り、意図的に古いバージョンをpipでダウングレードして再実行すると警告が出ることを確認しました。MCPプレビューエンドポイントの設定有無は general_settings の設定ファイルを直接grepするのが早く、mcp-rest という文字列がconfig内に存在するかどうかも併せて確認すると安心です。

grep -n "mcp" config.yaml 2>/dev/null && echo "MCP機能有効。エンドポイント無効化オプションの確認を推奨"

著者視点の発見ポイント

PoCリポジトリの攻撃リクエストを実際に読んでいて印象的だったのは、CVE-2026-42271の攻撃ペイロードがLiteLLMの正規機能(MCPサーバーの接続テスト)をそのまま悪用している 点です。新しいバイナリを送り込む必要も、既知の脆弱なライブラリを踏む必要もなく、「接続テストのためにMCPサーバー設定を受け取って起動を試みる」という設計そのものが攻撃者にとってのコマンド実行APIになっていました。

これは「入力値検証の漏れ」ではなく「機能そのものが攻撃者向けAPIとして成立してしまう」設計レベルの問題で、SQLiのような典型的な実装ミスとは別種の警戒が必要だと感じました。MCPサーバーを動的に追加・テストできる機能を持つゲートウェイ/プロキシ実装は今後も増えていくはずなので、「接続テスト用エンドポイントに認証済みユーザーの入力をどこまで信用するか」は、他のMCP対応プロダクトを評価する際にも意識しておきたいポイントです。


まとめ

LiteLLMのようなAIゲートウェイは、APIキー・プロバイダー認証情報・場合によってはホストOSへのアクセス経路を一手に引き受ける立場にあるため、脆弱性の影響範囲が単なる「情報漏洩」を超えやすい構造になっています。今回の2件は幸い両方とも v1.83.7 以降で修正済みですが、CVE-2026-42208が開示から36時間ほどで実攻撃を受けた事実は、パッチ適用の速度そのものがセキュリティ対策として機能する時代であることを示しています。手元のバージョンが対象範囲に入っていないか、上記のセルフチェックスクリプトで一度確認してみてください。

  1. CVE-2026-42208: Targeted SQL injection against LiteLLM's authentication path discovered 36 hours following vulnerability disclosure - Sysdig 2

  2. Security Update: CVE-2026-42208 in LiteLLM Proxy - LiteLLM公式ブログ

  3. LiteLLM CVE-2026-42208 SQL Injection Exploited within 36 Hours of Disclosure - The Hacker News 2

  4. CVE-2026-42271-PoC - GitHub 2

  5. LiteLLM Flaw CVE-2026-42271 Exploited in the Wild, Chains to Unauthenticated RCE - The Hacker News 2

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?