YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
fastavro .avro decompression-bomb β PoC model file
This repo is a gated security PoC for a huntr Model File Vulnerability submission. It contains a crafted
.avro model/data file that triggers uncontrolled memory consumption when read by the standard fastavro
library. Do not load evil.avro on a machine you care about without a memory limit.
- Target format: Avro β Apache (
.avro) - Parser:
fastavro(Fast Avro for Python), PyPIfastavro==1.12.2, commita937166 - CWE: CWE-409 (Decompression Bomb) / CWE-400 (Uncontrolled Resource Consumption)
- Class: unauthenticated denial-of-service on model/data load
The bug
fastavro's deflate_read_block() (fastavro/_read.pyx) reads a compressed Avro data block and decompresses
the entire block in one uncapped call β BytesIO(zlib.decompressobj(-15).decompress(data)) β with no
output-size cap, no streaming, and no caller-facing limit. It runs from the zero-config public read API
fastavro.reader(open(path,'rb')), before the per-record decode loop. Because block decompression happens
independently of the declared block_count, evil.avro sets block_count=0 β so no schema-valid records are
needed; the bomb fires purely on the block decompress.
Files
evil.avro(2,087,353 bytes) β the PoC: a valid Avro container whose single block is a2 MB raw-DEFLATE stream that decompresses to 2 GiB of zero bytes (1029Γ deflate ratio).normal.avro(20 KB) β a benign same-codec control (2000 real records).build.pyβ reconstructs both files from scratch.victim.pyβ the victim:list(fastavro.reader(open(path,'rb'))).repro.shβ end-to-end driver (builds + runs both undercommand time -v).evidence/β rawtime -voutput and the measured RSS.
Reproduce
python3 -m venv venv && . venv/bin/activate
pip install fastavro==1.12.2
command time -v python3 victim.py evil.avro # watch "Maximum resident set size"
command time -v python3 victim.py normal.avro # benign control
Measured result (WSL2, CPython 3.10, fastavro 1.12.2)
evil.avroβ peak RSS 4,213,716 KB (~4.02 GiB), 1,051,108 minor page faults (real committed memory), exit 0.normal.avroβ peak RSS 18,260 KB.- Amplification: 2067Γ the on-disk file size, 231Γ the benign baseline. The 2 GiB target is a demonstration
ceiling (test VM had ~14 GB free); the effect scales linearly with the attacker-chosen decompressed size up to
OOM /
MemoryError.
Honest scope: this is a denial-of-service (memory exhaustion) primitive, not RCE. The sibling bzip2_read_block
and xz_read_block share the identical unbounded-decompress shape (not separately measured here).
Fix
Cap the decompressed output in deflate_read_block (and the bzip2/xz siblings): use a bounded
decompressobj.decompress(data, max_length) loop that aborts once a byte/ratio budget is exceeded, and expose a
public max_block_size / max_decompressed_size keyword on fastavro.reader().