The same two-byte trick that broke ESET’s antivirus in 2004 still works today — against 50 of 51 engines on VirusTotal. The industry had 22 years to fix it.

TL;DR

  • CVE-2026-0866 (CERT/CC VU#976247): a malformed ZIP that declares Method=0 (uncompressed) while the payload is actually DEFLATE-compressed
  • AV engines trust the header and scan compressed bytes as raw data — signatures never match
  • 50 of 51 engines on VirusTotal fail to detect the malware; only Kingsoft catches it
  • The payload is recoverable in 6 lines of Python using standard zlib
  • Discovered by Chris Aziz (Bombadil Systems); GitHub PoC is public
  • WinRAR and 7-Zip show an error — only automated scanning pipelines are blind

Why This Matters

Zombie ZIP isn’t a vulnerability in an obscure tool. It’s a flaw in how the security industry collectively decided to parse a 40-year-old file format.

Email gateways, cloud sandboxes, EDR products, and SIEM ingestion pipelines all scan ZIP attachments as part of their core function. When 98% of them can be bypassed with a header that says “trust me, this is uncompressed”, every ZIP-based malware delivery chain gets a free pass.

This matters most in environments that rely on gateway scanning as a primary defense layer — which is most enterprise environments.


The ZIP Format: What You Need to Know

Before getting into the attack, a quick primer on how ZIP files are structured. ZIP isn’t a single compressed blob — it’s a structured container with metadata that describes each embedded file.

A ZIP archive has three main components:

[Local File Header 1] [File Data 1]
[Local File Header 2] [File Data 2]
...
[Central Directory]
[End of Central Directory Record (EOCD)]

Local File Header

Each file inside the ZIP gets its own Local File Header immediately before its data. The structure starts with the signature PK\x03\x04 and contains:

Offset Length Field
0 4 Signature (0x04034b50 = "PK\x03\x04")
4 2 Version needed to extract
6 2 General purpose bit flag
8 2 Compression method ← THE KEY FIELD
10 2 Last mod file time
12 2 Last mod file date
14 4 CRC-32
18 4 Compressed size
22 4 Uncompressed size
26 2 File name length
28 2 Extra field length
30 var File name
? var Extra field
? var File data

The compression method field (bytes 8–9) tells the parser how to handle the file data that follows. The two most common values:

ValueMethodMeaning
0x0000STOREDData is uncompressed — read as-is
0x0008DEFLATEData is compressed — run through inflate

Central Directory

At the end of the archive, the Central Directory contains a summary record for each file. It duplicates the compression method field and is what most tools use to build a file listing.

The Parser’s Trust Assumption

Here’s the architectural decision that creates the vulnerability: most ZIP parsers — and almost all security scanning engines — read the compression method from the header and use it to decide how to process the subsequent bytes. If Method says 0 (STORED), they treat the bytes as raw data. They do not verify that the declared method matches the actual data structure.


How Zombie ZIP Works

The Zombie ZIP technique exploits this trust directly:

Step 1: Create a normal ZIP file containing a malicious payload. The payload is DEFLATE-compressed as usual — Method=0x0008, data is compressed.

Step 2: Patch the compression method field in the Local File Header from 0x0008 to 0x0000. Two bytes. The actual file data — the DEFLATE-compressed payload — is left completely unchanged.

Step 3: Leave the Central Directory untouched, or patch it to match. The inconsistency between declared method and actual data is now baked in.

Before (normal ZIP):
Local File Header: compression_method = 0x0008 (DEFLATE)
File data: [DEFLATE-compressed payload]
After (Zombie ZIP):
Local File Header: compression_method = 0x0000 (STORED) ← patched
File data: [DEFLATE-compressed payload] ← unchanged

What the AV Engine Sees

The scanning engine reads the Local File Header. Compression method = 0x0000. It concludes: “this file is uncompressed, scan the raw bytes.”

It then scans the raw bytes — which are DEFLATE-compressed data. DEFLATE-compressed data looks like high-entropy binary noise to a scanner. Malware signatures are written against decompressed, recognizable byte patterns. The scanner finds nothing, reports the archive as clean, and passes it through.

What a Purpose-Built Loader Sees

An attacker’s custom loader doesn’t care what the header says. It just calls zlib.decompress() on the file data regardless of the declared method. The DEFLATE stream decompresses perfectly — the payload was never damaged, only mislabeled.

The PoC from Bombadil Systems demonstrates this in six lines:

import zipfile, zlib
with open("zombie.zip", "rb") as f:
raw = f.read()
# Locate and decompress the "stored" entry directly via zlib
# (bypassing the declared Method=0 header)
payload = zlib.decompress(raw[offset:], -15) # raw deflate stream

The payload comes out byte-perfect. The AV saw noise. The loader sees the original file.


CVE-2026-0866 and the Ghost of CVE-2004-0935

The first time this class of vulnerability was documented was 2004 — CVE-2004-0935 affected ESET Anti-Virus versions before 1.020. The exact same mechanism: ZIP header declares uncompressed, actual content is compressed, ESET scanned the compressed bytes and missed the malware.

ESET fixed their product. The rest of the industry apparently didn’t notice, or didn’t prioritize it.

22 years later, Chris Aziz at Bombadil Systems rediscovered the same technique and tested it against VirusTotal’s full engine set. The result:

ResultCount
Failed to detect50 engines
Detected correctly1 engine (Kingsoft)
Detection rate~2%

CERT/CC assigned VU#976247 and the CVE got its number: CVE-2026-0866. The name “Zombie ZIP” comes from the concept of a long-dead vulnerability rising from the grave — same trick, different year, same result.


Why WinRAR and 7-Zip Error Out

If Zombie ZIP bypasses AV engines, why does opening it in WinRAR or 7-Zip produce an error instead of silently executing the payload?

Because WinRAR and 7-Zip follow the ZIP spec. When they see Method=0, they attempt to read the file data as raw uncompressed bytes. The data is actually a DEFLATE stream — it starts with a DEFLATE header byte, not valid file content. The decompressed “file” is garbage. The extractor reports a CRC mismatch or corrupt archive.

This is the key distinction:

ToolBehavior with Zombie ZIP
WinRARCRC error / extraction failure
7-ZipReports corrupt archive
Windows ExplorerExtraction error
AV engine (scanner mode)Scans “raw” bytes, reports clean
Email gatewayPasses the file through
Cloud sandboxMarks as clean
Custom loaderDecompresses correctly, executes

The attack isn’t designed to trick end users — it’s designed to bypass the automated scanning layer before the file reaches the user. The attacker delivers the ZIP via phishing, the gateway scans it and passes it, and then the attached dropper (not WinRAR) handles the actual extraction.


Attack Chain in Practice

A realistic deployment scenario targeting enterprise email:

[1] Payload Preparation
- Pack malicious executable (e.g., Cobalt Strike beacon, AsyncRAT)
into a normal DEFLATE-compressed ZIP
- Patch Local File Header: bytes 8-9 from 0x08 0x00 → 0x00 0x00
- Result: Zombie ZIP, 50/51 AV engines blind
[2] Phishing Delivery
- Attach Zombie ZIP to a spearphishing email
- Lure: "Q1 invoice", "contract review", "security update"
- Send through email infrastructure that avoids reputation blocks
[3] Gateway Processing
- Email gateway receives message, scans attachment
- ZIP parser reads Method=0, scans DEFLATE bytes as raw data
- No signatures match — attachment flagged as clean
- Email delivered to recipient inbox
[4] User Interaction
- User opens ZIP — sees error (archive appears corrupt)
- But: phishing email includes instructions:
"If the file shows an error, run the included setup.exe first"
- Or: a JavaScript dropper / macro opens the ZIP using
a custom extraction routine rather than shell association
[5] Payload Execution
- Custom loader decompresses via raw zlib (ignores method header)
- Malicious executable extracted to %TEMP% and launched
- AV/EDR never scanned the actual payload bytes

The “open setup.exe if you see an error” social engineering step is not hypothetical — it mirrors the ClickFix technique covered in The ‘Fix’ Is the Exploit: ClickFix, FileFix and Pastejacking Attacks Explained, which conditions users to run things when software appears broken.


Detection

Zombie ZIP detection falls into two categories: file-based (static) and behavioral.

YARA Rule — Header Method Mismatch

rule ZombieZIP_Method_Mismatch
{
meta:
description = "Detects ZIP with STORED method declaration over DEFLATE data"
author = "Hive Security"
date = "2026-03-13"
reference = "CVE-2026-0866"
strings:
// Local File Header signature
$lfh_sig = { 50 4B 03 04 }
// Method = STORED (0x0000) at correct offset
$method_stored = { 50 4B 03 04 ?? ?? ?? ?? 00 00 }
// DEFLATE magic bytes (common start of deflate stream)
$deflate_magic = { 78 9C } // zlib wrapper (default compression)
$deflate_raw = { 78 DA } // zlib best compression
$deflate_min = { 78 01 } // zlib no compression (but still zlib)
condition:
$lfh_sig at 0 and
$method_stored at 0 and
(
$deflate_magic in (30..256) or
$deflate_raw in (30..256) or
$deflate_min in (30..256)
)
}

This catches the most common variant: a ZIP whose first entry declares STORED but whose file data begins with a zlib header (78 9C, 78 DA, or 78 01).

Header Validation in Python

For integration into custom pipelines, a simple structural validator:

import struct
def is_zombie_zip(path: str) -> bool:
"""Return True if ZIP declares STORED but data looks like DEFLATE."""
with open(path, "rb") as f:
sig = f.read(4)
if sig != b"PK\x03\x04":
return False
f.seek(8) # compression method offset
method = struct.unpack("<H", f.read(2))[0]
f.seek(30) # skip to file data (min header size, no name/extra)
data_start = f.read(2)
# Method=STORED but data starts with zlib magic
return method == 0 and data_start in (b"\x78\x9c", b"\x78\xda", b"\x78\x01")

Behavioral Indicators

Beyond static file analysis, watch for:

  • Process calling zlib or inflate APIs on a file that was never extracted via standard shell — unusual decompression path
  • ZIP attachment delivered to mailbox + CRC error on open + executable spawned shortly after — delivery chain correlation
  • WinRAR or explorer.exe reporting ZIP error + new process under OUTLOOK.EXE or browser — user opened broken ZIP, something else ran

What You Can Do Today

1. Update Security Products

Vendors are actively pushing fixes. Ensure your email gateway, EDR, and endpoint AV are on the latest signatures and engine versions. Check vendor advisories for explicit CVE-2026-0866 coverage.

2. Deploy the YARA Rule

Add the YARA rule above to your scanning pipeline — email gateway, file upload endpoint, EDR custom rule engine (CrowdStrike, Defender for Endpoint, and SentinelOne all support custom YARA). This catches the known PoC variant.

3. Test Your Stack Against VirusTotal’s Baseline

Upload a benign test file packaged as a Zombie ZIP (no malicious payload — just a text file with a patched header) to your internal scanning tools. If they report clean, your pipeline is blind to this technique.

4. Email Gateway: Reject Malformed Archives

Most enterprise email gateways support a policy to reject or quarantine structurally malformed ZIP files. Enable it. The tradeoff: some legitimate (badly authored) ZIP tools produce non-spec archives that will get caught. Evaluate the false positive rate in your environment.

5. Don’t Rely on Gateway Scanning Alone

Zombie ZIP is a reminder that file format parsing is an arms race. Layered defense means:

  • Gateway scanning (catch known-bad)
  • Sandbox detonation (catch suspicious-behavior)
  • EDR behavioral monitoring (catch post-execution)
  • User awareness (recognize social engineering cues like “run this if you see an error”)

No single layer is reliable alone.



Sources