CWE-409 is what happens when a product does not handle, or mishandles, compressed input with a very high compression ratio that produces a large output. MITRE gives the standard example: a decompression bomb, a small ZIP file that produces a large amount of data when decompressed.
Upload endpoints, archive importers, document converters and XML parsers are the usual places.
How it happens
The code reads a compressed stream and writes out whatever comes. It trusts the stream to be reasonable. Compression ratios of thousands to one are possible for repetitive data, so a file that looks harmless on disk can fill memory or disk when opened.
XML has its own version. MITRE lists an XML bomb in a web server module (CVE-2009-1955) and a parsing library that allowed an XML bomb (CVE-2003-1564). In these, nested entity definitions expand into far more text than the input suggests.
import zipfile
# Vulnerable: extracts everything with no size check
with zipfile.ZipFile("upload.zip") as z:
z.extractall("/data/out")
What an attacker gains
Availability. The record notes that CPU and memory can be consumed quickly, leading to poor performance or a crash. No code execution is needed. One small upload can take a service down.
How to find it
The record lists no detection methods. Practical checks:
- Find every place your code decompresses or expands untrusted data: ZIP, gzip, image formats, XML entities.
- Test with a known high ratio test archive in a safe environment and watch memory and disk.
- Check whether your XML parser has entity expansion limits turned on.
How to fix it
- Set a maximum total uncompressed size and a maximum ratio, and stop reading when either is exceeded.
- Read in chunks and count output bytes as you go, rather than trusting header fields.
- Limit the number of entries in an archive.
- Disable or cap XML entity expansion.
- Run decompression in a worker with memory and time limits.
import zipfile
MAX_TOTAL = 100 * 1024 * 1024 # 100 MB
total = 0
with zipfile.ZipFile("upload.zip") as z:
for info in z.infolist():
total += info.file_size
if total > MAX_TOTAL:
raise ValueError("archive too large when expanded")
z.extractall("/data/out")
The header size can be forged in some formats, so for hostile input also count bytes while streaming.
Quick check
Why not just limit upload size?
Upload size measures the compressed bytes. The damage comes from the expanded size, so that is what you must limit.