LiteLLM 1.107.0の改ざんで今やること:Pythonを起動しない確認手順と、原因になったgh CLIトークンの見直し方

LiteLLMの1.107.0に認証情報を盗むコードを混ぜた攻撃者は、PyPIに不正ログインしたわけではありません。LiteLLMの報告によると、使われたのはエンジニアのGitHub CLI(gh)のトークンでした。多くの開発者のターミナルにも、同じ種類のトークンが入っています。

LiteLLMを使っている人は、Pythonを起動せずに痕跡を確かめることを先にやってください。LiteLLMを使っていない人も、自分のghトークンを見直す理由ができました。GitHubは同じ10月21日に、ghのトークンを期限付きにする機能を公開しています。

目次

何が起きたか:正規の手順で、改ざん版が公開された

10月21日、PyPIのlitellm 1.107.0と公式Dockerイメージに不正なコードが入りました。影響を受けるのは次の場合です。時刻はUTCで、日本時間では9時間足します。

  • バージョンを固定せずに、11:39〜13:23(日本時間20:39〜22:23)にpipでlitellmを入れた
  • Dockerイメージのv1.107.0、latest、main-latest、main-stableを、11:46〜14:20(日本時間20:46〜23:20)に取得した

攻撃の流れは、LiteLLMの説明では次のとおりです。社内のログからghのOAuthトークンを入手し、ブランチ保護を外し、ハイフン1つ違いの偽リポジトリ(Berri-AI/litellm)を指すようにワークフローを書き換えました。そのうえで、正規のリリースの仕組みが改ざん版をビルドし、公開しています。Datadogの分析では、3人分のトークンでリリースの起動と承認を行い、実行記録まで消していました。トークンがどこから漏れたかの詳細は、調査が続いています。

LiteLLMの侵害は2回目です。3月の1.82.7と1.82.8でも、Pythonの起動時に自動で読み込まれる.pthファイルが使われました。今回のcook.pthとsitecustomize.pyも同じ仕組みで、litellmをimportしなくても、その環境でPythonを起動するたびに動きます。

影響を受けたかは、Pythonを起動せずに確かめる

LiteLLMの告知はpip show litellmでの確認を案内していますが、pip自体もPythonで動きます。改ざん版が入った環境でこれを打つと、そのたびに不正なコードが動きます。StepSecurityの解説も、Pythonを起動せずにファイルを探して消すよう勧めています。

次のコマンドはどれもPythonを使いません。macOSとLinuxのシェル向けです。私は自分のPCに、改ざん版が入った状態をまねたフォルダと正常なフォルダを作り、改ざん版の側だけが出力されることを確かめました。

find "$HOME" -path '*site-packages*' \( -name cook.pth -o -name 'litellm-1.107.0.dist-info' \) 2>/dev/null
ls -la /tmp/.pc_once_72129118c59f5636c0bb /tmp/remote_code_loader.lock 2>/dev/null
grep -rn -A1 --include=uv.lock --include=poetry.lock 'name = "litellm"' . | grep 'version = "1.107.0"'
grep -rn --include='requirements*.txt' 'litellm==1.107.0' .

1行目は、ホームフォルダ以下の仮想環境から改ざん版のファイルを探します。サーバーやコンテナでは"$HOME"を/に変えてください。私のMacではホームフォルダ全体で30秒ほどかかり、何も見つかりませんでした。2行目は、不正なコードが残す目印のファイルです。3・4行目はプロジェクトのフォルダで実行し、ロックファイルや依存関係の一覧に1.107.0が記録されていないかを見ます。

何も出なければ、その場所に改ざん版は入っていません。ただし、CIのジョブはすでに消えた環境で動いています。該当する時間帯にlitellmを入れたジョブがないか、CIの実行ログも見てください。Dockerは、LiteLLMが公表したイメージのdigest(sha256:1bbcb144…)と、手元のイメージを照らし合わせます。

入っていたら:消すより先に、届く鍵をすべて入れ替える

LiteLLMとStepSecurityが勧める手順は次のとおりです。

  1. /tmp/remote_code_loader.lockを開いているプロセスを止める。1時間ごとに外部からコードを取り込む常駐プロセスです
  2. site-packagesのcook.pth、cook.py、sitecustomize.pyと、/tmpの目印ファイルをシェルで削除する
  3. litellmを1.106.2に入れ直す。できれば仮想環境やイメージを作り直す
  4. そのマシンから読めた秘密をすべて入れ替える

大事なのは4つ目です。盗まれる対象は、LiteLLMに設定したAPIキーだけではありません。SSH鍵、AWSやGCPの認証情報、Kubernetesの設定、npmやPyPIやGitHubのトークン、ブラウザに保存したパスワードまで含まれます。LiteLLMはAWSのCloudTrailで不審なGetCallerIdentityの呼び出しを確かめるよう勧めています。OpenAI・Anthropic・Geminiのキーの止め方は、APIキーが流出したときの失効手順にまとめてあります。

原因になったghのトークンを、自分の端末で見直す

ここからはLiteLLMを使っていない人にも関係します。今回の入口になったghのOAuthトークンは、gh auth loginを一度実行すれば作られ、有効期限が付きません。私の端末でgh auth statusを実行すると、トークンはOSのキーチェーン(keyring)に保存され、権限はgist、read:org、repo、workflowでした。

注目したいのはworkflowです。GitHub Actionsのワークフローを書き換えられる権限で、今回の攻撃者がやったことそのものです。ワークフローのファイルを自分で編集しないなら、この権限は外せます。外すと.github/workflowsの変更をpushできなくなるので、必要になったら付け直します。

gh --version
gh auth status
gh auth refresh --remove-scopes workflow
gh auth login --expiring

4行目が、GitHubが10月21日に公開した期限付きトークンです。トークンに有効期限が付き、裏で自動的に更新されます。漏れても、いつまでも使えるわけではなくなります。毎回フラグを付けたくなければ、gh config set expiring_credentials enabledで既定にできます。GitHubは将来、新しいログインの既定にする予定だとしています。

このフラグはgh 2.103.0以降で使えます。私の端末のghは2.70.0で、gh auth login --helpに--expiringはまだありませんでした。1行目で版を確かめ、古ければ先に更新してください。--remove-scopesは2.70.0にもあり、repo、read:org、gistの3つは外せない仕様です。期限付きトークンへの切り替えは、私は版が古いため試せていません。

これまでに発行したトークンを確実に無効にするなら、GitHubの「Settings」→「Applications」→「Authorized OAuth Apps」でGitHub CLIの承認を取り消し(Revoke)、ログインし直します。GitHubの説明どおり、見覚えのない承認もここで一緒に消せます。

ログに出したトークンは、いずれ誰かに読まれる

LiteLLMの説明では、トークンは社内のログから取られました。CIでset -xを付けたままトークンを扱うスクリプトや、デバッグのために環境変数をまとめて出力する処理は、どこにでもあります。私は、ログに鍵が出ていないかを確かめる作業は、ライブラリの更新と同じくらい優先度が高いと考えています。

依存関係の側では、バージョンを固定してロックファイルで管理していれば、今回の被害はほぼ避けられました。pip install litellmのように版を指定しない入れ方は、2時間の公開時間に当たればそのまま感染します。社内のスクリプトやDockerfileに、版を固定していないpip installが残っていないかも、今日のうちに探しておくとよいです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメント一覧 (1件)

コメントする

目次