深入剖析cURL中的HTTP/2与HTTP/3响应头注入漏洞

本文详细披露了一个存在于cURL中的高严重性安全漏洞。该漏洞源于cURL在HTTP/2与HTTP/3协议处理中,对来自恶意服务器的响应头缺少关键字符验证,导致可实施响应头注入与HTTP响应拆分攻击,影响多个后端实现。

cURL中的HTTP/2与HTTP/3响应头注入漏洞分析报告 #3481849

================================================================================ 漏洞报告:cURL中的HTTP/2和HTTP/3响应头注入 漏洞类型: 响应头注入 / HTTP响应拆分 严重性: 高危(CVSS 7.5+) 受影响版本: cURL最新版(截至2025-12-29) 状态: 0-DAY(在http2.c、curl_ngtcp2.c、curl_osslq.c中未修复)

摘要 cURL的HTTP/2和HTTP/3协议处理器中存在一个关键的响应头注入漏洞。当从恶意服务器接收HTTP/2或HTTP/3响应头时,cURL会直接将头名和头值拼接到HTTP/1样式的头字符串中,而不会验证其中是否包含回车符(\r)、换行符(\n)或空字节(\0)。此漏洞已于2025-12-27在提交6842d4e中仅对QUICHE HTTP/3后端进行了部分修复。其余三个后端仍然存在漏洞:

  • lib/http2.c - 通过nghttp2实现的HTTP/2(有漏洞
  • lib/vquic/curl_ngtcp2.c - 通过ngtcp2实现的HTTP/3(有漏洞
  • lib/vquic/curl_osslq.c - 通过OpenSSL QUIC实现的HTTP/3(有漏洞

================================================================================ 技术细节 易受攻击的代码模式:

HTTP/2 (lib/http2.c, 第1705-1717行):

1
2
3
4
5
6
7
8
9
/* 转换为HTTP/1样式的头 */
curlx_dyn_reset(&ctx->scratch);
result = curlx_dyn_addn(&ctx->scratch, (const char *)name, namelen);  // 未验证
if(!result)
  result = curlx_dyn_addn(&ctx->scratch, STRCONST(": "));
if(!result)
  result = curlx_dyn_addn(&ctx->scratch, (const char *)value, valuelen);  // 未验证
if(!result)
  result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));

HTTP/3 via ngtcp2 (lib/vquic/curl_ngtcp2.c, 第1220-1229行):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curlx_dyn_reset(&ctx->scratch);
result = curlx_dyn_addn(&ctx->scratch,
                        (const char *)h3name.base, h3name.len);  // 未验证
if(!result)
  result = curlx_dyn_addn(&ctx->scratch, STRCONST(": "));
if(!result)
  result = curlx_dyn_addn(&ctx->scratch,
                          (const char *)h3val.base, h3val.len);  // 未验证
if(!result)
  result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));

HTTP/3 via OpenSSL QUIC (lib/vquic/curl_osslq.c, 第873-887行):

1
2
3
result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);  // 未验证
// ...
result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);    // 未验证

已修复的代码 (curl_quiche.c, 提交6842d4e):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
static bool is_valid_h3_header(const uint8_t *hdr, size_t hlen)
{
  while(hlen--) {
    switch(*hdr++) {
    case '\n':
    case '\r':
    case '\0':
      return FALSE;
    }
  }
  return TRUE;
}

// 现在在使用前进行验证:
if(is_valid_h3_header(value, value_len) &&
   is_valid_h3_header(name, name_len)) {
  // ... 处理头
}

================================================================================ 攻击场景

  1. 客户端 使用HTTP/2或HTTP/3连接到恶意服务器
  2. 恶意服务器 发送一个带有精心构造响应头的HTTP/2或HTTP/3响应:
    • 头名: "X-Injected\r\nSet-Cookie: session=evil"
    • 头值: "foo"
    • 头名: "X-Normal"
    • 头值: "foo\r\nX-Injected-Header: malicious-value"
  3. cURL 在未经验证的情况下将其转换为HTTP/1样式的头:
    • 结果: "X-Injected\r\nSet-Cookie: session=evil: foo\r\n"
  4. 处理这些头的应用程序可能会:
    • 接受注入的Cookie(Cookie注入)
    • 将注入的头视为合法头进行处理
    • 如果被用作代理/CDN,可能导致缓存中毒
    • 安全头(如CSP、HSTS)被绕过

================================================================================ 可利用条件 必要条件:

  • 受害者必须使用HTTP/2或HTTP/3连接到攻击者控制的服务器
  • 应用程序必须处理cURL返回的响应头
  • 攻击者能够控制服务器端的HTTP/2或HTTP/3实现

攻击向量:

  • 中间人攻击,篡改HTTP/2/3响应
  • 已沦陷的服务器发送恶意响应头
  • 恶意重定向至攻击者的HTTP/2/3服务器

================================================================================ 影响评估

  • Cookie注入: 攻击者可设置任意Cookie
  • 缓存中毒: 存储的响应可能包含注入的头
  • 安全头绕过: CSP、HSTS等安全策略可能被操纵
  • 响应拆分: 经典的HTTP响应拆分攻击
  • 信息泄露: 通过错误信息中的精心构造头

注意: HTTP/2有额外的风险因素:

  • lib/http2.c第460行:nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
  • 禁用了nghttp2中对RFC 9113响应头格式的验证!

================================================================================ 建议的修复方案 将应用于curl_quiche.c(提交6842d4e)的相同验证逻辑应用到以下文件:

  • lib/http2.c - 在第1705行前添加is_valid_header()检查
  • lib/vquic/curl_ngtcp2.c - 在第1220行前添加is_valid_h3_header()检查
  • lib/vquic/curl_osslq.c - 在第873行前添加is_valid_h3_header()检查

建议的补丁:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// 添加到每个文件:
static bool is_valid_header(const uint8_t *hdr, size_t hlen)
{
  while(hlen--) {
    switch(*hdr++) {
    case '\n':
    case '\r':
    case '\0':
      return FALSE;
    }
  }
  return TRUE;
}

// 然后在处理前进行验证:
if(!is_valid_header(name, namelen) || !is_valid_header(value, valuelen)) {
  // 返回错误或跳过该头
  return NGHTTP2_ERR_CALLBACK_FAILURE;  // 对于HTTP/2
}

================================================================================ CVE分配 此漏洞应被分配一个CVE标识符。

================================================================================ 时间线

  • 2025-12-27: 为QUICHE后端提交了部分修复(提交6842d4e
  • 2025-12-29: 在HTTP/2及其他HTTP/3后端中发现相同漏洞
  • 2025-12-29: 本报告生成 ================================================================================ 研究员 通过静态分析cURL源代码仓库发现。
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计