LiteLLM攻撃を外から追跡する。.pthファイルの「見えない自動実行」を検証した
LiteLLMのPyPI侵害で使われた.pthファイル攻撃を外部から検証した。Pythonのsite.pyが起動時に.pthを自動実行する仕組み、exfilドメインのDNS追跡、従来のPyPIマルウェアとの比較、攻撃者自身のバグまで。IOCも整理。
コラム
kkm
Backend Engineer / AWS / Django
何を調べたのか
LiteLLMのPyPIパッケージが乗っ取られた件で、私が気になったのは2つだ。攻撃者が使ったインフラは今どうなっているのか。そして「Pythonを起動するだけで感染する」という.pthファイルの仕組みは、実際にどう動くのか。
内部のペイロードを実行する気はない。外部から観測できる情報だけで、この攻撃の輪郭を描いてみる。
.pthファイルはなぜ危険なのか
今回の攻撃で最も技術的に興味深いのは、バージョン1.82.8で採用された .pth ファイルという手法だ。PyPIマルウェアといえば setup.py のインストールフックが定番だったが、.pthはそれとは根本的に違う。
Pythonには site.py というモジュールがある。インタープリタが起動するたびに呼ばれ、site-packages ディレクトリ内の .pth ファイルを走査する。本来はパッケージのパスを追加するための仕組みだが、import で始まる行はPython文として実行される。
試しに手元のmacOSで確認してみた。
$ python3 -c "import site; print('\n'.join(site.getsitepackages()))"
/Library/Developer/CommandLineTools/.../site-packages
/Library/Python/3.9/site-packages
$ ls /Library/Developer/CommandLineTools/.../site-packages/*.pth
distutils-precedence.pth
$ cat distutils-precedence.pth
import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'stdlib') == 'local'; ...distutils-precedence.pth はsetuptoolsが使っている正規の.pthファイルだ。中身を見ると import os; で始まり、Pythonが起動するたびに実行されている。つまり.pthファイルによる自動実行は、攻撃者が発明した手法ではない。Pythonのパッケージシステムに最初から組み込まれている仕組みを、悪用しただけだ。
攻撃者が仕込んだ litellm_init.pth は、この仕組みを使って二重base64エンコードされたペイロードを subprocess.Popen で子プロセスとして起動する。従来のマルウェアとの違いを整理すると、深刻さがわかる。
| 手法 | 実行タイミング | import必要 | 持続性 |
|---|---|---|---|
| setup.py hook | pip install 時のみ | 不要 | 1回限り |
| __init__.py injection | パッケージimport時 | 必要 | import毎 |
| .pthファイル | Pythonインタープリタ起動時 | 不要 | 毎回 |
setup.py フックは「インストール時に1回だけ」実行されるため、インストール後にシステムを監視すれば検出の余地がある。__init__.py への注入は、当該パッケージをimportしない限り発動しない。しかし.pthファイルは、同じvenv内で pytest を実行しても、django の開発サーバーを起動しても、python -c "print('hello')" と打っても発火する。パッケージの存在を知らなくても感染する。
攻撃インフラを外から追う
マルウェアの送信先ドメイン models.litellm.cloud を調べてみた。
$ whois litellm.cloud | grep -E "^(Creation Date|Registrar|Name Server):" | head -4
Creation Date: 2026-03-24T21:28:48.040Z
Registrar: NAMECHEAP
Name Server: dns1.registrar-servers.com
Name Server: dns2.registrar-servers.com
$ dig models.litellm.cloud +short
(応答なし)
$ curl -sS -o /dev/null -w "HTTP:%{http_code}" https://models.litellm.cloud/
HTTP:000ドメインは攻撃当日の3月24日にNamecheapで取得されている。作成時刻は21:28 UTCで、悪意あるパッケージが公開された08:30 UTCよりも後だ。WHOISの作成日時はドメインの「初回登録」ではなく「現在のレジストラでの登録日」を示す場合がある。実際には、攻撃者は前日の3月23日にドメインを準備していたことがSnykの分析で確認されている。
現在、DNS解決は不可能で、HTTPアクセスもコネクションが確立しない。テイクダウンされたか、攻撃者自身がインフラを撤収したかのいずれかだ。
もう一つのC2ドメイン checkmarx.zone はバックドアの永続化に使われており、systemdサービスが50分ごとにアクセスして次段階のペイロードを取得する。名前が示す通り、セキュリティ企業Checkmarxの名前を騙っている。TeamPCPは同時期にCheckmarxのGitHub ActionsとOpenVSX拡張機能も標的にしており、C2ドメインとしてこの名前を選んでいる。
攻撃者もバグを出す
技術的な分析で面白い指摘がある。FutureSearchの分析によると、.pthファイルはPythonプロセスの起動ごとにペイロードを subprocess.Popen で子プロセスとして生成する。そしてその子プロセスもPythonプロセスなので、起動時にまた.pthが読まれ、また子プロセスが生まれる。
理論上、これはフォークボムになる。
実際には子プロセスがsite.pyを読まない設定(-S フラグなど)で起動されていれば回避できるが、もし対策が不完全なら、マルウェアがインストールされた環境ではPythonを起動するたびにプロセスが指数関数的に増殖し、最終的にシステムがハングする。攻撃者にとって最大の敵が自分自身のバグだったかもしれない、というのは皮肉な話だ。
IOCの整理
影響を受けた可能性のある環境で確認すべき痕跡(Indicator of Compromise)を整理する。
| 種別 | 値 |
|---|---|
| ファイル | litellm_init.pth (SHA256: 71e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238) |
| ファイル | proxy_server.py (SHA256: a0d229be8efcb2f9135e2ad55ba275b76ddcfeb55fa4370e0a522a5bdee0120b) |
| バックドア | ~/.config/sysmon/sysmon.py |
| systemdサービス | sysmon.service(ユーザーサービス) |
| exfilドメイン | models.litellm.cloud |
| C2ドメイン | checkmarx.zone/raw |
| K8s Pod | node-setup-* (kube-system namespace) |
| tarアーカイブ | tpcp.tar.gz(TeamPCPの命名規則) |
この調査で確認できなかったこと
外部からの観測には限界がある。今回はマルウェアのペイロードを実際に実行していないし、侵害されたPyPIパッケージのバイナリも入手していない。.pthファイルの仕組みはPythonの公開ドキュメントとローカル環境の既存ファイルから検証し、攻撃の動作はSnyk、Wiz、FutureSearchの分析レポートと照合した。
「影響を受けたのは3時間のウィンドウだけ」という点についても、正確な被害規模は不明だ。自動ビルドパイプラインやDocker buildでlitellmの最新版を取得していた環境では、人間が気づく前にマルウェアが実行されている可能性がある。
pip installの一打が全てを変える
今回の攻撃で改めて突きつけられたのは、pip install という日常的な操作が持つ破壊力だ。正規のパッケージが正規のアカウントから公開され、正規のチェックをすべて通過し、インストールしただけで認証情報が根こそぎ持っていかれる。
バージョンをピン留めしていても、CI/CDの上流にあるセキュリティスキャナーが侵害されれば、ピン留めしたバージョンそのものが汚染される。Trivyという「守る側のツール」が攻撃の入口になったという事実は、サプライチェーンの信頼モデルがどこまで脆いかを示している。
LiteLLMチームは現在、Mandiantと協力して調査を続けている。.pthファイルを悪用した攻撃はPythonエコシステムではまだ珍しいが、手法が公開された以上、模倣犯が出てくるのは時間の問題だろう。依存関係のハッシュ検証、CI/CDパイプラインのアクション固定、PyPIトークンのスコープ制限。やるべきことはわかっている。問題は、それを全員がやるかどうかだ。
参照元
- →Snyk: How a Poisoned Security Scanner Became the Key to Backdooring LiteLLM
- →FutureSearch: Supply Chain Attack in litellm 1.82.8 on PyPI
- →Wiz: TeamPCP Trojanizes LiteLLM
- →GitHub Issue #24518: litellm PyPI compromised — timeline and status
- →The Hacker News: TeamPCP Backdoors LiteLLM Versions 1.82.7–1.82.8
- →Python公式ドキュメント: site — Site-specific configuration hook