漏洞
OpenSSL:重传握手,为什么会带出别的内存
OpenSSL 的 CVE-2026-84782 出在 DTLS 握手重传:程序换了要发送的消息,却沿用上次写到的位置,可能把堆内存发给对端或使进程崩溃,使用 DTLS 的应用应更新库,并检查暂停后的发送能否恢复。

文章导航
网络不可靠,消息没送到,再发一次。这是通信程序每天都在做的事。可如果上一条消息只发了一半,程序还记着“下次从这里继续”,重传又来借用同一块内存,会发生什么?OpenSSL 这次修补的错误就藏在两次发送交接的地方:消息换了,读取位置却留了下来。
9 月 29 日,OpenSSL 披露高危漏洞 CVE-2026-84782。受影响的是 DTLS 握手发送过程,可能把进程堆内存作为握手数据发给对端,也可能读到无效地址而崩溃。补丁做了两件事:每次重传从消息开头读起,以及在原来的发送尚未完成时暂缓重传。
1 先找到真正使用 DTLS 的程序
OpenSSL 为很多程序提供加密通信能力,DTLS 则把类似 TLS 的保护用于数据报通信,通常运行在 UDP 上。UDP 不替握手消息保证按顺序送达,DTLS 需要自己安排分片、等待和重传。本次出错的是这条路径;普通 HTTPS 使用的 TCP/TLS 发送过程不因此自动受影响。客户端和服务器都可能发送较大的握手消息,两端都在检查范围内。OpenSSL 列出的 FIPS 密码模块本身不受影响,出错代码在模块之外;使用这些模块的应用仍可能调用受影响的 DTLS 代码。
具体触发条件还包括一次写入中途暂停。OpenSSL 用 SSL_ERROR_WANT_WRITE 告诉应用:底层暂时接收不了更多数据,稍后继续。应用保留连接,重传计时器却可能先到期。如果计时器此时重建另一条握手消息,重传会改动暂停写入仍要使用的共享状态。下面沿 3.5.8 的 dtls1_retransmit_message() 与 dtls1_do_write() 说明这次交错。
已有受支持分支应先安装对应修复:4.0.3、3.6.5、3.5.9 或 3.4.8;它们同时是截至 10 月 1 日核对到的各分支最新稳定版。发行版可能用更低的上游版本号回补,具体包号在第 4 节。暂时无法更新的 DTLS 服务,可以按产品支持方式暂停该入口或限制到确有需要的对端,但会影响依赖它的业务,允许访问的对端仍能到达旧代码。永久处理需要修复库或承载它的产品。
OpenSSL 将问题评为 High,归为越界读取 CWE-125。截至 10 月 1 日 01:20 UTC,CISA 的最新 KEV 目录未收录此项,NVD 尚待分析,页面上的 CVSS 3.1 8.2 分来自 CISA-ADP;已确认使用受影响 DTLS 代码的服务仍应更新。
2 缓冲区装进了 A,位置还停在 B
先看一次正常发送。init_buf 保存正在写出的握手消息,init_off 是当前发送位置,init_num 是尚待发送的长度。消息较大时,3.5.8 的 dtls1_do_write() 根据 MTU,也就是路径能够承载的数据包大小,切出合适的片段。每成功写出一片,就向前移动位置、扣掉相应长度;暂时写不出去时,它返回,让应用之后从保留的位置继续。
与此同时,OpenSSL 为重传保留了一份握手消息队列。队列名叫 sent_messages,不过消息在构造结束时就已入队,并不要求全部分片已经发完,更不表示对端确认收到了它。因此,发送与重传操作能够碰到同一条尚未完成的消息,也能够碰到同一轮握手中的不同消息。
用上游服务器测试的情境来理解:短的 ServerHello 已经发出,随后较长的 Certificate 只发出第一片,写入便暂停了。此时共享缓冲区还装着证书消息,发送位置也停在证书中间。计时器到点后,重传逻辑取出队列前面的消息,把它复制回缓冲区开头,并重新设置待发送长度。问题出在这几行旧代码:它没有同时把 init_off 归零。
memcpy(s->init_buf->data, frag->fragment,
frag->msg_header.msg_len + header_length);
s->init_num = frag->msg_header.msg_len + header_length;
于是,同一组状态里出现了两个不同来源:缓冲区开头和长度属于重传消息 A,位置却继承了暂停消息 B。程序随后进入 dtls1_do_write(),按这个位置重写分片头,再把&s->init_buf->data[s->init_off] 交给记录层。记录层继续用传入的指针和长度组装待发送记录。消息标识看上去来自 A,实际取出的正文却可能是 B 留在后面的字节,甚至越过已分配缓冲区的末尾。

官方确认的后果包括堆内容以明文握手数据发给对端,以及读取未映射区域造成拒绝服务。能读到哪些内容、多少内容,取决于当时的消息和内存状态;公告没有给出可据此确认私钥泄露的结论。
3 位置归零以后,暂停的发送仍要接得上
第一处补丁很短。修复提交 906cf0ef 在复制消息、设置长度之后加上 s->init_off = 0;,让重传从正确的开头开始。原来的证书消息还在等着继续发送,它原先的进度怎么办?
旧函数有一段“保存与恢复状态”的代码,保存的是记录层对象和方法,用于采用原消息对应的发送状态。暂停写入依赖的 init_buf、init_off、init_num 和握手消息头没有一起保存。重传成功结束时,发送函数还会把位置和剩余长度清零。应用再次调用 SSL_accept()、SSL_connect()、SSL_read() 或 SSL_write() 时,仍要续写原来的握手消息;证书示例中的内容、位置和剩余长度,却已被重传改动。
握手状态机仍知道自己停在发送阶段。WRITE_STATE_SEND 分支会再次执行写入,而消息长度和头部已经对不上。官方说明,调试构建可能在这里触发断言并终止进程。这是另一项后果:即使一次重传恰好没有越界读,原来的发送也可能被它破坏。
第二处补丁因此放在超时处理函数 dtls1_handle_timeout()。只要握手仍处于写入流程,且尚未回到允许转入下一步的状态,本轮就先不重传:
if (s->statem.state == MSG_FLOW_WRITING
&& s->statem.write_state != WRITE_STATE_TRANSITION)
return 0;
计时器在这项检查之前已经重新安排,暂停的写入则留给应用下一次调用去继续。重传等当前发送让出共享状态,得到机会后再从位置零开始。
随补丁新增的两项回归测试专门卡住这次交接。客户端测试把 ClientHello 做大,服务器测试让 Certificate 分片,两者都用一个受控的输入输出层让写入停在 WANT_WRITE。接着先解除这个输出层的写入限制,再连续处理三次超时,并检查底层写调用次数没有增加。此时测试装置已经允许发送,计时器期间没有重传,是新检查挡住了它。
最后,测试恢复原来的握手调用,排除 SSL_ERROR_SSL 和 SSL_ERROR_SYSCALL。它检查的是暂停状态得以保留、后续调用能继续推进;代码没有断言整次握手最终成功,也没有测量泄露内容。
4 版本号较低,也可能已经修好
上游在 4.0、3.6、3.5、3.4、3.0、1.1.1 和 1.0.2 系列确认了影响。下面列出当前公开支持分支的首次修复版本;截至 2026 年 10 月 1 日,它们也就是各自最新稳定版。继续沿现有受支持分支更新,通常比为了这个漏洞临时跨大版本更容易控制兼容性。
3.4 和 3.6 距离上游停止支持都已很近。先修补当前部署,再安排迁移,能把本次安全更新与后续兼容性工作分开;需要较长维护周期的新部署,可优先评估 3.5 LTS。公告还列出 3.0.23、1.1.1zj、1.0.2zs,但这些属于 Premium 支持修复。发行版维护的旧系列有自己的支持和回补安排,不能只拿上游公开下载表判断它们。
| 分支 | 修复与当前稳定版 | 上游支持到期 |
|---|---|---|
| 4.0 | 4.0.3 | 2027 年 5 月 14 日 |
| 3.6 | 3.6.5 | 2026 年 11 月 1 日 |
| 3.5 LTS | 3.5.9 | 2030 年 4 月 8 日 |
| 3.4 | 3.4.8 | 2026 年 10 月 22 日 |
Ubuntu 的实时包表就展示了这种差别:26.04 的 openssl 修复包是 3.5.5-1ubuntu3.6,24.04 是 3.0.13-0ubuntu3.16,22.04 是 3.0.2-0ubuntu1.30。这些上游版本数字都低于公告中的首次修复版,但发行版已经把补丁放进自己的包修订。必须连同后缀一起核对,并且只把结论用于包表对应的组件;同一系统里,其他程序内嵌的 OpenSSL 仍可能有独立状态。
Debian 的记录进一步说明了为什么要看具体仓库:trixie 的安全包 3.5.7-1~deb13u3 已修复,普通仓库所列的 3.5.7-1~deb13u2 仍受影响;bookworm 安全仓库所列的 3.0.22-1~deb12u1 在本次核对时也仍标为受影响。这些状态截止 10 月 1 日 01:20 UTC,部署时以所属仓库的最新状态和完整包号为准。
5 最后检查正在运行的那份库
更新包之后,最容易漏掉的是旧进程。命令行里的 openssl version 能说明这个命令使用的版本,业务进程可能还加载着旧共享库,也可能静态包含了另一份 OpenSSL。应核对服务实际使用的库或产品构建,按产品要求重启或重新部署;容器镜像、设备固件和自带依赖的应用分别处理。仅替换宿主机的一个包,未必会改变这些程序正在执行的代码。
验证时,用产品的正常对端完成 DTLS 握手和一轮业务,再检查丢包、分片及写入暂停后的恢复。维护产品代码的团队可以复用前文的上游回归情境,确认多次超时不会改掉暂停消息的发送进度。如果升级失败,先保留必要的访问限制;需要回退时,选用含有等效修复的受支持构建,避免重新开放仍有漏洞的入口。
历史连接是否真的带出了敏感数据,需要日志、崩溃记录或流量证据进一步判断。普通握手失败本身无法回答这个问题,补丁安装成功也无法追溯此前发出过哪些字节。如果已有异常证据,还需围绕受影响进程可接触的数据调查,确定哪些秘密需要撤销或更换。
6证据与来源
6.1来源与材料
- OpenSSL:2026 年 9 月 29 日安全公告https://openssl-library.org/news/secadv/20260929.txt
- OpenSSL 3.5.8:握手发送、队列与重传源码https://github.com/openssl/openssl/blob/f4dc4d58b48d346a8270183f89acf826d459b0ca/ssl/statem/statem_dtls.c
- OpenSSL 3.5.8:记录层接收发送指针与长度https://github.com/openssl/openssl/blob/f4dc4d58b48d346a8270183f89acf826d459b0ca/ssl/record/rec_layer_d1.c#L611-L669
- OpenSSL 3.5:两处修复与客户端、服务器回归测试https://github.com/openssl/openssl/commit/906cf0ef1c85ca40ce69163e9086d6d3fe292943
- OpenSSL:当前稳定版本与支持日期https://openssl-library.org/source/
- Ubuntu:各组件修复包与受影响状态https://ubuntu.com/security/CVE-2026-84782
- Debian:发行版与仓库包状态https://security-tracker.debian.org/tracker/CVE-2026-84782
- CVE 原始记录:OpenSSL CNA 与 CISA-ADPhttps://cveawg.mitre.org/api/cve/CVE-2026-84782
- NVD:分析状态与变更历史https://nvd.nist.gov/vuln/detail/CVE-2026-84782
- CISA:已知遭利用漏洞目录https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json