LiteLLM 1.107.0 の改ざん版(PyPI・Docker)を入れたか、Python を動かさずに確かめる手順と、今日失効させる鍵

LiteLLM 1.107.0 の改ざん版(PyPI・Docker)を入れたか、Python を動かさずに確かめる手順と、今日失効させる鍵

ミオ|AI大好きな後輩

ミオ|AI大好きな後輩

LiteLLM は、さまざまな AI サービスを同じ書き方で呼び出すための Python のライブラリで、サーバーとして動かす Proxy の Docker イメージもあります。10月21日、その 1.107.0 に、パスワードやクラウドの鍵を盗むファイルが混ざった状態で、Python のパッケージ置き場 PyPI と Docker に公開されました。LiteLLM の公式のインシデント記事は、公開されていた間に pip で入れたか、Docker イメージを起動した環境を、侵害されたものとして扱うよう求めています。

この記事は、自分の環境が対象かどうか、疑わしい環境の Python を動かさずに入れたかを調べる方法、止めて作り直す手順、失効させる鍵の範囲を、公式の記事と解析各社の資料から整理します。疑わしいホストやコンテナは、調査に入る前にネットワークから切り離します(Endor Labs)。Python を追加で動かさなくても、すでに動いている常駐プロセスは通信できるためです。切り離したうえで、今日の作業は次の三段です(筆者の整理です)。

LiteLLM の公式のインシデント記事と、Endor Labs・Chainguard・JFrog・Datadog・Wiz の公開資料に基づく図です。疑わしい環境への今日の作業を、ファイルで確かめる、止めて作り直す、鍵を失効させる、の三段に分けています。この分け方は各資料の手順をもとにした筆者の整理です。段ごとの具体的な中身(ファイル名・コマンド・失効させる鍵の一覧)と、各資料の取得時刻は図に載せず、本文に書いています。

  1. ファイルで確かめる:Python を動かさず、シェルの道具で、混入したファイルと 1.107.0 の記録を探します
  2. 止めて作り直す:ネットワークから切り離し、混入したファイルを消して、仮想環境やイメージを作り直します
  3. 鍵を失効させる:その環境から読めたものを、感染していない別の端末で取り替えます

筆者は、LiteLLM の公式記事(日本時間10月22日 8:39 に取得。協定世界時 20:30 更新と表示)、GitHub の issue(8:39 に API で取得。最後のコメントは協定世界時 23:37)、解析各社の記事とドキュメント(6:35〜6:58 に取得)を突き合わせました。調査は続いていて、時刻が資料で食い違う箇所は並べて書きます。侵入経路は調査中で、公式が公表した範囲を紹介します。攻撃者の特定は扱いません。

自分は対象なのか

公式記事(8:39 取得)によると、PyPI の litellm 1.107.0 は、配布形式である wheel のすべてが、協定世界時 11:39〜13:23(日本時間 20:39〜22:23)の間、汚染された状態で公開されていました。Docker Hub の v1.107.0・latest・main-latest・main-stable の各タグは、協定世界時 11:46〜14:20(日本時間 20:46〜23:20)の間、汚染されたイメージを指していました。GitHub のコンテナレジストリ(GHCR)の同じ各タグも、11:46 から、取得できなくなった 13:51(日本時間 22:51)までの間、汚染されたイメージを指していました。latest のようにバージョンを固定しないタグを当該期間に取得した場合、1.107.0 を選んでいなくても汚染版を受け取った可能性があります(Endor Labs、6:58 取得)。

今回の件では 1.107.0 より前の版は安全で、固定するなら 1.106.2 か、公開済みの明示した版を使うよう、公式記事は書きます。ただし、3月に侵害された 1.82.7 と 1.82.8 は別の件の汚染版なので、固定先から外します(Datadog、筆者の整理です)。企業向けの非公開の配布は影響を受けていません。LiteLLM の GitHub のタグのソースには混入が無く、攻撃者が管理するリポジトリから作られた成果物だけが汚染されていたので、ソースを読んでも気づけません。

直接入れていなくても対象になりえます。週に約2,300万回ダウンロードされる(Datadog、6:58 取得)ライブラリで、Chainguard(6:58 取得)は、これに依存するパッケージが2,000を超えると書きます。Strands Agents は、10月21日のリリースで litellm を 1.107.0 未満に制限しました(リリースノート、6:44 取得)。ロックファイルに 1.107.0 が見つかったら、アプリが litellm を読み込んでいなくても調べる対象です(Chainguard)。

公開の窓を外れた取得も残りえます。GitHub の issue(8:39 取得)で第三者が、タグを消しても、窓の間に社内ミラーや CI のキャッシュが取り込んだ分は確認できないと指摘しています。Endor Labs は、10月21日の CI のログと依存のキャッシュを調べるよう書きます。

ファイルで確かめる:Python を動かさずに入れたかを調べるには

このマルウェアは、LiteLLM を使っていなくても動きます。汚染された wheel は、cook.pth、cook.py、sitecustomize.py のファイルを、Python のライブラリ置き場(site-packages)の直下に置きます(Wiz、6:36 取得)。Python の公式ドキュメント(6:54 取得)によると、.pth ファイルの import で始まる行は、-S を付けて起動した場合を除き、Python の起動時に必ず実行され、sitecustomize という名前のモジュールも自動で読み込まれます。そのため、その環境で Python や pip を動かすたびに、盗む処理が再び走ります。litellm のパッケージを消しても、これらのファイルは残ります(Datadog)。

確認の順序は資料で分かれています。LiteLLM の公式記事は確認方法に pip show litellm を挙げますが、同じ記事は、影響を受けた環境では pip を含む Python の起動ごとに再び実行されると書きます。Chainguard、Endor Labs、JFrog(6:40 取得)は、疑わしい環境の Python や pip を動かさず、シェルの道具で調べるよう書きます。GMO Flatt Security の記事(6:35 取得)の手順は、pip show litellm と pip uninstall litellm から始まります。pip show も Python の起動なので、疑わしい環境ではファイルの検索を先にします(筆者の整理です)。

ネットワークから切り離した環境で調べます。次のコマンドは、Chainguard と Endor Labs が載せる手順から選んだもので、Python を使いません。

find / -path "*site-packages/cook.pth" -o -path "*site-packages/cook.py" -o -path "*site-packages/sitecustomize.py" 2>/dev/null
find / -path '*site-packages/litellm-1.107.0.dist-info' -type d 2>/dev/null
grep -rn "1.107.0" requirements*.txt poetry.lock uv.lock Pipfile.lock pyproject.toml 2>/dev/null | grep -i litellm
ls -la /tmp/.pc_once_* /tmp/remote_code_loader.lock /tmp/.pc_sq_* /tmp/.pc_e_* 2>/dev/null
docker images --digests | grep -i litellm

最初の grep は、リポジトリのルートで実行し、名前と版が同じ行にある形式(requirements.txt など)でしか当たりません。poetry.lock・Pipfile.lock・uv.lock は、1.107.0 を検索して、前後の同じパッケージの記録に litellm があるかを見ます。uv.lock は wheel の URL の行で当たる場合もありますが、出力が無いだけで対象外とは判断しません。また、macOS の既定の zsh では、一致しないパスがあるとエラーで止まり、grep と ls は実行されません。bash で実行するか、パスを個別に確かめます(いずれも筆者の整理です)。

Docker は、イメージの digest(内容から決まる識別子)で照合します。次は、litellm の index が公式記事、litellm の amd64 と arm64 が Endor Labs、litellm-database と litellm-non_root の amd64 と arm64 が Chainguard の載せるものです。公式記事は litellm-database と litellm-non_root の index を先頭12桁(c00f9d92c427、7b00e91cd2fe)だけ載せています。

litellm index  sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e
litellm amd64  sha256:bd1aeee419ea36da9aa2b657288da4101887be468f020ac63d22ba6ed84a3ea5
litellm arm64  sha256:56b74573eed6e4aafe4b50603dbe7853e0345f188c8e44a09f1098caf1f4bcdf
litellm-database amd64  sha256:24581ddc30767f16ecb69d2b0517556f2cd7dea56971adb42a3d1beb97d69523
litellm-database arm64  sha256:827dd893c74cf621a860a66a3a447a5c732533f087589ce905573e285c0a239c
litellm-non_root amd64  sha256:645a67d810628252d63df85dcc8824c58bcc77cf8383f4d3e6321f0ccc3ce9e4
litellm-non_root arm64  sha256:f75c8edae59b72b1ebfd617295833f465c85bb2fab9c1d60f21b496db86cb8da

見つかったときの読み方です。cook.pth か cook.py、内容かハッシュで照合して一致した sitecustomize.py、litellm-1.107.0.dist-info、上の識別子のどれかが見つかれば、対象です。sitecustomize.py は正規のものもあるので、名前だけでは決められません(Endor Labs)。照合先は、上の Endor Labs の記事の「File hashes」節です。そこに載る sitecustomize.py の SHA-256 は 34bef04fe88ac9d0aae0475630b1738681d15b94f233ac8aa46eacf5968305fa です。シェルの sha256sum(macOS では shasum -a 256)で対象ファイルの値と比べます。値が異なることだけで安全とは判断しません。公式記事は、見つからなくても環境が無事とは限らないと書きます。/tmp/.pc_once_72129118c59f5636c0bb は、送信に成功したあとにだけ作られる目印で、あれば持ち出しが完了しています。無くても無事とは言えません(JFrog)。Windows では、常駐の仕組みは動きませんが、盗む処理は動きます(JFrog)。

止めて作り直す:入れていたら、まず何をするか

  • 切り離す:疑わしい環境は、冒頭で述べたとおり調べる前にネットワークから切り離し、プロセス一覧とファイルの状態、外向きの通信ログを残します(Endor Labs)
  • 消す:Python や pip を動かさず、シェルから cook.pth、cook.py、照合で一致した sitecustomize.py と、同名の __pycache__ のファイルを、影響を受けたすべての site-packages から消します(公式記事。正規の sitecustomize.py があるので、消す前に確かめると Chainguard は書きます)
  • 止める:/tmp/remote_code_loader.lock を握る常駐の Python プロセスを止めます(公式記事)
  • 作り直す:公式記事は、litellm==1.106.2 に固定して入れ直すだけでなく、仮想環境やイメージを最初から作り直すほうがよいと書きます。JFrog は、1.106.2 を入れるのは、起動時のファイルを消したあとか、新しく作った環境に限るとします
  • 送信先を止める:盗んだデータの送信先は、公式のドメイン litellm.ai に似せた www.litellm[.]dev です(JFrog)。DNS とプロキシのログで通信を探し、止めます

Docker のタグの扱いは資料が分かれます。公式記事は、Docker Hub のタグは協定世界時 14:20 までに取り除いたか、検証済みの安全な成果物に向け直したと書きます。一方、issue には、協定世界時 17:03 の確認として、latest・main-latest・main-stable・v1.107.0 のどれも 404 を返し、タグは向け直されず削除されていたという投稿があります(AI エージェントを名乗るアカウントの投稿)。どちらでも、タグでなく版か digest で固定して作り直します(Endor Labs)。

Docker の撤去時刻も食い違います。Wiz は、GHCR と Docker Hub の両方を協定世界時 13:53 ごろに撤去したと書き、公式記事の Docker Hub の 14:20 と合いません。窓の端は公式記事の 14:20 で見ます。

PyPI からの削除時刻も食い違います。公式記事は協定世界時 13:23 まで、Wiz は 13:20 の時点で隔離済みと書き、Qiita の記事(6:56 取得)は日本時間 22:40 ごろ(協定世界時 13:40 ごろ)と書きます。窓の端は余裕を持って見ます。

鍵を失効させる:何を取り替えるのか

公式記事は、1.107.0 を入れたホストが外に出られたなら、持ち出しは成功したと考えるよう書きます。目印のファイルが無くても同じです。作業は、感染していない別の端末から行います(Datadog、Endor Labs)。

急ぐ理由があります。Wiz(19:05 更新の記載)は、盗まれた AWS のインスタンスロールの認証情報が、持ち出しの約4.5時間後(協定世界時 16:20〜17:01)に、身元確認の呼び出し(GetCallerIdentity)と Bedrock のモデル一覧の取得で試されたのを観測し、他人のクラウドで AI モデルを無断で使う手口(LLMjacking)と整合する動きだと書きます。公式記事は、CloudTrail でインスタンスロールの GetCallerIdentity をインストール時刻の前後で探し、そのあと、ロールの認証情報が組織の外から使われていないかを見るよう書きます。

取り替えるものは、置き場所ごとに分けると次のとおりです。

  • LiteLLM Proxy:DB に保存した上流プロバイダの鍵すべて、マスターキー、仮想キー、DATABASE_URL のパスワードです(公式記事)。Proxy は上流の鍵を PostgreSQL に保存していて、マルウェアはそのデータベースを直接読みます(Wiz)
  • クラウド:インスタンスやタスクの IAM ロール、AWS・Azure・Google Cloud の認証情報、Kubernetes のサービスアカウントのトークン、コンテナレジストリの認証情報です(Endor Labs)
  • 環境変数と .env:そのホストのどのプロセスの環境変数にあった秘密も含みます(公式記事)。.env のファイルも盗まれます(Endor Labs、Datadog)。Linux では、読める限りほかのプロセスの環境変数も集めます(JFrog)
  • 開発の鍵:SSH・GPG・TLS の秘密鍵、Git・npm・PyPI のトークンです(Endor Labs)
  • AI ツール:Claude・Codex・Gemini・Cursor・Copilot のセッションです(Endor Labs)
  • ブラウザ:保存したパスワードと Cookie です(Datadog)

取り替えたあとは、クラウド・ID 管理・リポジトリ・レジストリの監査ログで、インストール以降に古い鍵が使われていないかを確認します(Endor Labs)。

原因はどこまで分かっているのか

公式記事(8:39 取得。協定世界時 20:30 更新と表示)の「How this happened?」は、攻撃者が3組の開発者の GitHub CLI の認証情報を持ち、そのうち1組はワークフローを実行できたと書きます。また、攻撃者が持っていたトークンの1つは組織のオーナーのもので、攻撃者はそれでブランチ保護を無効にして強制的に書き込み、正規の BerriAI/litellm にハイフンを足した Berri-AI/litellm を指すワークフローを実行して、汚染された成果物を署名・公開しました(ハイフンの有無だけの違いは Datadog も書きます)。

トークンの出どころについて、公式記事は、Claude Code が応答本文に GitHub CLI の OAuth トークンを埋め込む場面があり、応答本文をログに残すと LiteLLM のログに現れうると書きます。また、内部で動かしていた LiteLLM がインターネットに公開されていて、攻撃者はそのログからトークンを得たと考えていますが、そこへどう入ったかは確認できておらず、最近修正された脆弱性を疑っています。issue の LiteLLM メンテナのコメント(協定世界時 23:37)は、組織のすべての認証トークンを洗い出して失効・ローテーションすることが当面の焦点で、最初の侵入の経緯には解決していない点が残るとし、レジストリは新しいリリースに対してロックしたままで、通常のリリースは止めていると書きます。

provenance(どこで誰が作ったかの記録)があっても、所有者名を確かめます。Endor Labs によると、コンテナの build の provenance には source_owner が Berri-AI と記録されていました。PyPI の公式ドキュメント(Trusted Publishing の説明、6:55 取得)も、Trusted Publishing はコードの安全性や作者の信頼性を保証しないと書きます。

LiteLLM の PyPI 版が侵害されたのは今年2回目です。JFrog は、3月の 1.82.7 と 1.82.8 とは別の件として扱うよう書き、Datadog も、両者の関連は示されていないとします。

次に備えるには

  • 版とハッシュを固定し、イメージは digest で固定します。固定しないタグやインストールが、選んでいない環境まで巻き込んだと Endor Labs は書きます
  • 公開直後の版を入れない待機期間(cooldown)を置きます。PyPI の公式ブログ(4月2日付、6:54 取得)は uv の exclude-newer を挙げ、Chainguard は、スキャナーと研究者が調べる時間が取れるまで新しい版を手の届かない所に置く仕組みと説明します。PyPI の今回の版は約2時間で取り除かれた(Datadog)ので、数日の待機は公開直後の自動取得を避ける助けになります。ただし、社内ミラーやキャッシュに残った分まで安全にする仕組みではありません(筆者の整理です)
  • 依存に新しく増えた、実行する行を含む .pth と sitecustomize.py を見張ります。Endor Labs は、これらは珍しく、見つかれば強い兆候だと書きます

取り替えた API キーに利用上限を付ける考え方は有料の既刊コーディングエージェントにAPIキーを渡す前に:止まる上限と通知だけの上限を見分け、上限到達で再試行を止めるで、CI でエージェントを回すときのトークンの権限はGitHub Actions でコーディングエージェントを安全に回す:信頼できない入力・トークンの権限・使えるツールを絞る点検表で整理しています。

参考になったら、記事下部のライクとXの共有ボタンから応援してもらえるとうれしいです。


あなたも記事の投稿・販売を
始めてみませんか?

Tipsなら簡単に記事を販売できます!
登録無料で始められます!

Tipsなら、無料ですぐに記事の販売をはじめることができます Tipsの詳細はこちら
 

この記事の販売者

ミオ|AI大好きな後輩

先輩、新しいAIもう試しました? Codex・Claude Code・Gemini の新機能を、公式資料から図解でまとめます。 どこが変わって、何に気をつければいいか。 一緒に追いかけましょう!

この販売者が書いた他の記事

  • Claude Code の cloud sessions が正式提供に:クレジットは利用枠より先に使われる(公式の補足あり)

  • Opus 5 から 5.5 へ移る前に:400 を返す破壊的変更4つと、エラーなしで変わる2つを自分のコードで探す

    ¥300
    1 %獲得
    (3 円相当)
  • Gemini 4 Argon が発表:出力100万トークン・料金・提供元のベンチマーク・いま使える人

関連のおすすめ記事

  • ゼロから3日で始動|AIコンサル大全

    ¥124,800
    1 %獲得
    (1,248 円相当)
    水口一星

    水口一星

  • 【AI自動化・マネタイズ実例書】たった2週間〜機械オンチなママでもできた全作業過程

    ¥38,300
    1 %獲得
    (383 円相当)
    みお

    みお

  • 【5年更新型コンテンツ】AIを最大活用するためのリテラシー強化バイブル

    ¥59,800
    1 %獲得
    (598 円相当)
    こはく

    こはく