Chainguardの未然防御事例:LiteLLMが再び攻撃され、PyPIとDocker Hubに悪意ある1.107.0が公開
本稿は Chainguard 公式ブログ「LiteLLM attacked again: Malicious 1.107.0 wheels on PyPI and Docker Hub (October 2026)」(2026年10月21日公開)の和訳記事です。
著者:Quincy Castro (Chainguard社 CISO)
TL;DR(要約)
何が起きたか: 2026年10月21日、攻撃者が LiteLLM 1.107.0 を PyPI に公開しました。7つのwheelすべてに、認証情報窃取マルウェアとリモートコードローダーが含まれていました。同バージョンの LiteLLM 公式 Docker イメージにも、同じペイロードが含まれています。
影響を受けるアーティファクト: PyPI 上の litellm==1.107.0、および ghcr.io/berriai/litellm、litellm-database、litellm-non_root の各イメージ、そして docker.io/litellm/litellm の v1.107.0、latest、main-latest、main-stable の各タグです。
規模: litellm は直近1か月で PyPI から 1億回以上ダウンロードされています。2,000以上のパッケージが依存しているため、多くのチームが直接指定していなくてもインストールしています。
リスク: Python は起動のたびにペイロードを実行します。litellm を import するコードがなくても実行されます。窃取マルウェアは、SSH鍵、クラウドの認証情報、CI/CD のシークレット、ブラウザのパスワード、AI ツールのトークン、そして LiteLLM プロキシのデータベースに保存された LLM プロバイダーの API キーを盗みます。
Chainguard のお客様: 影響はありません。 Chainguard は多層防御により、悪意あるパッケージが利用可能にならないようにしています。Chainguard のマルウェアスキャンは、リリースからわずか16分後に 1.107.0 を検知しました。また、Chainguard は LiteLLM を上流のソースコードからビルドしており、そのソースコードにはペイロードが含まれていません。
対処法: 1.107.0 をインストールした場合、または影響を受けるイメージを pull した場合は、そのホストを隔離してください。Python を再び実行する前にスタートアップファイルを削除し、そのホストが到達できたすべての認証情報をローテーションしてください。
2026年10月21日 11:39 UTC に、LiteLLM 1.107.0 が PyPI に現れました。GitHub 上のソースコードは無害でしたが、ビルド済みの7つのwheelすべてに、認証情報窃取マルウェアと、1時間ごとに動作するリモートコードローダーが含まれていました。LiteLLM が侵害されたのは、7か月で2回目です。2026年3月には、TeamPCP グループが盗んだ PyPI トークンを使い、バージョン 1.82.7 と 1.82.8 を公開しました。
本稿では、何が起きたのか、なぜ Chainguard のお客様が影響を受けなかったのか、そしてそれ以外の LiteLLM 利用者がいますぐ行うべきことを説明します。
影響を受けるパッケージ
影響を受けるのは、1つの PyPI リリースと、それを元に作られた公式 Docker イメージです。
| パッケージ | 悪意あるバージョン | 公開日時(UTC) | 最新の安全なバージョン |
|---|---|---|---|
| litellm(7つのwheelすべて) | 1.107.0 | 10月21日 11:39:51 | 1.106.2 |
| イメージ | 影響を受けるタグ |
|---|---|
ghcr.io/berriai/litellm | v1.107.0 |
ghcr.io/berriai/litellm-database | v1.107.0 |
ghcr.io/berriai/litellm-non_root | v1.107.0 |
docker.io/litellm/litellm | v1.107.0、latest、main-latest、main-stable |
wheel はサポート対象のすべてのプラットフォームを網羅しています。macOS(x86_64 と arm64)、manylinux と musllinux(x86_64 と aarch64)、Windows です。バージョン 1.106.2、1.107.0rc1、1.107.0rc2 は無害です。PyPI は 1.107.0 を隔離(quarantine)しました。
ソース配布物(sdist)には、ペイロードのファイルは含まれていません。ただし、その pyproject.toml には data = "cook" という1行が追加されており、v1.107.0 の Git タグにはありません。Git タグは、通常の署名付きコミットを指しています。Docker Hub や GHCR の latest タグを信用しないでください。各イメージのダイジェストを、後掲のリストと照合してください。
マルウェアの挙動
追加された data = "cook" という設定により、ビルドツールの maturin は、cook/ フォルダを各wheel内の litellm-1.107.0.data/purelib/ にコピーします。インストール時、pip はこのフォルダのファイルを、他のすべてのパッケージと同じ site-packages のルートに配置します。攻撃者はこの経路を使い、cook.pth、cook.py、sitecustomize.py の3つのファイルを置きました。
起動時のトリガー: cook.pth には import cook の1行だけが書かれています。Python は起動時に .pth ファイルを処理するため、その環境で Python プロセスが起動するたびに cook.py が実行されます。pip、IDE の言語サーバー、litellm を import しないスクリプトも対象です。
認証情報の窃取: cook.py は、privcollect と自称する 61 KB のプログラムをデコードします。このプログラムは、SSH・GPG・TLS の秘密鍵、シェルの履歴、ブラウザのパスワードと Cookie、AWS・Azure・Google Cloud の認証情報、Kubernetes の設定とサービスアカウントトークン、Docker のシークレット、パッケージレジストリのトークンを収集します。さらに、GitLab Runner、Jenkins、Vault、Terraform、Packer の CI/CD シークレットと、15以上の AI コーディングツールの設定も取得します。
クラウドとプロセスのシークレット: このプログラムは、Linux 上で読み取り可能なすべてのプロセスの環境変数を読み取ります。AWS、Google Cloud、Azure のインスタンスメタデータサービスに問い合わせ、盗んだ AWS の認証情報を GetCallerIdentity で検証します。
LiteLLM プロキシのデータベース: このプログラムは DATABASE_URL を通じて接続し、LiteLLM_CredentialsTable と LiteLLM_ProxyModelTable をダンプします。侵害されたプロキシホストが1台あれば、多数の上流 AI プロバイダーのキーが一度に漏えいしかねません。
データの外部送信: このプログラムは、盗んだデータを AES-256-CBC で暗号化し、その鍵を RSA-4096 の公開鍵で包み、結果を tar.gz アーカイブにまとめます。そして、アーカイブを平文の HTTP で www.litellm[.]dev/healthcheck に送信します。このドメインは正規の litellm.ai ドメインではなく、よく似せた偽物です。
リモートコードローダー: sitecustomize.py は、Linux と macOS 上で、切り離された(detached)デーモンをフォークします。このデーモンは1時間ごとに、GitHub リポジトリ内のファイルからサーバーのアドレスを読み取ります。アドレスが解決できると、窃取プログラムを再び実行し、そのサーバーから Python コードをダウンロードして、整合性チェックなしで実行します。
ローダーの状況: 研究者が分析した時点で、この GitHub 上のファイルは空だったため、ローダーはコードを実行しませんでした。Wiz によると、GitHub はこのリポジトリを削除しています。ローカルの窃取プログラムはこのファイルに依存せず、単独で動作します。
プラットフォームごとの範囲: Windows では、デーモンが os.fork() を必要とするため失敗します。それでも、窃取プログラムは Windows で実行されます。
侵入の痕跡(インジケーター)
影響を受けるバージョンをインストールした場合、または影響を受けるイメージを実行した場合(上の「影響を受けるパッケージ」を参照)は、その環境が侵害されたものとして扱ってください。
ネットワーク
www.litellm[.]dev(データ送信先、パスは/healthcheck)- raw.githubusercontent[.]com/letters9813/pythonclient-test/main/domain.txt(C2 アドレスのデッドドロップ)
http://{domain}/codepy(第2段階。デッドドロップが返す任意のドメイン)- 外向きリクエストの HTTP ヘッダー
X-Token: XWiA8gZNa7NcX2XRoAEb
wheel のハッシュ(SHA256)
| ファイル | SHA256 |
|---|---|
litellm-1.107.0-cp310-abi3-macosx_10_12_x86_64.whl | 7598727f209d717041db37f7dca271d3a7d328a279890e0e1d55375729aeb86d |
litellm-1.107.0-cp310-abi3-macosx_11_0_arm64.whl | 0aaf7ba346d41ef2f40390436738b001180d5a1c03aa093f121078a9969d9a1a |
litellm-1.107.0-cp310-abi3-manylinux_2_28_aarch64.whl | 668169d35b87bf51324080390b1b1c5ab12bd335cfe5f1a99e3689abd5e02ae6 |
litellm-1.107.0-cp310-abi3-manylinux_2_28_x86_64.whl | 462d0c9f67ca522d9e0e4acd245e90b36ce0d5353604a66bb7c43a1ec4a6fcf7 |
litellm-1.107.0-cp310-abi3-musllinux_1_2_aarch64.whl | c2e32d0ce2548aa662c18bf5001911a79fe5b30cf8357132275add02fdcde054 |
litellm-1.107.0-cp310-abi3-musllinux_1_2_x86_64.whl | 1bbce8caa94f01c0aaa0b7ddae51553a8a32e1ce53d96d44fe583272aff7ad00 |
litellm-1.107.0-cp310-abi3-win_amd64.whl | 12d4ccc5c6f8d35cf4f2cf233b7f3e418e863e657d81ff5f5fc9e210005b9484 |
イメージのダイジェスト
| イメージ | linux/amd64 | linux/arm64 |
|---|---|---|
ghcr.io/berriai/litellm:v1.107.0 および docker.io/litellm/litellm | sha256:bd1aeee419ea36da9aa2b657288da4101887be468f020ac63d22ba6ed84a3ea5 | sha256:56b74573eed6e4aafe4b50603dbe7853e0345f188c8e44a09f1098caf1f4bcdf |
ghcr.io/berriai/litellm-database:v1.107.0 | sha256:24581ddc30767f16ecb69d2b0517556f2cd7dea56971adb42a3d1beb97d69523 | sha256:827dd893c74cf621a860a66a3a447a5c732533f087589ce905573e285c0a239c |
ghcr.io/berriai/litellm-non_root:v1.107.0 | sha256:645a67d810628252d63df85dcc8824c58bcc77cf8383f4d3e6321f0ccc3ce9e4 | sha256:f75c8edae59b72b1ebfd617295833f465c85bb2fab9c1d60f21b496db86cb8da |
パッケージ
- site-packages のルートにあるスタートアップファイル:
cook.pth、cook.py、sitecustomize.py - wheel の
RECORDに記載されたエントリ:litellm-1.107.0.data/purelib/配下の3つのパス - sdist の
pyproject.tomlのビルド設定:[tool.maturin]配下のdata = "cook"
その他の痕跡
- 1回限りの実行マーカー:
/tmp/.pc_once_72129118c59f5636c0bb。窃取マルウェアは、外部送信に成功するとこのファイルを書き込みます。このファイルが存在する場合は、シークレットが盗まれたものとみなしてください。存在しない場合でも、そのホストが無害であると証明されたわけではありません。 - ローダーのロックファイル:
/tmp/remote_code_loader.lock - 一時ファイル:
/tmp/.pc_sq_*および/tmp/.pc_e_* - 環境変数:
PRIVCOLLECT_ACTIVE=1 - 作業ディレクトリが…の、切り離された Python プロセス
影響を受けているかを確認する方法
警告: 侵害が疑われる環境の中で、Python や pip を実行しないでください。Python が起動するたびに、ペイロードが再実行されます。以下の確認は、すべてシェルのツールで行ってください。
1.スタートアップファイルを検索します。環境によっては、正規の sitecustomize.py が存在します。該当したファイルは、削除する前に内容を確認してください。
find / -path "*site-packages/cook.pth" -o -path "*site-packages/cook.py" -o -path "*site-packages/sitecustomize.py" 2>/dev/null
2.ロックファイルと requirements ファイルから、影響を受けるバージョンを検索します。
grep -rn "1.107.0" requirements*.txt poetry.lock uv.lock Pipfile.lock pyproject.toml 2>/dev/null | grep -i litellm
3.ホストのマーカーを探します。
ls -la /tmp/.pc_once_* /tmp/remote_code_loader.lock /tmp/.pc_sq_* /tmp/.pc_e_* 2>/dev/null
4.ローダーのロックファイルを保持しているプロセスを探します。
lsof /tmp/remote_code_loader.lock 2>/dev/null
5.LiteLLM のイメージをダイジェスト付きで一覧し、上記のダイジェストと比較します。
docker images --digests | grep -i litellm
6.DNS やプロキシのログから、www.litellm[.]dev と、パス letters9813/pythonclient-test を検索します。
ロックファイルに該当があった場合は、アプリケーションが litellm を import していなくても、調査が必要です。トリガーは import ではなく、Python の起動だからです。
影響を受けている場合に、いますぐ行うこと
- ホストまたはコンテナを隔離します。 そのホストが到達できたすべての認証情報とファイルが侵害されたものとして扱ってください。
- Python を再び起動する前に、スタートアップファイルを削除します。
cook.pth、cook.py、sitecustomize.py、__pycache__内のそれらのファイル、および/tmpのマーカーを、シェルのツールで削除してください。pip uninstallは Python を起動するため、ペイロードが再び実行されます。 - ローダーを停止します。
/tmp/remote_code_loader.lockを保持している、切り離された Python プロセスをすべて見つけて停止してください。 www.litellm[.]dev、デッドドロップの URL、およびデッドドロップが返したすべてのドメインへの外向き通信(エグレス)をブロックします。 DNS、プロキシ、エンドポイント、クラウド監査のログは保管しておいてください。- 漏えいした可能性のある認証情報をローテーションします。 SSH 鍵と GPG 鍵、TLS 秘密鍵、クラウドと Kubernetes の認証情報、パッケージ公開用トークン、ソース管理のトークン、AI ツールのトークン、ブラウザに保存されたパスワード、
DATABASE_URL、LiteLLM のマスターキーと仮想キー、そしてLiteLLM_CredentialsTable内のすべてのプロバイダーキーが対象です。 /codepyへのリクエストを調査します。 その段階では任意のコードを実行できるため、窃取マルウェアの挙動だけでは影響の範囲を限定できません。- 仮想環境、ホスト、イメージを、検証済みの成果物から再構築します。 新しい環境では
litellm==1.106.2に固定してください。Docker イメージは、latestタグではなくダイジェストで固定してください。
Chainguard のお客様が守られた理由
Chainguard Libraries のお客様は、この攻撃にさらされませんでした。 Chainguard は、新しいパッケージバージョンをすべて、Chainguard Repository から提供する前に、悪意ある挙動がないかスキャンします。litellm 1.107.0 を pull したお客様はいませんでした。
Chainguard Libraries は、多層の防御を適用しています。そのうちの2つが、この攻撃を止めました。
1つ目: Chainguard のマルウェアスキャンは、2026年10月21日 11:55 UTC に litellm 1.107.0 を検知しました。最初のwheelが PyPI に届いてから16分後です。このブロックにより、PyPI のリリースは Chainguard Repository に入らず、ユーザーが pull できなくなりました。
2つ目: 新しいバージョンのマルウェアスキャンが済むと、Chainguard は Chainguard Factory で LiteLLM をソースコードからビルドします。攻撃者はペイロードを PyPI 上のビルド済みwheelに仕込みましたが、v1.107.0 の Git タグには含まれていません。Chainguard がそのソースコードからビルドしたwheelには、cook.pth、cook.py、sitecustomize.py は含まれません。
Chainguard Containers のお客様も影響を受けませんでした。
- Chainguard の LiteLLM イメージは、Chainguard Factory でソースコードからビルドされます。上流の BerriAI のイメージや、PyPI のwheelは使いません。
- Chainguard の Python ビルドは、依存関係を PyPI から直接ではなく Chainguard Libraries 経由で取得するため、さらに1層の防御が加わります。
クールダウン(cooldown)ポリシーを設定しているお客様には、さらにもう1層のガバナンスがあります。クールダウンは、スキャナーや研究者が調べる時間を確保するまで、新しいリリースを利用できないようにします。
LiteLLM が今年 PyPI にマルウェアを配布したのは、これが2回目です。そして、Chainguard のお客様が後始末をする必要がなかったのも2回目です。3月に公開された悪意あるバージョンには、検証可能なソースコードがなかったため、Chainguard はそれらをビルドしませんでした。
今回の教訓と今後の展望
LiteLLM が2026年に侵害されたことが記録されているのは、これが2回目です。研究者は、今回の攻撃を TeamPCP や、3月の侵入経路と結びつけていません。
得られる教訓は次のとおりです。
- 攻撃者はソースコードを変更しませんでした。 Git タグは署名付きコミットを指しており、sdist にもペイロードのファイルはありません。ソースコード、Git タグ、sdist だけをレビューするチームは、この攻撃を見逃します。実行されるのはwheelなので、信頼しなければならない成果物はwheelです。
- ペイロードは AI インフラを狙っています。 1つのテーブルに多数の AI プロバイダーのキーを保持しうる LiteLLM プロキシのデータベースを狙い、15以上の AI コーディングツールの設定も収集します。侵害された AI ゲートウェイが1つあれば、組織全体のモデルアクセスが漏えいしかねません。
- 同じパッケージが、1年のうちに2つの異なる攻撃者に狙われることがあります。 LiteLLM は3月以降、メンテナーのアカウントを入れ替えました。それでも新たな攻撃は、誰も説明できていない経路で PyPI と Docker Hub に到達しました。パッケージのこれまでの履歴が無害でも、次のリリースが無害であるとは限りません。
クリエーションラインでは:
- Chainguardの導入・運用の支援を行っております。お気軽にお問い合わせください。
関連記事:chainguardカテゴリ記事一覧
