📝 この記事は forge.workstyle.tech に掲載した記事の転載です。
MacでLiteLLMを使っていて、依存先に含まれる環境も確認したい状況です。PyPIで配布されたLiteLLM 1.107.0が改ざんされていました。Pythonの起動時に読み込まれるファイルがあるため、バージョンだけでなくファイルと感染の目印も調べます。
先に結論
影響の対象は改ざんされた1.107.0を入れた環境です。確認するのは、インストール済みバージョン、3つのファイル、感染の目印です。入れていた場合はファイルやプロセスを消すだけでは足りず、端末から読めた秘密情報の鍵やトークンを作り直す必要があります。
どのバージョンが危ないのか
対象はPyPIのlitellm==1.107.0です。sdistとLinux・macOS・Windows向けのwheelが改ざんされました。日本時間では10月21日20:39ごろに公開され、22:40ごろにPyPIから削除されています。
私はニュースを知ったあと、自前のゲートウェイと、LiteLLMを依存に持つAIツールを別々に確認しました。まず、それぞれのPython環境でバージョンを見ます。仮想環境の場所を<venv>とすると、次のように実行できます。
<venv>/bin/pip show litellm
Version:の行を確認します。pip showが使えない場合は、環境内のsite-packagesにあるlitellm-*.dist-infoの名前も手がかりになります。
私のゲートウェイではlitellm-1.92.0.dist-infoが見つかりました。別のツールとuvのキャッシュも調べ、いずれも1.92.0でした。バージョンが見つかったら、起動スクリプトが自動更新を実行しないかも確認します。私の起動スクリプトには、仮想環境のLiteLLMを起動する処理だけがあり、インストールや更新の処理はありませんでした。
3つのファイルをどこで探せばよいのか
不正なファイルはPythonのsite-packagesに入ります。名前はcook.pth、cook.py、sitecustomize.pyです。.pthとsitecustomize.pyはPythonの起動時に読み込まれるため、LiteLLMをimportしていないプロセスでも動く可能性があります。
MacではSpotlightの検索も試せます。
mdfind -name cook.pth
mdfind -name sitecustomize.py
ただし、何も表示されなくても「無い」とは言えません。Spotlight(mdfind)は.venvや~/.cache、~/.localのような隠れたフォルダを探さないためです。私もSpotlightでは何も出ませんでした。次にfindで、ホームディレクトリ、Pythonのインストール先、Homebrewの場所などを直接調べました。
find ~ /opt/homebrew /usr/local /Library/Frameworks/Python.framework \
\( -name "cook.pth" -o -name "cook.py" -o -name "sitecustomize.py" -o -name "litellm-*.dist-info" \) \
-print 2>/dev/null
対象を広く探すと時間がかかります。写真や動画、iCloudなどの大きなフォルダを-pruneで除外すると速くなります。自分でPythonを入れた場所や、ツールが使うキャッシュも検索対象に含めてください。Linuxでもfindの考え方は同じです。Mac固有の検索場所はそのLinux環境でPythonやツールを置いた場所に置き換えます。
sitecustomize.pyが見つかったら何を見ればよいのか
sitecustomize.pyという名前だけで感染とは判断できません。私は5つ見つけましたが、正規のツールやPython環境にも入っていました。ファイル名ではなく、ハッシュと中身で見分けます。
各ファイルに対して、次のようにSHA-256を計算します。
shasum -a 256 /path/to/sitecustomize.py
出た値をSafeDepの解析記事に掲載された不正ファイルのハッシュと照らします。ハッシュ一覧はこの記事には載せません。解析記事には不正なファイルのハッシュと詳細があります。
中身も検索します。
grep -i -E "remote_code_loader|PRIVCOLLECT|litellm\.dev" /path/to/sitecustomize.py
私が見つけた5つのファイルは、記事の不正なハッシュと一致せず、検索した目印の文字列も含んでいませんでした。中身を読むときは、ファイル名だけで結論を出さず、ハッシュと合わせて確認してください。
目印ファイルとプロセスはどう確かめるのか
次に、盗んだデータを送った印と、常駐プロセスが動いた印を確認します。
ls -la /tmp/.pc_once_72129118c59f5636c0bb /tmp/remote_code_loader.lock
どちらも無いことだけで判断を終えず、プロセスも調べます。感染時のsitecustomize.pyは親プロセスから切り離された常駐プロセスを作ります。目印の条件のひとつは「親がPID 1のPythonプロセス」です。
ps -axo pid,ppid,lstart,command | awk '$2==1' | grep -i python
ここで注意したいのは、親がPID 1でも、それだけで不審とは限らないことです。launchdで常駐させたプロセスも親が1になります。出力のコマンドラインを見て、自分が起動したプロセスかを確かめてください。私の場合、親がPID 1のPythonプロセスはlaunchdが起動したゲートウェイでした。
環境変数と通信も確認するには
目印の環境変数PRIVCOLLECT_ACTIVE=1も探します。macOSには/procがないため、プロセスの環境変数をps eww -axで確認します。
ps eww -ax | grep "[P]RIVCOLLECT_ACTIVE=1"
私は最初にps eww -ax | grep -c PRIVCOLLECT_ACTIVEを実行し、結果が2になって驚きました。grep自身と、検索コマンドを含むシェルの行が数えられていたためです。検索コマンドの行を除いて数え直すと0でした。上のように最初の1文字を[P]と書くと、grep自身の行には一致しません。件数だけで決めず、表示された行の内容も見てください。
通信先も調べる場合は、次のようにします。
lsof -i -n -P | grep -i python
私は待ち受けが127.0.0.1:4000だけであることを確認しました。ここまでの確認結果を合わせて判断します。
1.107.0を入れていた場合は何をするのか
不正なコードは、SSH鍵、クラウドの認証情報、AIツールのトークン、ブラウザに保存したパスワードや履歴、環境変数、LiteLLMプロキシのデータベースにあるAPIキーなどを盗み、攻撃者のサーバーへ送ります。送り先として解析記事に記載されているドメインはwww.litellm[.]devです。本物のLiteLLMのドメインではありません。
1.107.0が入っていた場合は、3つのファイルを消すだけでは不十分です。常駐プロセスを止め、目印ファイルを消したうえで、その端末から読めた秘密は漏れたものとして扱い、SSH鍵、クラウドの鍵、AIツールのトークン、保存済みパスワード、各社のAPIキーなどを作り直します。詳しい解析と不正ファイルのハッシュはSafeDepの解析記事を確認してください。
私は調べた環境で1.107.0を見つけず、3つのファイルと目印も確認できませんでした。複数の環境にLiteLLMが入っていたので、ひとつの仮想環境だけを見て終わらせず、ツールの環境やキャッシュも調べました。
まとめ
- 各Python環境で
pip show litellmまたはlitellm-*.dist-infoを確認する。 -
findでcook.pth、cook.py、sitecustomize.pyを探す。Spotlightで見つからなくても検索を続ける。 -
sitecustomize.pyは解析記事のハッシュと中身を確認し、名前だけで判断しない。 - 2つの目印ファイル、
PRIVCOLLECT_ACTIVE=1、親がPID 1のPythonプロセスを調べ、PID 1ならコマンドラインも確認する。 - 1.107.0が入っていた場合はプロセスを止め、端末から読めた秘密情報の鍵やトークンを作り直す。