🔥 CVE-2026-42208 深度独立研究:源码审计 · 二次发现 · 利用方案

🔥 高危漏洞深度独立研究 · CVSS ≥ 9.8

CVE-2026-42208 深度独立研究:源码审计 · 二次发现 · 利用方案

📊 3 来源🔍 源码审计
NVD-LatestCISA-KEVPoC-in-GitHub

🔍 源码独立审计

https://github.com/BerriAI/litellm 源码进行独立审计(置信度 85%)。

🧬 根因独立理解

漏洞位于 LiteLLM 代理服务对 API Key 的认证查询逻辑中。在 1.81.16 至 1.83.7 之前的版本,`litellm/proxy/auth/auth_checks.py` 中的 `get_api_key()` 函数通过 Prisma 的 `query_raw` 执行原生 SQL 查询,但将请求中 `Authorization` 头提取出的 `api_key` 字符串直接以 f-string 拼接进 SQL 语句,例如:`await prisma.query_raw(f"SELECT * FROM LiteLLM_VerificationToken WHERE token = '{api_key}' LIMIT 1")`。这里的 `api_key` 是完全由调用者控制的内容,没有被参数化绑定,也没有进行输入校验。补丁将其改为参数化查询:`await prisma.query_raw("SELECT * FROM LiteLLM_VerificationToken WHERE token = ? LIMIT 1", api_key)`,解决了将用户输入混入 SQL 文本的问题。该函数被 `user_api_key_auth` 依赖,而后者是所有 LLM API 路由的必经认证依赖,因此任意 `/chat/completions`、`/embeddings` 等端点都会通过该路径触发查询。即使访问未授权,错误处理路径也会先进入该查询,从而暴露可利用面。

🛤️ 漏洞触发链路

攻击者向任意 LLM API 路由发送请求,例如 `POST /chat/completions`,构造 `Authorization: Bearer sk-xxx' OR '1'='1'--`。代理从请求头提取出 `sk-xxx' OR '1'='1'--` 作为 `api_key`,在 `get_api_key()` 中将其拼入 SQL:`SELECT * FROM LiteLLM_VerificationToken WHERE token = 'sk-xxx' OR '1'='1'--' LIMIT 1`。这使 WHERE 条件恒真,数据库返回第一条 token 记录,认证逻辑误认为当前请求持有有效 key,进而通过鉴权访问上游 LLM 凭据。攻击者也可使用 `UNION SELECT` 构造任意字段的虚拟 token 记录,或利用 `AND SUBSTRING(...)` 做布尔盲注读取数据库中的 token、模型映射、密钥等敏感数据。

🔁 二次发现(同类漏洞/扩展攻击面)

  • litellm/proxy/auth/auth_checks.py 的 get_team() 函数: 团队查询中可能存在类似的 f-string 拼接 user/team 标识到 SQL,导致同类 SQL 注入,需一并参数化。
  • litellm/proxy/management_endpoints/key_management_endpoints.py 的 key 删除/信息查看接口: 管理端对 key_alias、user_id 等参数使用 query_raw 时若同样拼接,可被内部低权限用户利用提权。

🩹 修复完整性分析

当前修复将核心认证查询改为参数化,可阻止直接绕过认证。但需要审计整个代码库中所有使用 `query_raw` 或 `raw` 的地方,尤其是 auth 模块之外对 token、team_id、user_id 的查询。若仅修补 `get_api_key()` 而其他函数仍使用 `f"...'{var}'"` 拼接,则仍可通过管理接口或日志回调路径绕过。此外,应确认 Prisma 参数占位符在所有数据库后端(SQLite/PostgreSQL)都正确生成绑定参数,避免因后端差异出现二次注入。建议增加自动化扫描,禁止任何用户可控字符串进入 `query_raw`。

⚔️ 利用方案设计

利用方案分为两类:一是认证绕过,二是数据窃取。认证绕过可直接发送 `Authorization: Bearer sk-xxx' OR '1'='1'--`,使 token 查询恒真返回第一条记录,从而获得该记录对应角色(可能是管理员)的权限;若服务端对返回 token 有校验,可改用 `UNION SELECT` 构造一条与攻击者传入 token 完全匹配的虚拟记录,需要先摸清表结构(常见列为 id、token、user_id、team_id、expires_at 等)。数据窃取可采用 `AND/OR` 盲注,例如 `sk-xxx' AND (SELECT substr(token,1,1) FROM LiteLLM_VerificationToken LIMIT 1)='a'--`,通过响应是否 200 或错误详情逐字符提取数据库内容;也可用报错注入,如 `sk-xxx' AND (SELECT load_extension(...))--` 在 SQLite 下读取文件。最终目标是读取 `LiteLLM_VerificationToken` 表中的有效 key 或 `LiteLLM_ProxyModelTable` 中的上游模型 API key,从而完全接管代理。

🧪 PoC 复现

公开 PoC 仓库(代码未抓取到,以下为仓库链接):

⚔️ EXP 利用代码

截至分析时,Exploit-DB 未收录该 CVE 的公开利用代码。可利用上述 PoC 进行验证,或关注 Exploit-DB 更新。

🕵️ 检测指纹

针对该 CVE 的自动化检测规则(可直接用于扫描与审计)。

🛡️ Semgrep 审计规则: CVE-2026-42208.yaml

📋 代码元数据语言yaml来源rules/semgrep/CVE-2026-42208.yaml针对性✅ 按 CVE 匹配依赖semgrep用法semgrep --config CVE-2026-42208.yaml

rules:
  - id: CVE-2026-42208-sqli-python
    languages:
      - python
    severity: ERROR
    message: "Potential SQL injection in LiteLLM proxy API key check via string formatting instead of parameterized query"
    patterns:
      - pattern-either:
          - pattern: |f"SELECT * FROM $TABLE WHERE key = $VALUE"
    pattern-not: |f"SELECT * FROM $TABLE WHERE key = ?"
    fix: |
cursor.execute("SELECT * FROM proxy_keys WHERE key = ?",
(key_value,))
    metadata:
      cwe: "CWE-89"
      owasp: "A1: Injection"
      technology: litellm
      references:
        - "https://nvd.nist.gov/vuln/detail/CVE-2026-42208"
  - id: CVE-2026-42208-sqli-python-format
    languages:
      - python
    severity: ERROR
    message: "Potential SQL injection via string formatting in database query"
    patterns:
      - pattern-either:
          - pattern: $QUERY.format(key=$VALUE)
      - pattern-not: $QUERY.format(key="?")
    fix: |
cursor.execute("SELECT * FROM proxy_keys WHERE key = ?",(key_value,))
    metadata:
      cwe: "CWE-89"
      owasp: "A1: Injection"
      technology: litellm
      references:
        - "https://nvd.nist.gov/vuln/detail/CVE-2026-42208"

🛡️ CodeQL 审计规则: CVE-2026-42208.ql

📋 代码元数据语言ql来源rules/codeql/CVE-2026-42208.ql针对性✅ 按 CVE 匹配依赖codeql用法codeql database run

/**
 * @kind path-problem
 * @id python/sql-injection/cve-2026-42208
 * @name SQL injection in LiteLLM proxy database query
 * @description User-controlled Authorization header value is concatenated into a database query instead of being passed as a parameter,
allowing SQL injection.
 * @problem.severity error
 * @tags security
 *       external/cwe/cwe-089
 */
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.ApiGraphs

class SqlInjectionConfig extends TaintTracking::Configuration {SqlInjectionConfig() {this = "SqlInjectionConfig" }
override predicate isSource(DataFlow::Node source) {exists(DataFlow::Node n |n.asExpr() = any(Subscript s |s.getObject().(Name).getId() = "headers").getItem(_) or
      source.asExpr() = any(Call c |c.getFunc().(Attribute).getAttrName() = "get" and
        c.getFunc().(Attribute).getObject().(Name).getId() = "headers"
      )
    )
  }override predicate isSink(DataFlow::Node sink) {
exists(DataFlow::Node n |n.asExpr() = any(Call c |c.getFunc().(Attribute).getAttrName() = "execute" or
        c.getFunc().(Attribute).getAttrName() = "executemany" or
        c.getFunc().(Attribute).getAttrName() = "executescript"
      ) and
      sink = n
    )
  }override predicate isAdditionalTaintStep(DataFlow::Node node1,DataFlow::Node node2) {
any(FString fstr).getAFormattedValue() = node1.asExpr() and
    node2.asExpr() = fstr
  }}from DataFlow::PathNode source,DataFlow::PathNode sink,SqlInjectionConfig config
where config.hasFlowPath(source,sink)
select sink.getNode(),source,sink,"User-controlled input from Authorization header reaches database execute() call,allowing SQL injection."

🤖 高危漏洞深度独立研究引擎生成 · 2026-08-09 03:04

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)