libcurl HTTP/2 多路复用状态隔离失效漏洞深度剖析

本文详细分析了 libcurl 在处理 HTTP/2 多路复用连接时存在的一个关键设计缺陷,该缺陷导致不同请求流之间共享认证状态,可能使得未授权请求绕过认证,威胁应用安全。

漏洞概述

漏洞名称: 多路复用连接中的状态隔离失效(共享认证上下文) 受影响产品: libcurl 受影响版本: v7.43.0 至当前版本 (v8.x) - 所有支持 HTTP/2 多路复用的版本 严重等级: 严重 (CVSS: 9.1)

执行摘要

libcurl 在管理 HTTP/2 多路复用连接的状态时存在一个根本性的设计缺陷。库违反了“Easy Handle 隔离”原则,将认证状态(特别是 NTLM/Negotiate 上下文)存储在共享的连接对象(Connection Object) 上,而非独立的流(Stream/Easy Handle) 上。

这种违规操作导致,如果次要的、未经认证的 Easy Handle(“攻击者流”)与一个已获得特权的主 Easy Handle(“管理员流”)共享同一个物理 TCP 连接,前者将能够有效地“继承”后者的认证上下文。虽然这通常由竞态条件或特定的流控制状态(“Ouroboros”)触发,但漏洞根源在于 不变量的违背:认证状态 ⊂ 连接,而非认证状态 ⊂ 流。

这不是一个使用错误。用户在使用 HTTP/2 时无法选择退出这种状态共享,使得该库默认对于经过认证的多路复用流量是不安全的。

技术链分析

漏洞利用依赖于结构性的状态隔离失败:

  • 缺陷 A: 不变量违背 (STATE-001)

    • 位置: lib/url.c / lib/transfer.c
    • 描述: conn->ntlmconn->negotiate 结构体附加在 connectdata 对象上。在 HTTP/1.1(1:1 映射)中这是可接受的。在 HTTP/2(1:N 映射)中,这意味着所有 N 个流都错误地共享了同一个认证机状态。
  • 缺陷 B: 时序触发器 (RACE-001)

    • 位置: lib/multi.c
    • 描述: 要武器化共享状态,攻击者需要“管理员”流保持连接开放并已认证,但不消耗响应。CURL_READFUNC_PAUSE 或网络反压状态允许一个流被“无限期暂停”,创建一个稳定的时间窗口,在此期间 conn->ntlm.state 处于 AUTHENTICATED 状态,可供任何新流利用。

概念验证

已附上一个功能性的 C 程序 (ouroboros_poc.c)。零准备: PoC 使用标准的 curl_multi_add_handle 调用。不需要内存破坏或特殊载荷。它简单地证明了句柄 B(无凭据)从受保护端点返回 200 OK,因为句柄 A(有凭据)在同一连接上处于活动状态。

预期反驳与回应

  • 反驳 A: “NTLM/Negotiate 是面向连接的协议,不了解多路复用。”

    • 回应: 同意。然而,如果 libcurl 选择在 HTTP/2(一种多路复用传输协议)上支持这些协议,它就承担了强制执行句柄隔离不变量的责任。如果协议无法区分流(如 NTLM),libcurl 必须 要么:
      1. 阻止该连接的多路复用(降级为 1:1)。
      2. 将该连接独占锁定给已认证的句柄。 允许“状态渗透”是因为底层协议是遗留协议,这是抽象层(libcurl)的失败,而非协议本身。
  • 反驳 B: “这是一个应用程序逻辑错误。应用程序不应在一个 Multi 句柄中混合用户。”

    • 回应: 错误。CURLM(Multi)接口的设计目的是在物理层面管理连接池,同时在逻辑上向应用程序呈现分离的 CURL(Easy)句柄。应用程序开发者无法精细控制哪些 Easy 句柄映射到 HTTP/2 上下文中的哪个物理 TCP 连接。libcurl 在内部管理此映射。因此,开发者无法通过应用程序逻辑(除了全局禁用 HTTP/2)来防止这种竞态条件。缺陷在于内部的连接池管理。

影响

影响分析 (CVSS 9.1)

  • 机密性: 高。 凭据/会话在句柄边界间泄露。
  • 完整性: 高。 请求被错误地授权。
  • 可用性: 无(在此上下文中)。
  • 攻击途径: 网络 (AV:N)。
  • 攻击复杂度: 低 (AC:L) - HTTP/2 上的默认行为。
  • 所需权限: 无 (PR:N)。
  • 用户交互: 无 (UI:N)。
  • 范围: 未改变 (S:U) - (保守评分以避免被拒绝,严格影响依赖 curl 的应用程序)。
  • CVSS v3.1 分数: 9.1 (严重) CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

后续讨论摘要

在报告提交后,libcurl 维护者与漏洞提交者 raulvdv 进行了深入的讨论。

  1. 维护者质疑报告所描述问题的实际可行性,并要求澄清核心问题。raulvdv 简要重申了设计缺陷:认证状态存储在共享的连接结构上,而非独立的句柄上,导致多路复用时未认证的句柄可以继承认证状态。

  2. 在关于 NTLM 协议本身限制的讨论中,raulvdv 引用了 RFC 9113 (HTTP/2) 的相关章节来说明流应是独立的,并指出浏览器(Chrome/Firefox)处理此类冲突的标准做法是强制降级到 HTTP/1.1。他认为 libcurl 的问题在于试图支持这种无效的组合(NTLM + HTTP/2 多路复用),而未进行降级或序列化连接。

  3. 维护者 bagder 质疑 PoC 未提及 NTLM,并要求展示针对真实服务器的漏洞利用。这促使 raulvdv 重新深入审查代码。

  4. 关键转折点: raulvdv 在重新审查代码后发现,对于 NTLM 认证,libcurl 在 lib/http.c 中已有保护机制:当检测到 HTTP/2 连接上使用 NTLM 时,会显式关闭连接并强制降级到 HTTP/1.1,并且在重用连接时会进行凭据匹配。这有效地防止了他最初描述的竞态条件。

  5. 然而,他发现对于同样属于连接导向认证的 Negotiate (Kerberos/SPNEGO),libcurl 似乎缺乏类似的降级保护和连接重用时的凭据匹配机制。conn->http_negotiate_state 存储在共享的 connectdata 结构上,且 HTTP 的 PROTOPT_CREDSPERREQUEST 属性会导致 url_match_auth() 跳过凭据验证,这让他担心在 HTTP/2 多路复用活跃时可能存在状态共享风险。

  6. 维护者 bagder 回应指出,如果一个服务器正确地提供了面向连接的认证方法,它也无法可靠地在 HTTP/2 上实现,因此不认为这是一个实际的安全问题,并将报告状态更改为 Not Applicable(不适用)。不过,他承认可以在 Negotiate 认证部分添加相关安全注意事项的文档。

  7. 报告最终被公开披露,以遵循项目的透明性原则。

报告元数据

  • 报告 ID: #3487952
  • 报告者: raulvdv
  • 报告日期: 2026年1月5日 UTC 22:13
  • 披露日期: 2026年1月8日 UTC 12:57
  • 状态: 已披露,标记为“不适用”
  • 漏洞类型: 数据元素暴露给错误的会话
  • CVE ID:
  • 赏金:
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计