telnetd 预认证溢出漏洞解析,CVE-2026-32746 的成因与修复

GNU inetutils 的 telnetd 存在一个预认证溢出漏洞,编号 CVE-2026-32746,CVSS 3.1 评分 9.8。攻击者在登录提示出现之前,只要向 23 端口发送一组构造过的 LINEMODE 子选项,就可以越界写入约 400 字节内存,进而获得任意写和任意释放原语。官方修复是补一个边界检查,修复版本为 inetutils 2.8,主要发行版已经跟进发布补丁。

漏洞在代码里的位置

问题出在 telnetd 的 LINEMODE 协商逻辑。协议里有一个 SLC 子选项,用来设置本地字符的含义,每个三元组包含功能号、标志和取值。服务器把这些三元组追加进一个静态缓冲区,缓冲区固定 108 字节,头部占 4 字节,可用 104 字节。

add_slc 这个函数往里写数据时不检查容量,只递增写入指针。每个字节如果等于 0xFF 还要翻倍写两次,三元组最多能撑到 6 字节。收到超过编号范围的功能号时,服务器会排队回复一条不支持消息,这类回复的数量没有上限。大约 35 个这样的三元组之后,写入就越过边界,覆盖掉相邻的全局变量,其中包含写入指针自身。

指针被污染之后,后续的结束标记写入会落到攻击者可控的地址上,这就是任意写的来源。取值字节还可以用来做指针的部分覆写。

为什么叫 32 年的漏洞

这段代码来自 1994 年进入代码库的实现,披露时已经存在三十多年。同源的客户端缺陷 CVE-2005-0469 在 2005 年被修过,当时补的是同一类边界检查。服务器端这次漏掉了。

攻击条件与利用难度

前提条件有三个。攻击者网络能到 23 端口,目标支持并通告 LINEMODE,服务器使用受影响版本的 telnetd。不需要任何凭据,整个触发过程发生在选项协商阶段。

利用难度随架构变化。32 位系统上实测拿到过任意释放和堆指针泄露。64 位系统明显更难,攻击者只能做小端部分覆写,构造连续空字节不可行。公开的检测工具带探测模式,只检测 LINEMODE 是否被通告,不发送溢出载荷,可以用来做资产盘点。

修复与缓解步骤

  1. 升级到 inetutils 2.8 或更新版本,这是首个包含修复的正式发行版。
  2. 用发行版补丁的,确认版本号。Debian 侧修复版本是 bookworm 的 2.4-2+deb12u3 和 trixie 的 2.6-3+deb13u3,Ubuntu 侧覆盖 24.04、25.10、26.04 三个 LTS/常规版本,更老的系统经扩展维护通道提供。
  3. 补丁的行为是静默忽略溢出数据,不会有错误提示,升级后要做一次探测验证。
  4. 用不上 telnet 的环境直接停服务、封端口,这是成本最低的一条。

修复补丁加了一行界限检查,写不下的三元组直接丢弃。

暴露面怎么看

受影响的实现范围很广,包括 inetutils 自身以及多个发行版打包版本,还有一批 BSD 派生实现和嵌入设备固件。内存破坏类漏洞的利用要针对具体编译产物定制,攻击者会按目标价值取舍,不会被批量扫一遍就沦陷。漏洞目前不在已知被利用目录里,公开材料只到概念验证阶段。

老协议在遗留系统里的存量比想象中大。工控、网络设备和内部管理网还留着 telnet 服务的场景,值得借这次机会做一轮清理,能换 SSH 的换掉,换不掉的限制来源地址。

常见问题

  • 只在内网开 telnet 要修吗。要修,内网横向移动的起点往往就是这类老服务。
  • 怎么确认服务是否受影响。用探测工具检查 LINEMODE 通告,或直接核对版本号。
  • 修复后要重启什么。重启 telnetd 服务即可,由 inetd 托管的按托管方式重启。
  • 用 BusyBox 的需要处理吗。不同实现路径不一样,先确认版本来源,不确定就按暴露面收敛处理。