Sep 29, 2026·8 min read·15 visits
A bypass of the decompression bomb protection in adm-zip (CVE-2026-39244) allows attackers to trigger out-of-memory crashes by setting the declared uncompressed size header of a malicious ZIP entry to 0, which disables zlib limits.
A critical denial-of-service vulnerability in the adm-zip npm package allows attackers to bypass decompression-bomb protections introduced in version 0.5.18. By declaring the uncompressed file size as exactly 0, an attacker can disable the maxOutputLength constraint in the Node.js zlib wrapper, leading to complete system memory exhaustion and process crashes.
The popular Node.js library adm-zip provides synchronous and asynchronous APIs to decompress ZIP archives directly in-memory. Applications integrate this library to process user-supplied archives, exposing a significant attack surface to remote untrusted inputs. When parsing an archive, adm-zip relies on metadata from the central directory and local headers to understand compression parameters and allocate buffers.
In earlier iterations of the library, specifically versions prior to 0.5.18, a critical vulnerability identified as CVE-2026-39244 existed. The parser eagerly pre-allocated memory using Buffer.alloc() based directly on the uncompressed size specified by the file headers. Because this value is attacker-controlled and unvalidated, processing a crafted archive containing an inflated size header (such as 4 gigabytes) caused immediate unrecoverable out-of-memory errors on the host application.
To remediate CVE-2026-39244, the developers introduced a validation mechanism in version 0.5.18 that leveraged Node.js's native zlib maxOutputLength constraint to abort decompression if the output exceeded the declared header size. However, a logic flaw in this patch led to the discovery of GHSA-RCW4-F5RP-G42V. By declaring an uncompressed file size of exactly zero, an attacker can completely disable this decompression limit, allowing high-ratio compression bombs to bypass all system resource constraints and trigger process-level denial-of-service.
The root cause of GHSA-RCW4-F5RP-G42V lies in the implementation of the validation logic within the inflater module. To construct the safety parameters for the underlying zlib library, adm-zip evaluated the declared size using a ternary check: version >= 15 && expectedLength > 0 ? { maxOutputLength: expectedLength } : {}. When processing an entry with a declared size of 0, the check expectedLength > 0 evaluated to false, leaving the options block empty and disabling the maxOutputLength constraint.
In the ZIP format, file entries can legitimately declare an uncompressed size of 0 to indicate empty files or directory placeholders. By exploiting this, an attacker can supply a fully-populated, high-ratio Deflate stream under a file record that claims to be empty. The library accepts the record as valid, bypasses the expectedLength safety check, and forwards the deflated data stream to Node's internal zlib engine without any output length restriction.
Additionally, the asynchronous processing pipeline in adm-zip failed to implement any level of stream-based safety limits. While the synchronous API is designed to respect options like maxOutputLength, Node's streaming APIs behave differently during sequential data emissions. The async path accumulated data chunks continuously in a list without cross-referencing the aggregate size against the expected limits, allowing arbitrary data inflation to occur unchecked.
This combination of logic bypasses highlights the difficulty of retrofitting memory limits on top of complex file parsers. By failing to enforce a minimal boundary or check actual stream data accumulation, the protection mechanism was entirely neutralized by a single byte change in the ZIP central directory header. The failure is classified under CWE-400 (Uncontrolled Resource Consumption) and CWE-697 (Incorrect Comparison).
The vulnerability transition becomes clear when examining the code changes in zipEntry.js and methods/inflater.js. In version 0.5.17, the library allocated buffers directly from the uncompressed size header, exposing the allocation path to instant crashes. The 0.5.18 patch removed this direct allocation for DEFLATED files and introduced the maxOutputLength option, but introduced the ternary expression that returned an empty object when the expected size was 0.
Below is the logic in methods/inflater.js before the fix for GHSA-RCW4-F5RP-G42V:
// Vulnerable logic in adm-zip 0.5.18 - 0.6.0
const option = version >= 15 && expectedLength > 0 ? { maxOutputLength: expectedLength } : {};If an attacker sets the uncompressed size field to 0, expectedLength is passed as 0. The parser returns {} for option, leaving the zlib engine with no limits.
In version 0.6.1, this logic was replaced with a secure floor and manual stream boundaries. The developer ensured that maxOutputLength is always set to at least 1, which prevents empty objects and forces zlib to abort if any data is decompressed from an entry that claims to be empty. The patch also introduced manual size accumulation checks to protect the asynchronous streaming paths.
Below is the patched logic implemented in methods/inflater.js for version 0.6.1:
// Patched logic in adm-zip 0.6.1
const maxOutputLength = expectedLength > 0 ? expectedLength : 1;
const option = version >= 15 ? { maxOutputLength } : {};Furthermore, the patch secured the stream data listener on the asynchronous path. By keeping an active count of decompressed chunks and calling a termination function if total > maxOutputLength, the parser enforces limits even when Node's native streaming APIs ignore maxOutputLength. This dual-layered validation ensures that both synchronous and asynchronous extraction paths are protected against inflated structures.
Exploiting GHSA-RCW4-F5RP-G42V requires minimal prerequisite access. The attack vector is entirely remote and unauthenticated, requiring only that the target system exposes an endpoint that processes user-supplied archives through adm-zip. The exploit utilizes a crafted ZIP file containing a single entry that specifies an uncompressed size of 0 while containing a highly compressed Deflate stream (a zip bomb).
To construct the payload, an attacker generates a large buffer of repeated characters (such as 1 gigabyte of null bytes) and compresses it using standard Deflate. This generates a small compressed stream, often less than 1 kilobyte. The attacker then parses the raw binary of the ZIP and modifies the uncompressed size field within both the Local File Header and the Central Directory File Header to 0x00000000, while preserving the compressed size field.
When the target application calls extraction functions like extractAllTo() or entry.getData(), adm-zip initializes the decompression routine. Because the uncompressed size is 0, the options block is initialized empty on vulnerable versions. The zlib engine decompressor consumes system memory until V8 exceeds its heap limit, terminating the Node.js process and causing a denial of service.
The security impact of GHSA-RCW4-F5RP-G42V is classified as High, presenting a severe risk of denial-of-service across impacted environments. Since Node.js is single-threaded, a blocking CPU-bound or memory-bound operation like uncontrolled decompression halts the event loop for all concurrent users before crashing the process. In multi-tenant systems or API gateways, a single malicious upload can disrupt service for all users on that instance.
Because V8 limits heap allocation (typically to 1.4 GB on older 64-bit platforms and up to 4 GB on modern runtimes), memory exhaustion occurs rapidly when decompressing billions of bytes. When memory limits are reached, Node.js is unable to allocate further buffers and terminates immediately with an uncatchable fatal error. The lack of standard error handling during such crashes prevents recovery, meaning system orchestrators (like Kubernetes or PM2) must detect and restart the container, leading to service degradation.
While this vulnerability does not directly expose application data or allow remote code execution, it remains a high-value target for adversaries seeking to cause disruption. Because the vulnerability requires no special privileges or authentication, any backend service accepting file inputs (such as profile images, document uploads, or database imports) is fully exposed to this denial-of-service vector.
Remediation requires upgrading the adm-zip dependency to version 0.6.1 or newer. This update ensures that the ternary logic enforces a minimum output cap of 1 byte for any entries declaring a size of zero, and implements explicit checks within the asynchronous stream reader to prevent runaway inflation.
While the fix in 0.6.1 is robust for modern Node.js environments, several structural edge cases must be evaluated by security teams. First, applications running on Node.js versions earlier than 15 will not benefit from the native maxOutputLength restriction on synchronous paths because the API option is not supported by legacy runtimes. Although the async path remains guarded by the manual length check, synchronous extractions on legacy runtimes will remain vulnerable.
Secondly, while individual file decompression is capped, adm-zip does not enforce aggregate limits across multi-file archives. An attacker can still supply an archive containing thousands of small, valid entries. Processing such an archive can still exhaust disk space or CPU cycles through directory traversal operations and file system overhead. Security teams must implement absolute processing limits, sandboxed execution environments, and maximum archive size limits at the network edge to ensure comprehensive defense-in-depth.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
adm-zip cthackers | >= 0.5.18 < 0.6.1 | 0.6.1 |
adm-zip cthackers | < 0.5.18 | 0.5.18 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 (Uncontrolled Resource Consumption) |
| Attack Vector | Network / Remote |
| CVSS Score | 7.5 (High) |
| Exploit Status | Proof of Concept (PoC) Available |
| KEV Status | Not Listed |
The software does not properly control the allocation and maintenance of a limited resource, enabling an attacker to influence the amount of resources consumed and leading to a denial of service.
The Natural Language Toolkit (NLTK) versions 3.6 through 3.10.3 contain an algorithmic complexity vulnerability in the LegalitySyllableTokenizer.tokenize method. When processing tokens composed of continuous runs of vowels, an unguarded string reversal and set membership lookup loop executes in quadratic time relative to the input token length. Remote attackers capable of submitting arbitrary text to applications utilizing this tokenizer can trigger sustained single-core CPU exhaustion and application-level denial of service.
CVE-2026-126304 is a symlink path traversal vulnerability (CWE-59) in the Natural Language Toolkit (NLTK) versions 3.10.0 prior to 3.10.4. The YCOECorpusReader class constructs internal sub-readers for the 'psd' and 'pos' subdirectories using FileSystemPathPointer.join(), which relies exclusively on lexical string-prefix validation rather than canonical path resolution. An attacker who supplies a crafted YCOE corpus containing symbolic links in place of the 'psd' or 'pos' subdirectories can bypass directory confinement and read arbitrary files from sibling corpora or sensitive directories within the NLTK data sandbox.
CVE-2026-128077 (GHSA-r99w-jh2x-3mg9) is an authorization bypass and interpretation conflict vulnerability affecting Nuxt and its underlying @nuxt/nitro-server package in versions 4.6.0 through 4.6.3. When an incoming HTTP request path begins with a double forward slash (e.g., //x/admin), the WHATWG URL parser within the Nuxt server-side page renderer interprets the initial segment as a network host/authority and resolves the remaining path (/admin) against the application's page router. Because Nitro server middleware, route-scoped serverHandlers, and Nitro routeRules evaluate the unparsed request path (//x/admin), path-based access controls, redirects, and security header policies configured for /admin fail to match and are bypassed.
Prior to version 2.10.2, the sqlite3-ruby gem explicitly cast 64-bit Ruby string lengths (long) to 32-bit signed integers (int) when invoking the underlying SQLite C API. On 64-bit platforms, providing a string of 4 GiB (2^32 bytes) or larger causes integer truncation, instructing SQLite to process only a prefix of the string. This flaw affects SQL query preparation, bound parameter values, and return values from custom Ruby-defined SQL functions and aggregates, allowing trailing SQL clauses such as WHERE restrictions to be silently dropped under specific conditions.
CVE-2026-128068 (GHSA-cpgf-5mjf-73p7) is a Regular Expression Denial of Service (ReDoS) vulnerability in the rails-html-sanitizer Ruby gem affecting versions 1.0.3 through 1.7.1. The flaw resides in the Rails::HTML::PermitScrubber#scrub_attribute method, which uses the multiline regular expression /^\s*[^#\s].*/m to enforce local-only URI fragment references on SVG elements. On Ruby runtimes prior to version 3.2 (CRuby <= 3.1 and JRuby), evaluating an attribute value composed of repeated newline and whitespace characters without a hash character triggers quadratic backtracking O(n^2). Applications are only vulnerable if their sanitization allowlist is explicitly configured to permit SVG reference elements such as <use> alongside href or xlink:href attributes.
CVE-2026-126360 (GHSA-mf5v-w3w3-f6cg) is an algorithmic complexity vulnerability (Regular Expression Denial of Service) in the trace context propagation logic of the opentelemetry-api package within the OpenTelemetry Python ecosystem. In versions prior to 1.45.2, the TraceContextTextMapPropagator.extract() method utilizes a regular expression susceptible to quadratic-time backtracking when parsing malformed W3C traceparent headers containing long sequences of whitespace followed by an embedded newline character. In Python applications—particularly those using single-threaded asynchronous event loops—an unauthenticated remote attacker transmitting payloads over newline-preserving carriers can monopolize CPU execution and induce application-level Denial of Service.