kevva / kevva/decompress

[CVE-2026-39245] Path traversal via indexOf containment bypass → arbitrary file write (CVE-2020-12265 bypass)

Open Beginner friendly
#115 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
JavaScript
Stars
419
Forks
55
PR merge metrics
No merged PRs in 30d

Description

## Summary

`decompress` (<= 4.2.1) uses `String.indexOf()` to verify the resolved path is inside the output directory, without enforcing a path-separator boundary. A sibling directory that shares a string prefix with the output directory passes the check, enabling directory traversal and **arbitrary file write** outside the extraction target. This bypasses the fix for CVE-2020-12265.

- **CVE:** CVE-2026-39245
- **CWE:** CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
- **Affected versions:** decompress <= 4.2.1
- **Fixed:** no fix released yet
- **Attack vector:** remote (crafted archive), no authentication required

## Affected component

`index.js` (lines ~29 and ~106):
realDestinationDir.indexOf(realOutputPath) !== 0

For `realOutputPath = "/tmp/app"`, a path of `"/tmp/app_config"` returns index `0` and incorrectly passes the check even though it is outside `/tmp/app`.

## Impact

Combined with the unvalidated symlink creation in the same package, an attacker can write arbitrary files into directories adjacent to the extraction target (e.g. output `/tmp/app`, escape to `/tmp/app_config`).

## Proof of concept

Given an output directory `/tmp/decompress_poc_app/`, an archive that (1) creates a symlink to the sibling `/tmp/decompress_poc_app_config` and (2) writes a file traversing through that symlink results in `pwned.txt` being written to `/tmp/decompress_poc_app_config/pwned.txt` — outside the intended output directory — because the `indexOf` check passes on the shared prefix.

## Correct fix

Append a path separator before the containment check:
realParentPath.indexOf(realOutputPath + path.sep) !== 0

## Mitigation

- Do not extract untrusted archives with this package until fixed.

Reported by Daniel Pua (devploit).

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in index.js around lines 29 and 106, where resolved paths are checked against the output directory. Reproduce the supplied sibling-directory path traversal proof of concept, then verify that shared-prefix paths are rejected while paths inside the output directory remain accepted. Done means the containment bypass no longer permits writes outside the extraction target.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
security
Issue type
Bug
Difficulty
2/5
Estimated time
Half a day
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.