| 项目 | 内容 |
|---|---|
| CVE ID | CVE-2023-4911 |
| 攻击类型 | 堆缓冲区溢出 → 本地权限提升(Local Privilege Escalation) |
| CVSS 3.1 | 7.8 (High) |
| 公开日期 | 2023-10-03 |
| 漏洞点 | glibc 动态加载器(ld.so)的 GLIBC_TUNABLES 解析器 |
| 受影响版本 | glibc 2.34~2.38 |
CVE-2023-4911 是 GNU C Library(glibc) 的动态加载器在解析 GLIBC_TUNABLES 环境变量时
发生的堆缓冲区溢出漏洞。攻击者可以利用此溢出
操纵动态加载器的库搜索路径(RPATH),从而在 SUID root
二进制文件(su、sudo 等)被执行时,加载攻击者准备的恶意共享库,
并以 root 权限执行任意代码。由于 glibc 实际上是所有主要
Linux 发行版的核心组件,该漏洞影响了 2021 年 4 月之后发布的大多数
基于 glibc 的发行版。
while (true)
{
char *name = p;
size_t len = 0;
/* 이름(name) 길이 찾기 */
while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
len++;
/* '=' 없이 끝나면 종료 */
if (p[len] == '\0')
{
if (__libc_enable_secure)
tunestr[off] = '\0';
return;
}
/* ':'를 먼저 만나면 잘못된 항목 */
if (p[len] == ':')
{
p += len + 1;
continue;
}
/* '='를 만났으므로 value 시작으로 이동 */
p += len + 1;
/* 원본 문자열에서 value 계산 */
char *value = &valstring[p - tunestr];
len = 0;
/* value 길이 찾기 */
while (p[len] != ':' && p[len] != '\0')
len++;
...
/* tunestr에 복사 */
...
if (p[len] != '\0')
p += len + 1;
}
__tunables_init() 在环境变量列表中找到 GLIBC_TUNABLES。tunables_strdup() 使用 __minimal_malloc() 分配缓冲区并复制原始字符串
(此时的 malloc 还是尚未完全初始化的非常早期的 malloc
实现)。parse_tunables() 以 :(冒号)为分隔符遍历该缓冲区,分离各个
key=value 对,并将值赋给相应的 tunable。parse_tunables() 按照 解析 name → 移动 p → 解析 value → 移动 p 的顺序处理一个 tunable。在正常输入情况下,处理完 value 后,p 会移动到下一个 tunable 的起始位置,然后解析下一项。
然而,当输入为如下 name=name=value 形式时:
GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=AAAA...(긴 문자열)
在第一次解析过程中,glibc.malloc.mxfast=AAAA... 整个被识别为一个 value,并复制到 tunestr 缓冲区。之后由于 value 后面不存在用于分隔下一个 tunable 的冒号(:),解析指针(p)无法移动到下一项,而是重新指向已复制 value 的起始位置。
问题在于这个 value 本身也是 name=value 形式。在下一次迭代中,解析器会错误地将其识别为一个新的 tunable,并将重复的数据写入缓冲区。由于 tunestr 只分配了与原始字符串大小相等的空间,因此会产生重复写入的部分,从而导致堆缓冲区溢出。
发生的堆缓冲区溢出会覆盖由 __minimal_malloc() 连续分配的相邻堆区域。攻击者可借此将动态加载器(ld.so)的内部结构体 link_map 的 l_info[DT_RPATH] 指针改为攻击者控制的栈地址。
该栈区域中预先放置了精心构造的 Elf64_Dyn 结构体,该结构体将攻击者指定的目录设为新的库搜索路径(RPATH)。结果,ld.so 会优先加载攻击者准备的共享库,而不是正常的系统库。
name=name=value 形式的 GLIBC_TUNABLES 环境变量。su 等)。parse_tunables() 中发生堆缓冲区溢出。link_map 的 l_info[DT_RPATH] 指针篡改为攻击者准备的伪 Elf64_Dyn 结构体所在的栈地址。libc.so.6。libc.so.6 的初始化代码(或被篡改的启动例程)以 root 权限执行,完成 setuid(0)、setgid(0) 以及 /bin/sh 的执行。su 的认证逻辑执行之前,因此无需密码验证即可获得 root shell。PoC 采用暴力破解方式,在随机化(ASLR)影响下反复执行 execve(),直到形成所需的内存布局。因此,攻击是否成功以及所需时间因环境而异,通常需要数百到数千次重复尝试。
首先将 git 中的内容克隆到目录中
git clone https://github.com/baeseungwon1010/CVE-2023-4911

通过以下命令构建 docker 镜像
cd C* && docker compose run --rm cve-2023-4911-lab

进入容器后运行漏洞利用代码
cd /home/student/exploit && ./exp
运行后等待一段时间,可以看到普通用户变为 sudo(0)。

将 glibc 从受影响版本升级到修复版本。 修补后,如有可能,通过重启/重新启动确保旧版本的 glibc 不再驻留内存。 如果无法立即修补,可以采取临时措施,删除不必要的 SUID、SGID 等进程。