title: "LiteLLMのCVE-2026-35029に備える:版数とマスターキーを点検するスクリプト"
tags:
- LiteLLM
- AI
- Python
- セキュリティ
- LLM
private: false
---
TL;DR
- LiteLLM Proxyの
/config/updateに認可の不備があり、読み取り専用アカウントから管理者の乗っ取りまで到達できた(CVE-2026-35029、修正版1.83.0、CVSS v4.0で8.7)(※1)。 - 本稿は手順中心の実践編である。自分の環境のLiteLLMが修正版かどうか、マスターキーが既定値のままでないかを点検する小さなスクリプトを作る。
- スクリプトの動作は、版数とサンプルのYAMLで確認した(Python 3.13、2026-11-04)。脆弱な環境での攻撃の再現は行っていない。
- 背景と攻撃の流れは、同日のZenn記事に書いた。本稿はTips寄りで、重複を避けている。
1. 先に確認すること:3つの質問
次の3つに答えられなければ、まず確認してほしい。
- 運用中のLiteLLMのバージョンは1.83.0以上か。(GHSAは1.82.6までを影響範囲として列挙している(※1))
- マスターキーは設定されていて、
sk-1234のような例示値ではないか。(Zenityによれば、認証はマスターキーが設定されている場合にだけ強制され、観測された攻撃は既定のキーや未設定の環境を狙っていた(※2)) - 管理画面と
/config/update、/get_imageが、インターネットから到達できる状態になっていないか。
1と2は、スクリプトで確認できる。3は、ネットワーク側の確認が必要である。
2. 点検スクリプト
インストール済みのLiteLLMの版数を読み、修正版(1.83.0)と比べる。インストールされていない環境(別サーバーの設定を点検する場合など)では、版数を引数で渡せる。設定ファイルを渡すと、master_keyの値も見る。
# ll_check.py(Python 3.13、標準ライブラリのみ)
import re, sys, os
from importlib import metadata
FIXED = (1, 83, 0)
WEAK_KEYS = {"sk-1234", "sk-12345", "changeme", "password", ""}
def parse(v):
return tuple(int(x) for x in re.findall(r"\d+", v)[:3])
def check_version(v):
return "OK" if parse(v) >= FIXED else "VULNERABLE"
def check_config(text):
findings = []
m = re.search(r"master_key\s*:\s*['\"]?([^'\"\n#]*)", text)
if not m:
findings.append("master_key の記載なし(環境変数 LITELLM_MASTER_KEY で設定している場合は別途確認)")
else:
val = m.group(1).strip()
if val.startswith("os.environ/"):
findings.append(f"master_key は環境変数参照: {val}")
elif val in WEAK_KEYS:
findings.append(f"弱い/既定のmaster_key: '{val}' → 直ちに変更")
return findings
if __name__ == "__main__":
try:
ver = metadata.version("litellm")
except metadata.PackageNotFoundError:
ver = sys.argv[1] if len(sys.argv) > 1 else None
if ver:
print(f"litellm {ver}: {check_version(ver)} (修正版は 1.83.0 以降)")
else:
print("litellm 未インストール。バージョンを引数で渡してください")
if len(sys.argv) > 2:
for f in check_config(open(sys.argv[2], encoding='utf-8').read()):
print("-", f)
使い方は次のとおり。
# インストール済みの環境で
python3 ll_check.py
# 版数を手で渡し、設定ファイルも点検する
python3 ll_check.py 1.80.0 config.yaml
3. 動作確認の結果
版数の判定は、境界の前後で確かめた(2026-11-04、筆者環境)。
$ for v in 1.74.0 1.82.6 1.83.0 1.95.1; do python3 ll_check.py $v; done
litellm 1.74.0: VULNERABLE (修正版は 1.83.0 以降)
litellm 1.82.6: VULNERABLE (修正版は 1.83.0 以降)
litellm 1.83.0: OK (修正版は 1.83.0 以降)
litellm 1.95.1: OK (修正版は 1.83.0 以降)
設定ファイルの点検は、次の2つで確かめた。
$ cat c1.yaml
general_settings:
master_key: sk-1234
$ python3 ll_check.py 1.80.0 c1.yaml
litellm 1.80.0: VULNERABLE (修正版は 1.83.0 以降)
- 弱い/既定のmaster_key: 'sk-1234' → 直ちに変更
$ python3 ll_check.py 1.90.0 c2.yaml # master_key: os.environ/LITELLM_MASTER_KEY
litellm 1.90.0: OK (修正版は 1.83.0 以降)
- master_key は環境変数参照: os.environ/LITELLM_MASTER_KEY
環境変数を参照している場合は、参照先の値までは見ていない。環境変数の中身がsk-1234でないかは、別途確認が必要である。
4. 点検後の対処
4.1 版数がVULNERABLEだったとき
- 1.83.0以降へ更新する。GHSAによれば、設定レベルの回避策はなく、更新が唯一の根本対策である(※1)。
- 更新前に、脆弱な版を公開していた期間を確認する。
- その期間にインターネットから到達できた場合は、侵害を疑い、次の4.3に進む。
4.2 マスターキーが既定値だったとき
例示値のままでは、攻撃者がそのキーで管理APIを呼べる。次の手順で変更する。
- 十分に長いランダムな値を生成する。
- 環境変数(または秘密管理サービス)から読ませる。設定ファイルに平文で書かない。
- 変更後、古いキーでの接続が拒否されることを確認する。
python3 -c "import secrets; print('sk-' + secrets.token_urlsafe(32))"
上のコマンドは、Pythonの標準ライブラリで、推測されにくい値を生成する例である。
4.3 侵害が疑われるとき
Zenityの記述では、攻撃の連鎖でデータベースの接続文字列、プロバイダーのAPIキー、Langfuseのキーなどが読み取れる(※2)。次の順で進める。
| 順序 | 対処 | 補足 |
|---|---|---|
| 1 | 外部公開を止める | 管理画面と/config/update、/get_imageを遮断 |
| 2 | ログを確認する |
/config/updateの呼び出し、UI_LOGO_PATHの変更、不審なパススルーエンドポイント |
| 3 | 秘密情報をローテーションする | DB接続、各プロバイダーのAPIキー、Langfuseのキー、マスターキー |
| 4 | プロバイダー側の利用量を見る | 急な増加は、鍵の不正利用の兆候 |
| 5 | 管理者アカウントを確認する |
UI_USERNAMEとUI_PASSWORDが書き換えられていないか |
5. ハマりやすい点
-
版数が取れない。
pip showで見つからない環境(コンテナの外で動かしている場合など)では、引数で版数を渡す。コンテナのイメージのタグも確認する。 -
latestタグの落とし穴。 イメージのタグが固定されていないと、再起動のたびに版数が変わりうる。点検時の版数を記録しておく。 -
環境変数の参照。
os.environ/…の形式は、スクリプトでは中身を見ていない。実際の値を別途確認する。 - 版数の情報源が混ざる。 第三者のサイトには、別の版数(1.95.0)やCVSS(8.1)で書かれた勧告も載っていた(※3)。筆者は一次情報で確認できなかったため、本稿ではCVE-2026-35029の1.83.0を基準にした。より安全側の版数に更新して損はない。
6. 筆者の評価
点検スクリプトは、判断を単純にしてくれる。版数とキーの2点は、数秒で機械的にわかる。一方で、これだけで「安全」とは言えない。ネットワークの公開範囲や、過去の侵害の有無は、人が確かめるしかない。
そして、このスクリプトは攻撃を再現していない。判定の根拠は、GHSAに書かれた版数の範囲だけである。Zenityは1.81.0では回避が403で拒否されたとも書いており、脆弱な版の境界は資料によって揺れている。安全側に倒して、1.83.0より前はすべて対象としている。
まとめ
LiteLLMのCVE-2026-35029に備え、版数とマスターキーを点検する小さなスクリプトを作り、境界値とサンプル設定で動作を確かめた。脆弱な環境での攻撃の再現は行っていない。1.83.0以降への更新、マスターキーの見直し、管理APIの公開範囲の限定、そして侵害が疑われる場合の鍵のローテーション。この4点を、今日のうちに確認したい。
※本記事の記載内容は筆者個人の見解であり、所属する会社や組織の立場や方針とは一切関係ありません。
筆者情報
【Tomcat-zhou】|AI/AX/AIセキュリティ専門家
AI戦略洞察、AI駆動開発の方法論設計と現場で回る仕組みづくりをしています。
noteで戦略洞察 / Zenn・Qiitaで技術・方法論を書いています。
AIセキュリティ・AIコンサル・AIエージェント開発・AI活用・AI駆動開発教育、AI人材育成サービスを一体で提供。
X: @ZY20240528
LinkedIn: Tomcat-zhou
Facebook: zhou yi
note: Tomcat-zhou
Zenn: Tomcat-zhou
参考資料
- GHSA-53mr-6c8q-9789 / CVE-2026-35029(OSV)
- A Read-Only Account is All it Takes to Own a LiteLLM Server(Zenity Labs)
- AI Changelog Week 44(neomanex)
タグ
LiteLLM AI Python セキュリティ LLM