从 RST_STREAM 到 UAF:curl 8.23.0 里唯一的 High —— CVE-2026-92392
- 安全公告:
- 漏洞总览:
- 原始报告(HackerOne):
- 引入提交:
- 修复提交:
漏洞
curl 在做上传时,如果服务端在传输过程中发来 HTTP/2 的 RST_STREAM,且原因码为 HTTP_1_1_REQUIRED 或 REFUSED_STREAM,curl 的处理有问题,最终导致 struct Curl_easy 对象在被释放后继续被使用(use-after-free)。
- CWE-416: Use After Free
- 严重级别:High
- 模块:HTTP/2
- 影响 curl 命令行工具
利用条件
这个 UAF 已被证明在恶意服务端同时满足一组特定条件时可以转成远程代码执行:
- 已知传输所用的精确 curl 版本、对象与结构体布局,并据此推导攻击偏移;
- 已知目标机上运行中的 curl 的精确架构细节;
- 已知且兼容的 libc 分配器构建与堆布局;
- nghttp2 与 OpenSSL/TLS 1.3 的行为,包括 session ticket 附近的分配影响;
- 固定的 CA bundle,以及其他依赖输入的分配几何;
- 需要两个并行传输,且它们的调度与时序保持不变;
- 正确标定的 libc 泄漏与符号偏移;
- exploit 目前的短命令约束(约 23 字节)。
也就是说,触发崩溃面很窄,但真正打成 RCE 需要凑齐上面这一长串前提,属于条件苛刻的攻击,并非碰到就能打。
影响版本
| 状态 | 版本 |
|---|---|
| 受影响 | curl 8.12.0 至 8.22.0(含) |
| 不受影响 | curl = 8.23.0 |
| 不受影响 | 8.20.X 分支 >= 8.20.2 |
| 不受影响 | 8.16.X 分支 >= 8.16.2 |
| 不受影响 | 8.14.X 分支 >= 8.14.3 |
先确认自己的上传走的是不是 HTTP/2
只有 HTTP/2 路径才会碰到这段代码,先确认传输实际协商到的协议版本。
看 curl 是否编译进了 HTTP/2 支持,Features: 行里要有 HTTP2:
$ curl --version
curl 8.22.0 (x86_64-pc-linux-gnu) libcurl/8.22.0 OpenSSL/3.5.0 nghttp2/1.68.0
Release-Date: 2026-09-02
Protocols: dict file ftp ftps gopher gophers http https imap imaps ...
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTP3 IDN ...
看某次上传实际用的是哪个版本,用 -w '%{http_version}':
$ curl -w '%{http_version}\n' -o /dev/null -T payload.bin https://example.com/upload
2
输出 2 表示这次传输走的就是 HTTP/2,1.1 则没有进入该路径。
libcurl 里通过 CURLOPT_HTTP_VERSION 控制:
/* 只在 TLS 上协商 h2,明文仍用 1.1(命令行默认的取向) */
curl_easy_setopt(h, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
/* 直接按 HTTP/2 发起 */
curl_easy_setopt(h, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
/* h2c,不经 ALPN 直接讲 HTTP/2 */
curl_easy_setopt(h, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
CURL_HTTP_VERSION_NONE 表示交给库去协商:HTTPS 下按 ALPN 结果决定,服务端选中 h2 就会用 HTTP/2。所以「我没显式设成 2」不等于没在用 HTTP/2,还是以 %{http_version} 的实测输出为准。
为什么是「上传」才触发
触发点要求客户端正在发送请求体时收到对当前流的 RST_STREAM。这类拒绝发生在传输进行中——服务端不等请求体收完就回一个 HTTP_1_1_REQUIRED(要求降级到 HTTP/1.1 重试)或 REFUSED_STREAM(该流未被处理,可安全重试),curl 便进入流关闭与重试的处理逻辑。
只有带请求体的传输才可能走这条路径:-T/--upload-file、--data、--data-binary 这些请求都会发送 body;纯下载不发送请求体,不构成上述时序。另外前面列出的利用条件里有「两个并行传输」,说明多路复用下流的调度与 easy handle 的生命周期交织,也是这条路径复杂的地方。
修复与缓解
- A:升级到 curl 8.23.0;留在旧分支的按对应修复版本走:8.20.2 / 8.16.2 / 8.14.3。
- B:把补丁打到本地版本上(修复提交见文首链接)。
- C:不使用 HTTP/2。命令行加
--http1.1即可,libcurl 里设CURLOPT_HTTP_VERSION为CURL_HTTP_VERSION_1_1。
时间线
- 2026-09-15:问题报告给 curl 项目。
- 2026-10-14:curl 8.23.0 发布,与本公告同步公开。
报告人:Luna with Claude, Anthropic;补丁作者:Stefan Eissing。
同批次的其他漏洞
curl 8.23.0 在 2026-10-14 一次性公开了 24 个漏洞。同一批里比较值得注意的还有:
- CVE-2026-92390:
-J的 no-clobber 保证被 103 Early Hints 绕过(工具) - CVE-2026-92391:
curl_mime_free递归导致栈溢出(库) - CVE-2026-94265:
Authorization: Negotiate被发往外部 origin - CVE-2026-94266:
CURLINFO_EFFECTIVE_URL中的密码泄漏 - CVE-2026-86410:重定向到 SFTP 或 SCP 绕过主机校验(工具)
- CVE-2026-87864:IMAP APPEND 配合 CRLF 转换造成命令注入
- CVE-2026-107929:本地接口连接复用错配
上一批随 curl 8.22.0(2026-09-02)发布的还有 CVE-2026-80229(OpenSSL provider 的 use-after-free,影响 8.14.0–8.21.0,规避手段是 CURLOPT_FORBID_REUSE)与 CVE-2026-80230(OpenSSL pinning 绕过:同时设置 CURLOPT_PINNEDPUBLICKEY 并关闭 CURLOPT_SSL_VERIFYPEER、CURLOPT_SSL_VERIFYHOST 时,服务端不提供证书也能通过 pin 校验)。
来源:、