LiteLLM 1.107.0 汚染事件の真相:AIアシスタントはいかにしてリリース権限をログに書き出したのか

LiteLLM 1.107.0 汚染事件の真相:AIアシスタントはいかにしてリリース権限をログに書き出したのか

サプライチェーンセキュリティLiteLLMPyPIAIインフラ認証情報流出

データソース:HN + web research

[tool.maturin]
data = "cook"

2026年10月21日 11:39:51 UTC、オープンソースの大規模言語モデル(LLM)ゲートウェイである LiteLLM が 1.107.0 正式版を PyPI にプッシュした。ソースツリーの pyproject.toml に追加されたのは、わずかこの1行の設定のみだった。しかしその後の2時間で、バージョンを固定していなかったすべてのデプロイ環境が、認証情報の収穫拠点へと変貌させられた。AIインフラにおける信頼の境界線は、「依存関係のソースコード監査」から「ビルド用クレデンシャルの死守」へと一気に崩壊した。そして今回、攻撃者にリリースのマスターキーを差し出したのは、皮肉にも開発者が日常的に愛用していたAIコーディングアシスタントだった。

わずか12バイトのフックがインタープリタを掌握、インポート不要で発火

PyPI 上で litellm==1.107.0 が公開されていたのは 13:23 UTC までの約2時間。Linux、macOS、Windows 向けの全7種類の wheel パッケージすべてに悪意あるペイロードが仕込まれていた。同時に Docker Hub および GHCR 上の v1.107.0、latest、main-latest、main-stable の4つのタグも改ざんされた。11:46:56 から 14:20 UTC にかけて、これらのタグは一様に汚染イメージ sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e を指していた。バージョンを固定していなかった CI/CD パイプラインは、更新を取得した瞬間に本番環境へ直接バックドアを招き入れることとなった。

悪意あるコードは Maturin の data = "cook" 指定によって wheel の purelib/ ディレクトリに混入された。パッケージがインストールされると、site-packages 直下に3つのファイルが展開される。12バイトの cook.pth、22,252バイトの cook.py、そして 25,509バイトの sitecustomize.py である。公開されている BerriAI/litellm リポジトリには cook/ というディレクトリは存在せず、ソース配布物(sdist)にも該当ファイルは含まれていなかった。GitHub の公開コミットログだけをチェックする従来のセキュリティ監査は、ビルド時注入の前では完全に無力だった。

cook.pth の中身は import cook というたった1行である。Python の .pth メカニズムにより、インタープリタ起動時に import で始まる文が自動的に実行される。同ディレクトリにある sitecustomize.py も起動時に自動評価されるグローバルフックであり、cook.py 内の圧縮ペイロードとともに二重の保険を形成していた。攻撃ペイロードはアプリケーション側のコード読み込みルートを完全に迂回しており、ホスト環境で import litellm を一度も実行しなくても、pip を走らせたり IDE の言語サーバーを起動したりするだけで即座にバックドアが作動する。

LiteLLM 1.107.0 悪意あるパッケージのファイル注入と二重ペイロード実行フロー 図:LiteLLM 1.107.0 悪意あるパッケージのファイル注入と二段階ペイロードの実行経路。出典:SafeDep(サプライチェーンセキュリティ分析レポート)

AIツールチェーンとゲートウェイDBを狙い撃ちにする16の収集モジュール

第1段階のペイロード cook.py には16個の収集モジュールが組み込まれており、標的マシンの隅々まで探索し尽くした。まず ~/.ssh/* 秘密鍵やシェル履歴を読み出し、Chromium や Firefox に保存されたパスワードを DPAPI やマスターキーで復号する。続けて AWS、Azure、GCP、~/.kube/config、Docker、gh、Vault、Terraform などの認証設定ファイルを総ざらいした。さらに AWS IMDS メタデータエンドポイント(IMDSv2 を試行後 IMDSv1 へフォールバック)を叩き、稼働中プロセスの /proc/<pid>/environ を走査して環境変数を吸い上げた。現代のクラウドネイティブエンジニアの開発環境に完璧に合致するよう仕立て上げられたペイロードだった。

攻撃者の狙いを如実に示していたのが、ai_devtools と db_litellm という2つの専用モジュールである。ai_devtools は Claude Code、Claude Desktop、Cursor、Windsurf、Copilot、VS Code の mcp.json、Gemini CLI、Codex、Continue、Aider、Zed、Hugging Face など、17種類に及ぶ AI 開発ツールの認証情報を狙い撃ちにした。一方の db_litellm は DATABASE_URL を読み取り、ゲートウェイデータベースの LiteLLM_CredentialsTable と LiteLLM_ProxyModelTable を丸ごとダンプした。AIゲートウェイは企業における LLM 利用の認証ハブそのものである。1台の LiteLLM プロキシを制圧することは、背後に接続されたすべての上流モデル API キーを一挙に入手することを意味する。

窃取されたデータはランダムな AES-256-CBC 鍵で暗号化された後、内蔵の RSA-4096 公開鍵で OAEP カプセル化された。データは tar.gz にまとめられ、X-Token: XWiA8gZNa7NcX2XRoAEb ヘッダーを伴って www.litellm.dev/healthcheck へ送信された。第2段階のペイロードである sitecustomize.py は /tmp/remote_code_loader.lock をロックし、fork() と setsid() によって常駐デーモンへと移行する。3,600秒ごとに GitHub リポジトリ letters9813/pythonclient-test の domain.txt を取得して情報窃取を再実行し、リモートの /codepy スクリプトをダウンロードして exec() で直に実行した。GitHub は 13:40 UTC 頃に letters9813 アカウントを停止したが、それ以前にネットワークへ接続されていた端末では既に第2段階コードが走っていた可能性が高い。本物そっくりのドメイン litellm.dev も相まって、通常のトラフィック監視でこの異常を2時間以内に察知するのは困難だった。

3つの特権キーが承認関門を突破、強化されたパイプラインが毒入りパッケージに署名

今年3月のセキュリティインシデントを受け、LiteLLM チームはリリース基盤の大規模なセキュリティ強化を実施したばかりだった。ビルドワークフローをプライベートリポジトリへ移し、Trusted GitHub Publishers を導入し、Docker タグのイミュータブル化を行い、コンテナ署名を義務付けていた。論理的には、外部の第三者がリリーススクリプトを改ざんすることも、公式署名を偽造することも不可能な設計だった。

しかし10月21日のログが明らかにしたのは、正当な鍵を握った者による内部突破だった。10:08:29 UTC、ブランチ保護ルールが PR レビューを経ていないワークフロー変更を正常にブロックした。だがわずか5秒後の 10:08:34 UTC、攻撃者は組織オーナー権限を持つ GitHub CLI トークンを用い、ブランチ保護を強制解除してコミット 9cf97e935aeaf791b9c8db224a2bb3ee156134ce をプッシュした。10:46:39 UTC、改ざんされたワークフローはタイポスクワッティングされた Berri-AI/litellm(正規の BerriAI よりハイフンが1つ多い)のコミット 20535d8fa を取得してプレリリースを成功させた。11:11:18 UTC、2つ目の開発者認証情報によって本番リリースの承認ゲートが通過した。11:53 UTC に汚染パッケージの配布が完了すると、3つ目の認証情報によって本番およびプレリリースの実行ログが削除された。攻撃者が3つの正当な高権限クレデンシャルを手中に収めていた時、自動署名や承認ゲートは防御の盾となるどころか、悪意あるビルドに公式のお墨付きを与える道具と化した。

LiteLLM 公式が発表した10月21日パイプライン侵害インシデント報告 図:LiteLLM 公式セキュリティアドバイザリによる10月21日のパイプライン侵害タイムライン。出典:LiteLLM(公式インシデント報告)

Claude Code がレスポンスにトークンを記録、公開ログが権限昇格の踏み台に

中核メンテナーの GitHub 認証情報はどこから漏洩したのか。LiteLLM 公式の事後調査により、決定的な詳細が判明した。特定の条件下において、Claude Code はユーザーの GitHub CLI OAuth トークンを API のレスポンスボディ内に含めて返却する動作をしていた。ゲートウェイ側でレスポンスボディのログ記録が有効化されていた場合、それらの特権トークンはそのまま LiteLLM のログに平文で書き込まれることになる。チームが内部ネットワークを調査したところ、インターネットに露出していた見覚えのない LiteLLM インスタンスが特定された。攻撃者は直近でパッチが当たった脆弱性を突いてそのインスタンスに侵入し、ログから GitHub CLI の認証情報を回収したと断定された。

この入れ子構造のインシデントを巡り、セキュリティコミュニティでは2つの見解が議論された。AIアシスタントに批判的な側は、ローカルエージェントが端末に対して広範すぎるアクセス権を持っていること、そして gh トークンをレスポンスに含めてしまう設計が最小権限とサニタイズの原則に反していると指摘した。一方、インフラ運用を重視する側は、クライアントが何を送信しようと、レスポンスボディを丸ごと保存するゲートウェイを公衆網に放置したこと自体が重大な運用過誤だと論じた。双方の主張は同一の技術的現実を浮き彫りにしている。開発者がAIアシスタントを使ってゲートウェイのコードを書き、そのアシスタントがゲートウェイログに鍵を吐き出す。AIツールチェーンの自己循環構造が、単一障害点のリスクを爆発的に拡大させたのだ。

復旧の第一歩は「pip を実行しないこと」、バージョン固定こそが防衛線

1.107.0 を導入してしまった組織に対し、公式の復旧手順書は直感に反する鉄則を冒頭に掲げている。「絶対にまず pip install を実行してはならない」。pip 自体が Python プロセスであるため、実行した時点で cook.pth と sitecustomize.py が再び起動してしまうからだ。正しい手順は、site-packages 直下の cook.pth、cook.py、sitecustomize.py および __pycache__ ディレクトリを手動で直接削除し、/tmp/remote_code_loader.lock を保持しているゾンビデーモンプロセスを kill することである。クリーンアップが完了して初めて、1.106.2 へのダウングレードやコンテナ・仮想環境の再構築に進むことができる。

悪意あるファイルの削除は応急処置に過ぎない。本質的な最優先事項は全クレデンシャルの完全なローテーションである。公式アナウンスは警告している。「1.107.0 が外部通信可能なホストで一度でも動作した場合、情報流出は既に完了していると想定しなければならない」。コンテナの再起動や /tmp の初期化によってロックファイルは消えてしまうため、ロックファイルが見当たらないからといって安全とは言えない。ホストの IAM ロール、環境変数内の機密情報、DATABASE_URL のパスワード、プロキシ DB 内の各モデルプロバイダー API キーをすべて再発行し、インストール時刻前後の CloudTrail における GetCallerIdentity 呼び出し履歴を洗い出す必要がある。なお、1.106.2、1.107.0rc1、1.107.0rc2、および固定バージョンの litellm-proxy-extras==0.4.110 のクリーンさは確認されている。2時間の電撃戦の犠牲となったのは、バージョン指定を固定していなかった環境だけだった。

サプライチェーンセキュリティの主戦場は移り変わった。攻撃者はもはや怪しい PR をリポジトリに送り込む手間をかけず、インターネット上に露出したログから認証情報を拾い、公式のビルドパイプラインに毒入りパッケージを署名させている。AIコーディングツールが日常的に端末内の機密トークンを処理する今、ゲートウェイログを高機密情報として厳格に隔離し、本番のすべての依存関係をハッシュレベルで固定することこそが、AIインフラの信頼境界を守る唯一の防壁である。

参考リンク:

  • SafeDep サプライチェーンセキュリティ分析レポート:LiteLLM PyPI 認証情報窃盗とバックドアの詳細分析
  • LiteLLM 公式セキュリティアナウンス:2026年10月21日セキュリティインシデントの振り返り
  • Hacker News コミュニティにおける LiteLLM 1.107.0 汚染事件に関するディスカッション