flawopen.com/Teardowns/cve-2023-6246-glibc-syslog-heap-buffer-overflow
CVE-2023-6246:glibc syslog 堆缓冲区溢出
CVE-2023-6246 是 glibc syslog 的堆缓冲区溢出,由过长日志头触发。请更新到发行版提供的 glibc 修复包。
想象一名员工用短尺测量包裹和标签。标签太长,放不进尺子的测量范围,于是计算过程没有记录所需空间。员工随后订购了一个很小的盒子,却试图把完整标签和内容都印进去。在 glibc 中,过长的 syslog 头可能使所需大小没有被正确记录,导致日志函数只分配少量堆内存。
核心概念与专有名词
glibc- GNU C Library 提供大多数 Linux 程序使用的核心 C 运行时函数。
__vsyslog_internal()- glibc 内部实现,
syslog()和vsyslog()使用它来格式化消息及SYSLOG_HEADER,然后交给系统日志服务。 SYSLOG_HEADER- syslog 消息前格式化的优先级和身份前缀,可包含程序名或通过
openlog()提供的标识。 Heap-based buffer overflow (CWE-122)- 写入操作超出堆上分配的缓冲区边界。此处的问题是在包含长名称的头部放不进初始固定缓冲区时,大小计算失败。
框架映射
- 本指南所述弱点的 CWE 参考: CWE-122
根本原因剖析 (Root Cause)
glibc 公告指出,__vsyslog_internal() 格式化包含长程序名的 SYSLOG_HEADER 时,没有更新所需缓冲区大小。在漏洞代码中,头部长度 l 可能达到或超过 1,024 字节的栈缓冲区,因此跳过了格式化消息正文并设置 bufsize 的分支。之后的堆分配路径便使用未设置或为零的大小,再将头部和消息写入过小空间。Qualys 指出,长名称可以来自调用方控制的 argv[0] basename,或超过 1,024 字节的 openlog() 标识;若存在漏洞的 setuid 程序沿此路径记录日志,问题便可能影响安全。Qualys 在 Fedora 38 上通过 su 展示了本地提权。风险取决于本地访问、配置和调用路径;这不是通用的远程 syslog 攻击。
攻击执行流程分解
本地前提
本地进程必须通过使用受影响 glibc 的程序调用 syslog() 或 vsyslog()。要产生安全影响,还需要特权调用方,并且调用路径没有用安全且有界的标识替换程序名前缀。
超长日志头
日志头超过初始缓冲区|SYSLOG_HEADER 包含长程序名(__progname,来自 argv[0] 的 basename)或较长的 openlog() 标识。Qualys 表示触发值必须超过 1,024 字节。
大小计算遗漏
跳过大小计算分支|当日志头放不进 1,024 字节栈缓冲区时,漏洞代码会跳过计算 bufsize 的分支。后续堆分配空间不足以容纳头部和格式化消息。
内存影响
写入超出分配范围|日志函数将头部和消息写入过小缓冲区,从而破坏堆内存。Qualys 在 Fedora 38 上通过 su 展示本地提权;不要推断所有应用或发行版都具有相同可达性。
源代码对比:漏洞与安全实现
旧分支在头部无法放入栈缓冲区时让 bufsize 保持为零,因此后续堆分配可能过小。修复会将栈缓冲区标记为不可用,用它测量正文长度,记录 l + vl,再通过现有分配路径按所需大小分配。这些是上游补丁的重点片段,并非可独立使用的完整函数。
源码摘录: misc/syslog.c
/* misc/syslog.c 在提交 6bd0e4efcc78 之前的重点代码片段。 */
char bufs[1024];
char *buf = NULL;
size_t bufsize = 0;
...
if (0 <= l && l < sizeof bufs)
{
va_list apc;
va_copy (apc, ap);
int vl = __vsnprintf_internal (bufs + l, sizeof bufs - l, fmt, apc,
mode_flags);
if (0 <= vl && vl < sizeof bufs - l)
buf = bufs;
bufsize = l + vl;
va_end (apc);
}
/* 长请求头会跳过上方设置大小的分支。 */
/* misc/syslog.c 在提交 6bd0e4efcc78 之后的重点代码片段。 */
char bufs[1024];
char *buf = bufs;
size_t bufsize;
...
if (0 <= l && l < sizeof bufs)
{
pos = bufs + l;
len = sizeof (bufs) - l;
}
else
{
buf = NULL;
pos = bufs;
len = sizeof (bufs);
}
vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags);
if (!(0 <= vl && vl < len))
buf = NULL;
bufsize = l + vl;
工程与系统安全加固清单
- ✓使用适用于当前发行版和版本的修复 glibc 软件包;不要只依据上游版本推断软件包状态。
- ✓核对发行版安全公告:上游修复版本为 glibc 2.39、2.38-42、2.37-57 和 2.36-126。
- ✓盘点调用
syslog()或vsyslog()的特权程序,并检查调用方可控的程序名或openlog()标识是否会进入日志头。 - ✓实现格式化日志时,即使初始固定缓冲区不足,也要计算完整头部与正文大小;为长程序名添加回归测试并检查分配大小运算。