GHSA-9mqm-qcwf-5qhg CredSweeper: Recursive archive size-limit bypass in deep scanner allows crafted compressed inputs to exhaust resources
SummaryCredSweeper's deep scanner does not enforce `recursive_limit_size` as a hard limit. Several recursive scanners fully decompress or fully read attacker-controlled content before the remaining budget is validated, and `AbstractScanner.recursive_scan()` continues processing even when the residual budget is already negative.This allows a crafted archive to bypass the intended recursive zip-bomb protection and force excessive memory / CPU consumption when deep scanning is enabled (`--depth > 0`). I confirmed this on upstream commit `8b081acf04311eafe8fbd66ea41d02b0a7a4c6f6` / package version `1.15.8`.The issue has two closely related exploitation paths that share the same root cause:Single-stream decompressor bypass:
`gzip`, `bzip2`, and `lzma/xz` inputs are fully decompressed first, then the remaining budget is computed, and the recursive scan proceeds even if the result is negative.Multi-entry archive cumulative-budget bypass:
`zip` and `tar` entries are checked only against the original per-entry budget, not against a mutable cumulative remaining budget shared across sibling entries. Multiple individually small entries can therefore exceed the configured recursive limit in aggregate.The impact is availability/resource exhaustion. I did not confirm arbitrary code execution, arbitrary file write, or data exfiltration from this issue.DetailsThe vulnerability is in the recursive deep-scanning path that is used when CredSweeper scans container-like inputs recursively.The relevant call chain is:`credsweeper/app.py:323`
`self.deep_scanner.scan(content_provider, self.config.depth, self.config.size_limit)``credsweeper/deep_scanner/abstract_scanner.py:269-305`
The initial deep-scan entry point passes a recursive size budget into nested scanners.`credsweeper/deep_scanner/abstract_scanner.py:58-94`
`recursive_scan()` stops only on:negative depthdata shorter than `MIN_DATA_LEN`
It does **not** stop when `recursive_limit_size` is negative.Exact source-level issue:Negative budgets are still accepted`credsweeper/deep_scanner/abstract_scanner.py:71-91`There is no guard such as `if recursive_limit_size < 0: return`.Full decompression happens before any hard budget enforcement`credsweeper/deep_scanner/gzip_scanner.py:33-43``credsweeper/deep_scanner/bzip2_scanner.py:38-43``credsweeper/deep_scanner/lzma_scanner.py:38-43`The decompressed payload is materialized in memory first. Only afterwards is the residual budget calculated, and because `recursive_scan()` accepts negative budgets, the oversize content is still scanned.Multi-entry archives use per-entry checks instead of a shared cumulative budget`credsweeper/deep_scanner/zip_scanner.py:49-60``credsweeper/deep_scanner/tar_scanner.py:48-59`These checks use the same original `recursive_limit_size` for every sibling entry. The budget is not decremented globally after the first extracted member. Therefore a `zip` or `tar` with many individually small files can exceed the intended aggregate extraction limit.Same code pattern is also present in RPM scanning`credsweeper/deep_scanner/rpm_scanner.py:42-51`The RPM scanner uses the same per-member pattern as ZIP/TAR. I did not include an RPM runtime PoC below only because it requires an extra third-party parser dependency, but the source-level pattern is the same.Version scope:The vulnerable recursive scanning logic was introduced by commit `0bd8fe56ad2e08b12d47677f7dbe1a75913969ae`.The last release before that commit is `v1.4.8`.The first release containing that commit is `v1.4.9`.Current upstream HEAD and package version `1.15.8` are still affected.PoCI reproduced the issue on:Repository: `https://github.com/Samsung/CredSweeper`Commit: `8b081acf04311eafe8fbd66ea41d02b0a7a4c6f6`Version: `1.15.8`I used a dependency-light harness that imports the exact vulnerable source files by path and stubs unrelated modules only to isolate the deep-scanner logic. The proof uses only Python's standard library.Reproduction steps:Clone the repository:Save the following as `proof_poc.py` one directory above the repository, or adjust `REPO_ROOT` accordingly:Run it with Python 3:Expected/observed output from my run on commit `8b081acf04311eafe8fbd66ea41d02b0a7a4c6f6`:What this proves:GZIP/BZIP2/LZMA:
With a configured recursive limit of `16`, CredSweeper still fully inflates a `64` byte payload and then continues recursion with a residual limit of `-48`.AbstractScanner:
The negative budget is not rejected. `recursive_scan()` still dispatches into `deep_scan_with_fallback()` with `recursive_limit_size = -48`.ZIP/TAR:
A configured limit of `16` still allows two `12` byte members to be processed, for a total extracted size of `24`.This is a complete end-to-end proof of the root cause and both exploitation variants.ImpactThis is an availability / resource-exhaustion vulnerability.Who is impacted:Users who run CredSweeper with deep scanning enabled (`--depth > 0`) on untrusted repositories, archives, or binary inputs.CI jobs, pre-merge checks, internal security automation, and local review workflows that recursively inspect attacker-controlled compressed files.Downstream services that expose CredSweeper as part of automated scanning of uploaded or fetched content.Practical consequences:Oversized decompressed content can be materialized and scanned even when it exceeds the configured recursive budget.Archive inputs with many individually small members can exceed the configured budget in aggregate.Jobs may hang, consume excessive memory/CPU, or be terminated by the operating system / CI platform.Security classification:Primary weakness: `CWE-409: Improper Handling of Highly Compressed Data (Data Amplification)`Related weakness: `CWE-400: Uncontrolled Resource Consumption`I did not confirm confidentiality or integrity impact from this issue. The impact I confirmed is denial of service / resource exhaustion.MitigationI recommend fixing this in three layers:Add a hard negative-budget guard in `recursive_scan()` and `structure_scan()`Before any recursive dispatch, abort when `recursive_limit_size < 0`.Enforce limits before or during decompression, not after full materialization`gzip`, `bzip2`, `lzma/xz` should use bounded incremental decompression / bounded reads.If the decompressed size exceeds the remaining budget, stop immediately before constructing the full payload in memory.Track a mutable cumulative budget across sibling archive members`zip`, `tar`, and `rpm` should share a remaining-budget counter across entries.After one child is accepted, decrement the shared remaining budget before processing the next sibling.Recommended regression tests:A gzip payload whose decompressed size exceeds the recursive limit must be rejected before recursion and without a negative residual budget being processed.Equivalent tests for bzip2 and lzma/xz.A zip/tar archive with two members that are each under the per-entry threshold but exceed the total threshold together must stop after the budget is exhausted.A direct unit test for `recursive_scan()` showing that negative `recursive_limit_size` stops recursion immediately.
CVSS v3.1 score: 5.5