This weakness occurs when software decompresses data without validating the compression ratio or limiting resource consumption during decompression. An…
This weakness occurs when software decompresses data without validating the compression ratio or limiting resource consumption during decompression. An attacker can craft a small, highly compressed file that expands to enormous size in memory or disk, exhausting system resources and causing denial of service. This is sometimes called a "zip bomb" or "decompression bomb."
02How It Happens
When data is compressed, a small input file can represent a much larger uncompressed payload. If an application accepts compressed input (ZIP, GZIP, bzip2, etc.) and decompresses it without checking the expansion ratio or enforcing size limits, an attacker can supply a malicious archive. The decompression process consumes CPU, memory, and disk space rapidly, potentially crashing the application or the entire system. The vulnerability is especially dangerous because the compressed file itself may be small enough to pass initial validation checks, but the decompressed output is not constrained.
03Real-World Impact
A denial-of-service attack via decompression bomb can render a web application, API, or file processing service unavailable. If the system runs out of memory during decompression, it may crash or become unresponsive. In shared hosting environments, this can affect other users' applications. File upload features, API endpoints that accept compressed payloads, and backup/restore functions are common attack surfaces.
04Vulnerable & Fixed Patterns
Vulnerable pattern
import zipfile
import io
def process_uploaded_zip(zip_data):
# zip_data is raw bytes from an upload
zip_buffer = io.BytesIO(zip_data)
with zipfile.ZipFile(zip_buffer, 'r') as zf:
for file_info in zf.infolist():
content = zf.read(file_info.filename)
# Process content without checking size
process_file(content)
Why it's vulnerable: The code decompresses all files in the archive without checking the uncompressed size or enforcing a maximum limit. A malicious ZIP with a 1 MB compressed size could expand to gigabytes.
Fixed pattern
import zipfile
import io
MAX_UNCOMPRESSED_SIZE = 50 * 1024 * 1024 # 50 MB limit
MAX_COMPRESSION_RATIO = 100 # Warn if ratio exceeds this
def process_uploaded_zip(zip_data):
zip_buffer = io.BytesIO(zip_data)
with zipfile.ZipFile(zip_buffer, 'r') as zf:
for file_info in zf.infolist():
# Check declared uncompressed size
if file_info.file_size > MAX_UNCOMPRESSED_SIZE:
raise ValueError(f"File {file_info.filename} exceeds size limit")
# Check compression ratio
if file_info.compress_size > 0:
ratio = file_info.file_size / file_info.compress_size
if ratio > MAX_COMPRESSION_RATIO:
raise ValueError(f"Suspicious compression ratio: {ratio}")
content = zf.read(file_info.filename)
process_file(content)
Vulnerable pattern
<?php
function process_uploaded_zip($zip_path) {
$zip = new ZipArchive();
if ($zip->open($zip_path) === true) {
for ($i = 0; $i < $zip->numFiles; $i++) {
$stat = $zip->statIndex($i);
$content = $zip->getFromIndex($i);
// Process content without size checks
process_file($content);
}
$zip->close();
}
}
?>
Why it's vulnerable: The code extracts all files without validating their uncompressed size or checking the compression ratio. A small ZIP file could decompress to consume all available disk or memory.
Fixed pattern
<?php
const MAX_UNCOMPRESSED_SIZE = 50 * 1024 * 1024; // 50 MB
const MAX_COMPRESSION_RATIO = 100;
function process_uploaded_zip($zip_path) {
$zip = new ZipArchive();
if ($zip->open($zip_path) === true) {
for ($i = 0; $i < $zip->numFiles; $i++) {
$stat = $zip->statIndex($i);
// Check uncompressed size
if ($stat['size'] > MAX_UNCOMPRESSED_SIZE) {
throw new Exception("File exceeds size limit");
}
// Check compression ratio
if ($stat['comp_size'] > 0) {
$ratio = $stat['size'] / $stat['comp_size'];
if ($ratio > MAX_COMPRESSION_RATIO) {
throw new Exception("Suspicious compression ratio");
}
}
$content = $zip->getFromIndex($i);
process_file($content);
}
$zip->close();
}
}
?>
05Prevention Checklist
Set a maximum uncompressed file size limit and reject archives containing files larger than this threshold.
Monitor and enforce a maximum compression ratio (e.g., reject files with ratios above 100:1).
Limit the total uncompressed size of all files extracted from a single archive.
Use streaming decompression where possible, and stop processing if size limits are exceeded.
Implement timeouts on decompression operations to catch runaway processes.
Log and alert on suspicious compression ratios or extraction attempts that exceed thresholds.
06Signs You May Already Be Affected
Monitor your application logs for sudden spikes in CPU or memory usage coinciding with file uploads or API requests that include compressed payloads. Check for crashed worker processes or out-of-memory errors during decompression. If your system has become unresponsive after processing a small uploaded file, a decompression bomb may be the cause.