OpenSSL HTTP/3 中 CURLINFO_TLS_SSL_PTR 返回无效指针的安全漏洞分析

本文详细分析了 curl 在 OpenSSL HTTP/3 连接中 CURLINFO_TLS_SSL_PTR 返回无效 SSL 连接指针的问题,包括漏洞复现步骤、潜在影响(从拒绝服务到可能的远程代码执行),以及利用条件的复杂性讨论。

摘要

curl_easy_getinfo CURLINFO_TLS_SSL_PTR 在 OpenSSL HTTP/3 连接中返回无效的 SSL 连接指针。使用此 SSL 连接会导致崩溃,并可能产生其他影响。此问题在 libcurl 8.14.1 中未出现,表明错误可能在 libcurl 本身(或 libcurl 8.14.1 未触发其他支持库中的错误)。一些调试输出表明这可能是一个释放后使用/悬空指针问题。如果是这样,此问题可能导致远程代码执行。撰写本报告时尚未确认。

受影响版本

  • 8.15.0(发布版)
  • 8.15.1-DEV(7c23e88d17e0939b4e01c8d05f430e167e148f4b)

复现步骤

  1. 针对 OpenSSL 3.5.1 及足够新的 nghttp2、nghttp3 和 ngtcp2 编译 libcurl(–with-openssl –with-nghttp2 –with-ngtcp2 –with-nghttp3)
  2. 使用 -fsanitize=address 编译以下概念验证应用:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <curl/curl.h>
#include <openssl/ssl.h>
#include <stdio.h>

static size_t header_callback(char *buffer, size_t size,
                              size_t nitems, void *userdata)
{
  size_t actual = nitems * size;
  CURL *curl = (CURL *) userdata;
  size_t printactual = actual;

  if (printactual && buffer[printactual - 1] == '\n')
  {
    printactual--;
    if (printactual && buffer[printactual - 1] == '\r')
      printactual--;
  }
  printf("H: %.*s\n", (int) printactual, buffer);

  if ((actual == 1 && buffer[0] == '\n') ||
      (actual == 2 && buffer[0] == '\r' && buffer[1] == '\n'))
  {
    const struct curl_tlssessioninfo *info = NULL;
    CURLcode res = curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &info);
    printf("D: headers completed, res %d info %p\n", res, info);
    if (info && !res)
    {
      printf("D: backend %d internals %p\n", info->backend,  info->internals);
      if(CURLSSLBACKEND_OPENSSL == info->backend)
      {
        SSL *ssl = info->internals;
        printf("D: OpenSSL ver. %s\n", SSL_get_version(ssl));
        STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
        printf("D: chain %p\n", chain);
      }
    }
  }
  return actual;
}

int main(void)
{
  printf("libcurl version: %s\n", curl_version());

  CURL *curl = curl_easy_init();
  if (curl)
  {
    curl_easy_setopt(curl, CURLOPT_URL, "https://curl.se");
    curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_3ONLY);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, curl);
    curl_easy_perform(curl);
    curl_easy_cleanup(curl);
  }

  return 0;
}
  1. 执行 PoC:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
$ ./curlpoc
libcurl version: libcurl/8.15.1-DEV OpenSSL/3.5.1 zlib/1.3.1 brotli/1.1.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.5 nghttp2/1.66.0 ngtcp2/1.14.0-DEV nghttp3/1.1 OpenLDAP/2.6.10
H: HTTP/3 200
H: content-length: 11441
H: server: nginx/1.27.5
H: content-type: text/html
H: x-frame-options: SAMEORIGIN
H: last-modified: Sun, 20 Jul 2025 02:11:09 GMT
H: etag: "2cb1-63a52df596e87"
H: cache-control: max-age=60
H: expires: Sun, 20 Jul 2025 02:14:40 GMT
H: x-content-type-options: nosniff
H: content-security-policy: default-src 'self' curl.haxx.se www.curl.se curl.se; style-src 'unsafe-inline' 'self' curl.haxx.se www.curl.se curl.se; require-trusted-types-for 'script';
H: strict-transport-security: max-age=31536000
H: via: 1.1 varnish, 1.1 varnish
H: accept-ranges: bytes
H: date: Sun, 20 Jul 2025 04:44:47 GMT
H: age: 69
H: x-served-by: cache-bma-essb1270054-BMA, cache-hel1410021-HEL
H: x-cache: HIT, HIT
H: x-cache-hits: 1, 15
H: x-timer: S1752986688.638651,VS0,VE0
H: vary: Accept-Encoding
H: alt-svc: h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400
H:
D: headers completed, res 0 info 0x622000010600
D: backend 1 internals 0x61c000001080
D: OpenSSL ver. unknown
AddressSanitizer:DEADLYSIGNAL
=================================================================
==81895==ERROR: AddressSanitizer: SEGV on unknown address 0x0000000005c9 (pc 0x00010472785c bp 0x00016b6ed690 sp 0x00016b6ed690 T0)
==81895==The signal is caused by a READ memory access.
==81895==Hint: address points to the zero page.
    #0 0x00010472785c in SSL_get_peer_cert_chain+0x28 (libssl.3.dylib:arm64+0x1385c)
    #1 0x000104700c5c in header_callback+0x2d0 (curlpoc:arm64+0x100000c5c)
    #2 0x00010488a494 in cw_out_ptr_flush+0x10c (libcurl.4.dylib:arm64+0x1a494)
    #3 0x00010488a000 in cw_out_do_write+0xa8 (libcurl.4.dylib:arm64+0x1a000)
    #4 0x000104889c74 in cw_out_write+0x70 (libcurl.4.dylib:arm64+0x19c74)
    #5 0x00010488a870 in cw_pause_write+0x200 (libcurl.4.dylib:arm64+0x1a870)
    #6 0x0001048c6d1c in cw_download_write+0x94 (libcurl.4.dylib:arm64+0x56d1c)
    #7 0x0001048c59cc in Curl_client_write+0x48 (libcurl.4.dylib:arm64+0x559cc)
    #8 0x0001048a3180 in http_write_header+0x54 (libcurl.4.dylib:arm64+0x33180)
    #9 0x0001048a2068 in http_rw_hd+0x1598 (libcurl.4.dylib:arm64+0x32068)
    #10 0x00010489dfac in Curl_http_write_resp_hd+0x28 (libcurl.4.dylib:arm64+0x2dfac)
    #11 0x0001048f55ac in cb_h3_end_headers+0xe0 (libcurl.4.dylib:arm64+0x855ac)
    #12 0x00010497ecd8 in nghttp3_conn_read_bidi nghttp3_conn.c
    #13 0x00010497e4c4 in nghttp3_conn_read_stream nghttp3_conn.c:526
    #14 0x0001048f4790 in cb_recv_stream_data+0x4c (libcurl.4.dylib:arm64+0x84790)
    #15 0x000104a2c4dc in conn_recv_stream ngtcp2_conn.c:7103
    #16 0x000104a2b178 in conn_recv_pkt ngtcp2_conn.c:9077
    #17 0x000104a1fcc4 in ngtcp2_conn_read_pkt_versioned ngtcp2_conn.c:9855
    #18 0x0001048f5cb8 in recv_pkt+0x80 (libcurl.4.dylib:arm64+0x85cb8)
    #19 0x0001048f67c4 in vquic_recv_packets+0x12c (libcurl.4.dylib:arm64+0x867c4)
    #20 0x0001048f34cc in cf_ngtcp2_recv+0xe8 (libcurl.4.dylib:arm64+0x834cc)
    #21 0x0001048d5730 in Curl_sendrecv+0x254 (libcurl.4.dylib:arm64+0x65730)
    #22 0x0001048b96d8 in multi_runsingle+0x980 (libcurl.4.dylib:arm64+0x496d8)
    #23 0x0001048b8b68 in curl_multi_perform+0x200 (libcurl.4.dylib:arm64+0x48b68)
    #24 0x00010488db84 in curl_easy_perform+0x188 (libcurl.4.dylib:arm64+0x1db84)
    #25 0x00010470096c in main+0x84 (curlpoc:arm64+0x10000096c)
    #26 0x0001991feb94 in start+0x17b8 (dyld:arm64e+0xfffffffffff3ab94)

==81895==Register values:
 x[0] = 0x000061c000001080   x[1] = 0x0000000000000000   x[2] = 0x00000000000120a8   x[3] = 0x0000000000000002
 x[4] = 0x0000000104700fa0   x[5] = 0x000000016b6ed690   x[6] = 0x000000016af04000   x[7] = 0x0000000000000001
 x[8] = 0x0000000000000301   x[9] = 0x00000002076b8588  x[10] = 0x0000000000000002  x[11] = 0x0000010000000000
x[12] = 0x00000000fffffffd  x[13] = 0x0000000000000000  x[14] = 0x0000000000000000  x[15] = 0x0000000000000000
x[16] = 0x0000000104727834  x[17] = 0x00000002086fde70  x[18] = 0x0000000000000000  x[19] = 0x000000016b6ed6e0
x[20] = 0x0000000000000000  x[21] = 0x0000000000000002  x[22] = 0x000061c000001080  x[23] = 0x000000016b6ed6c0
x[24] = 0x0000000000000001  x[25] = 0x000000016b6ed6a0  x[26] = 0x000000702d6fdad4  x[27] = 0x0000007000020000
x[28] = 0x000000010470098c     fp = 0x000000016b6ed690     lr = 0x0000000104700c60     sp = 0x000000016b6ed690
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (libssl.3.dylib:arm64+0x1385c) in SSL_get_peer_cert_chain+0x28
==81895==ABORTING
Abort trap: 6

影响

摘要:

  • 至少:访问 HTTP/3 站点时导致拒绝服务。
  • 潜在:如果返回的指针指向已释放的内存,可能导致内存损坏。根据目标平台的不同,可能实现远程代码执行。需要进一步分析以确定完整影响。

支持材料/参考文献

本报告不含 AI 内容,供您愉快阅读。停止垃圾信息!

时间线活动

  • nyymi 提交报告给 curl。14 天前
  • nyymi 发布评论。14 天前
    • 讨论如何触发回调函数,并提到索尼在 PlayStation 设备中使用相同的浏览器引擎和 curl 后端。
  • nyymi 发布评论。14 天前
    • 更正 OpenSSL 3.5 中 SSL 实际类型为 struct ssl_connection_st。
  • nyymi 发布评论。14 天前
    • 分析漏洞影响,认为可能低于最初估计,但仍存在安全影响,如 SSL_get_verify_result 可能错误返回 X509_V_OK。
  • bagder (curl staff) 发布评论。14 天前
    • 指出使用 SSL_get_verify_result 的应用程序已经放弃安全性,此错误不会使其更糟。
  • nyymi 发布评论。14 天前
    • 发现可能的 RCE 向量,涉及 RECORD_LAYER 结构中的函数指针,但需要堆地址知识。
  • nyymi 发布评论。14 天前
    • 提供模拟成功构建假 SSL 对象的 PoC,导致代码执行。
  • jimfuller2024 (curl staff) 发布评论。13 天前
    • 确认在 8.15.0 上复现,并调查是否存在更简单的利用链。
  • nyymi 发布评论。13 天前
    • 指出 SSL_get_verify_result 与 RCE 向量无关。
  • nyymi 发布评论。13 天前
    • 识别不需要堆地址知识的回调函数 SSL srp_ctx.TLS_ext_srp_username_callback,但认为用户代码调用此函数的可能性很低。
  • jimfuller2024 (curl staff) 发布评论。12 天前
    • 确认理论利用有效,但适用性狭窄。
  • bagder (curl staff) 发布评论。12 天前
    • 指出 SRP 在 1.3/QUIC 中不工作,使得使用 unlikely。
  • nyymi 发布评论。12 天前
    • 同意此向量人工性强,继续寻找更多工作向量。
  • nyymi 发布评论。12 天前
    • 初步查看 WolfSSL,发现许多回调 behind 编译时标志,难以确定是否可到达。
  • bagder (curl staff) 发布评论。11 天前
    • 总结漏洞利用条件复杂,不认为这是现实世界安全问题。
  • nyymi 发布评论。11 天前
    • 同意利用条件复杂,安全影响有限。
  • nyymi 发布评论。11 天前
    • 分析 WolfSSL 向量困难,结构偏移高度可配置。
  • bagder (curl staff) 发布评论。7 天前
    • 计划在 24 小时内关闭此报告为信息性。
  • bagder (curl staff) 关闭报告并将状态更改为信息性。6 天前
  • bagder (curl staff) 请求披露此报告。6 天前
    • 根据项目透明政策,希望所有报告公开。
  • nyymi 同意披露此报告。6 天前
  • 报告已披露。6 天前

报告详情

  • 报告时间:2025 年 7 月 20 日 5:10 UTC
  • 报告者:nyymi
  • 报告对象:curl
  • 报告 ID:#3261310
  • 严重性:无评级 (—)
  • 披露时间:2025 年 7 月 28 日 22:48 UTC
  • 弱点:释放后使用
  • CVE ID:无
  • 奖金:无
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计