{"id":"CVE-2026-77301","aliases":["GHSA-7q85-xj36-vmfc"],"url":"https://o3.security/vulnerability/CVE-2026-77301","summary":"adm-zip: Uncontrolled memory allocation via the declared uncompressed size (DoS)","details":"### Summary\nadm-zip allocates an entry's output buffer from the declared uncompressed size (central-directory `size` field) before validating it against the actual data. A tiny crafted ZIP that declares a huge uncompressed size forces a multi-gigabyte allocation from a few bytes.\n\n### Impact\nOn adm-zip 0.5.17 (latest), Node 24, a 105-byte ZIP with one stored entry declaring size = 1,774,399,200 makes `new AdmZip(buf).getEntries()[0].getData()` commit ~1.8 GB of resident memory in ~4.4 s before throwing `Error: ADM-ZIP: CRC32 checksum failed`, roughly 16 million times the input size. Because the buffer is committed before any validation, on a memory-constrained host (containers, serverless, small VMs) the allocation OOM-kills the process before the CRC check (uncatchable), and concurrent requests can exhaust memory even on larger hosts. Any service that reads entries from untrusted ZIPs is exposed to a remote denial of service.\n\n### Steps to reproduce\nAttachments are not supported in the advisory form, so the 105-byte PoC (sha256 `980d34356fbb248fe527b9d0ac3eabc5c99393a374014be6199523de16709386`) is inlined as base64 in this self-contained reproducer:\n\n```js\nconst AdmZip = require('adm-zip');\n// 105-byte crafted ZIP, base64-inlined\n// sha256 980d34356fbb248fe527b9d0ac3eabc5c99393a374014be6199523de16709386\nconst b64 = \"UEsDBBQAAAAAAAAAAAAAAAAABQAAAAUAAAABAAAAYWhlbGxvUEsBAhQAFAAAAAAAAAAAAAAAAAAFAAAA4C7DaQEAAAAAAAAAAAAAAAAAAAAAAGFQSwUGAAAAAAEAAQAvAAAAJAAAAAAA\";\nconst buf = Buffer.from(b64, \"base64\");          // 105 bytes\nconst zip = new AdmZip(buf);\nzip.getEntries()[0].getData();   // commits ~1.8 GB, then throws \"ADM-ZIP: CRC32 checksum failed\"\n```\n\nThe single entry declares uncompressed size = 1,774,399,200 with a compressed size of 5. `getData()` allocates the full declared size before the CRC check runs, so the memory is committed regardless of the (tiny) actual payload.\n\n### Root cause\n`zipEntry.js` does `Buffer.alloc(<declared uncompressed size>)` before checking the declared size against the compressed size / available bytes.\n\n### Suggested fix\nValidate the declared uncompressed size against the compressed size and a configurable maximum before allocating (yauzl, for example, requires the caller to bound this); reject or stream when the declared size is implausible relative to the input. Happy to send a patch.","published":"2026-09-18T16:38:47.064Z","modified":"2026-10-28T02:30:29.970046044Z","cvss":{"score":7.5,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"npm","name":"adm-zip","fixedVersion":"0.6.1"}],"fix":{"url":"https://github.com/cthackers/adm-zip/commit/491600683dacb6cb9fe0718a0eeb9cb5eb49afa6","label":"cthackers/adm-zip@4916006"},"references":[{"type":"WEB","url":"https://github.com/cthackers/adm-zip/releases/tag/v0.6.1"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/77xxx/CVE-2026-77301.json"},{"type":"ADVISORY","url":"https://github.com/cthackers/adm-zip/security/advisories/GHSA-7q85-xj36-vmfc"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-77301"},{"type":"FIX","url":"https://github.com/cthackers/adm-zip/commit/491600683dacb6cb9fe0718a0eeb9cb5eb49afa6"},{"type":"PACKAGE","url":"https://github.com/cthackers/adm-zip"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:30:29.970046044Z"}}