Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
POC-CVE-2026-102282 — CVE-2026-102282: adm-zip LPE via SUID/SGID preservation during archive extraction (fixed in 0.6.1) | Kitploit
Tools/GitHubGitHub/ahmed-elmahgob/poc-cve-2026-102282
Defensive ToolsPrivilege EscalationStatic AnalysisVulnerability ScannersVulnerability AnalysisExploitationSupply Chain SecurityLearning & EducationIncident Response

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
GitHubahmed-elmahgob/poc-cve-2026-102282

POC-CVE-2026-102282

CVE-2026-102282: adm-zip LPE via SUID/SGID preservation during archive extraction (fixed in 0.6.1)

View Repository
21h 7m agoNot yet reviewed
Share

CVE-2026-102282 — adm-zip setuid/setgid preservation during extraction

CVE GHSA CVSS 3.1

Incorrect permission assignment in the npm package adm-zip. Extraction can keep setuid, setgid, and sticky bits from an untrusted zip when keepOriginalPermission is enabled, allowing a root-owned setuid executable to be planted on disk from an attacker-supplied archive: local privilege escalation.

Packageadm-zip (npm)
EcosystemNode.js
CVECVE-2026-102282
AdvisoryGHSA-j5f4-cc29-5x44
CWECWE-732 Incorrect Permission Assignment for Critical Resource
CVSS v3.17.1 High — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
Affected< 0.6.1 (last known affected: <= 0.6.0)
Fixed0.6.1 (11 September 2026)
PublishedMaintainer advisory 11 September 2026; GitHub Advisory Database 29 September 2026

This repository contains the write-up, a dependency version checker, a safe behavioral test, and a self-contained proof of concept built around a benign verifier binary that only prints its credential context.

Affected versions

Every release before 0.6.1 is affected, including 0.6.0.

RangeStatus
>= 0 and < 0.6.1Affected
<= 0.6.0Last known affected
>= 0.6.1Fixed

The bug is reachable only when all of these are true:

  • The caller passes keepOriginalPermission=true to extractAllTo(), extractAllToAsync(), or extractEntryTo().
  • The process extracting the archive runs as root, or as another user whose identity is more privileged than the account that later runs the extracted file.
  • The archive is untrusted (upload, fetched artifact, build input, dependency archive).

Default extraction leaves the flag off and is not affected. Extraction as an unprivileged user can still apply the mode bits, but the resulting file is owned by that same user, so it does not cross a privilege boundary.

The bug

Unix mode bits live in the high 16 bits of a zip entry's external file attributes. adm-zip copies those bits onto the extracted path.

LocationRole
headers/entryHeader.js — fileAttrReads the Unix mode from the external attributes
adm-zip.js — extractAllTo, extractAllToAsync, extractEntryToUses fileAttr when keepOriginalPermission is true
util/utils.js — chmodSync inside the file write helperApplies that mode to the path just created
adm-zip.js directory extractionApplies the same mode to extracted directories

In 0.6.0 and earlier, fileAttr masks the shifted attribute with 0xfff (0o7777). That mask keeps the three special bits along with the usual rwx bits, and nothing in the write path clears 0o7000 before chmod:

// headers/entryHeader.js (<= 0.6.0)
get fileAttr() {
    return (_attr || 0) >> 16 & 0xfff;
}
  • setuid (0o4000)
  • setgid (0o2000)
  • sticky (0o1000)

The trigger

Extraction passes the archive-controlled mode straight to the filesystem:

// adm-zip.js
const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
filetools.writeFileTo(entryName, content, overwrite, fileAttr);

// util/utils.js
self.fs.chmodSync(path, attr || 0o666);

Note the 0.6.x call signature: extractAllTo(targetPath, overwrite, keepOriginalPermission) takes a literal boolean as its third argument. Passing an options object such as { keepOriginalPermission: true } is silently coerced to false, which masks the vulnerability in a test instead of demonstrating it. Both PoC scripts in this repo pass true directly.

The fix

The fix is commit 6a63c339 (v0.6.1). fileAttr now keeps only the nine rwx bits:

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

How it works

  1. A zip entry stores a Unix mode in its external attributes (the standard place, shifted 16 bits).
  2. With keepOriginalPermission=true, extraction reads that mode through fileAttr and passes it to chmod.
  3. Because the mask is 0o7777, setuid, setgid, and sticky survive.
  4. If that chmod runs as root, the new file is root-owned and can carry setuid. A later run by a less privileged user executes the file owner's code as root.
  5. The same path applies to directories. A setgid directory keeps group inheritance for files created inside it afterward.

With the flag left at its default, extraction does not apply the archive mode, and the special bits are not written.

Proof of concept

Everything lives under poc/. The bundled payload is verify.c, a benign program that prints its real and effective uid/gid and, in its default mode, opens a shell with the effective identity. It does not persist, hide, or do anything beyond demonstrating the credential context.

FilePurpose
poc/verify.cBenign setuid-verifier payload
poc/build-archive.jsPacks a file into a zip whose entry carries mode 0o4755
poc/extract.jsExtracts an archive with keepOriginalPermission=true, as an affected app would
poc/run-demo.shEnd-to-end run: build, extract as root, run as the invoking user

Run it

git clone <this repo> && cd CVE-2026-102282
npm install [email protected]        # the version under test
sudo apt install gcc             # if needed

./poc/run-demo.sh                # uses /var/tmp/cve-2026-102282-demo
./poc/run-demo.sh /path/to/out   # or a custom output directory

Expected output against 0.6.0:

== 3. extract as root with keepOriginalPermission ==
adm-zip version: 0.6.0
extracted /var/tmp/cve-2026-102282-demo/exploit.zip into /var/tmp/cve-2026-102282-demo/extracted
== 4. result on disk ==
4755 -rwsr-xr-x root:root
== 5. run as the unprivileged user (user) ==
uid=1000(user) ... 
[*] real  uid=1000 gid=1000
[*] eff.  uid=0 gid=0
[+] SUID active: effective uid 0 differs from real uid 1000

Expected output against 0.6.1:

== 4. result on disk ==
755 -rwxr-xr-x root:root
== 5. run as the unprivileged user (user) ==
uid=1000(user) ...
[*] real  uid=1000 gid=1000
[*] eff.  uid=1000 gid=1000
[-] effective and real ids match: no SUID context

Caveats for the demo:

  • The extraction directory must be on a filesystem not mounted nosuid; the script warns when it detects that mount option.
  • The script refuses to run as bare root (run it via sudo from your own account so step 5 has a non-root account to drop to).
  • Cleanup strips the setuid bit and removes the output directory on exit.

Behavioral test

Download Tool