net-tools 是构成 Linux 操作系统 NET-3 网络发行版基础组件的一套程序集,其中包含我们熟知的 ifconfig 等网络工具。近期,该工具集中的 lib/interface.c 文件内 get_name() 函数被发现存在一个堆栈缓冲区溢出漏洞,编号为 CVE-2025-46836。

漏洞描述显示,在 net-tools 2.10 及之前版本中,其网络工具在显示接口时未正确验证 /proc 文件的结构。get_name() 函数从 /proc/net/dev 复制接口标签到一个固定大小的 16 字节栈缓冲区时,未进行边界检查,可能导致任意代码执行或程序崩溃。已知的攻击路径不需要特权,但在该场景下也无法实现权限提升。
查看漏洞补丁
修复该漏洞的补丁已经提交,预计将在 net-tools 2.20 版本中发布。从提交记录可以看到,开发者为 get_name() 函数增加了一个安全版本,确保最多只复制 IFNAMSIZ-1 字节(即15个字节),并保证目标缓冲区始终以 NULL 结尾。


漏洞分析
漏洞的核心在于 lib/interface.c 文件中的 get_name() 函数。该函数接收两个参数:一个字符指针 *name 和另一个常量字符指针 *p。

为了理解漏洞的触发条件,我们需要逆向追踪调用 get_name() 的函数,例如 if_readlist_proc。在此函数中,我们可以看到传入 get_name() 的第一个参数 name 被定义为 char name[IFNAMSIZ];。IFNAMSIZ 是一个常量,其值通常为 16。

回到 get_name() 函数,重点关注 while 循环中向 name 缓冲区写入数据的部分。关键问题是:从指针 p 复制到 name 的数据量是否会超过 16 字节?
*name++ = *p++;
分析函数逻辑:从第237行开始,逆向关注输入 *p 在复制前是否经过了充分的长度校验。整个循环逻辑主要包含两个判断:
if (isspace(*p)):判断 *p 是否为空白字符(如空格),如果是则跳出循环。
if (*p == ':'):判断 *p 是否为冒号,用于处理可能的接口别名。
简而言之,*name 读取的是 *p 中从第一个非空格字符开始,直到遇到下一个空格或冒号之前的数据。这里并未对要复制的字符数做任何限制。

那么,*p 的数据来自哪里呢?继续向上游追溯,在调用处可以看到 s = get_name(name, buf);,即 *p 来源于 buf。
buf 是通过 fgets(buf, sizeof buf, fh) 从文件句柄 fh 中读取的,其大小在声明时定义为 char buf[512];,这明显超过了 name 缓冲区的 16 字节限制。

如果 fh 指向的文件(即 /proc/net/dev,虽然正常情况下该文件内容可控性有限,但漏洞描述暗示了攻击路径)中某一行在冒号前的接口名称长度超过 15 个字符(加上结尾的 \0 共 16 字节),那么 get_name() 函数就会向 name 栈缓冲区写入超长数据,从而导致经典的栈缓冲区溢出。对于此类漏洞的深入分析和挖掘技术,可以参考 安全/渗透/逆向 板块的讨论。

小结
本次对 CVE-2025-46836 的快速审计揭示了漏洞的本质:一个缺少边界检查的字符串拷贝操作 (*name++ = *p++),其源数据长度 (buf[512]) 远超目标缓冲区大小 (name[16])。修复方案也很直接——在拷贝前严格限制长度。这再次印证了安全编码中“不信任任何输入”和“明确边界”原则的重要性。如果你对更多底层安全研究感兴趣,欢迎到 云栈社区 与大家交流探讨。
|