Skip to content

GHSA-5r98-f33j-g8h7 CVE-2023-37478

pnpm incorrectly parses tar archives relative to specification

Critical npm
Severity
7.5 / 10
Critical · CVSS 3.1
Exploit chance (EPSS)
1.20%
Chance of exploitation in the next 30 days. Higher than 68.19% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-284 Improper Access Control
Fix
Available
9 packages have a fixed version

Description

Summary

It is possible to construct a tarball that, when installed via npm or parsed by the registry is safe, but when installed via pnpm is malicious, due to how pnpm parses tar archives.

Details

The TAR format is an append-only archive format, and as such, the specification for how to update a file is to add a new record to the end with the updated version of the file. This means that it is completely valid for an archive to contain multiple copies of, say, package.json, and the expected behavior when extracting is that all versions other than the last get ignored.

This is further complicated by that during tarball extraction, all package managers are configured to drop the first path component, so collisions can be created simply by using multiple root folders in the archive, even without performing updates.

When pnpm extracts a tar archive via tar-stream, it appears to extract only the first file of a given name and discards all subsequent files with the same name.

PoC

Create a root folder with the following layout:

  • a/package.json
  • package/package.json
  • z/package.json

File contents:

a/package.json

{
    "name": "test-package",
    "version": "0.1.0",
    "description": "This is a bad version of a test package",
    "dependencies": {
        "react": "^15"
    }
}

package/package.json

{
    "name": "test-package",
    "version": "0.1.0",
    "description": "This is a bad version of a test package",
    "dependencies": {
        "react": "^16"
    }
}

z/package.json

{
    "name": "test-package",
    "version": "0.1.0",
    "description": "This is the good version of a test package",
    "dependencies": {
        "react": "^17"
    }
}

Then use the tar binary to produce a tarball (working directory is the root folder): tar -c -z --format ustar -f package.tgz a package z The order of the folders at the end matters; whichever one is last will end up being the package.json that wins when extracted by npm; the one that is first will be the one that wins when extracted by pnpm.

Install the tarball via the file: protocol.

Observe that with npm, the lockfile has react@17, while with pnpm it has react@15.

Impact

This can result in a package that appears safe on the npm registry or when installed via npm being replaced with a compromised or malicious version when installed via pnpm.

Affected versions

npm

PackageVulnerableFixed in
@pnpm/cafs
< 7.0.5
7.0.5
@pnpm/exe
< 7.33.4
7.33.4
@pnpm/exe
>= 8.0.0, < 8.6.8
8.6.8
@pnpm/linux-arm64
< 7.33.4
7.33.4
@pnpm/linux-arm64
>= 8.0.0, < 8.6.8
8.6.8
@pnpm/linux-x64
< 7.33.4
7.33.4
@pnpm/linux-x64
>= 8.0.0, < 8.6.8
8.6.8
@pnpm/linuxstatic-arm64
< 7.33.4
7.33.4
@pnpm/linuxstatic-arm64
>= 8.0.0, < 8.6.8
8.6.8
@pnpm/macos-arm64
< 7.33.4
7.33.4
@pnpm/macos-arm64
>= 8.0.0, < 8.6.8
8.6.8
@pnpm/macos-x64
< 7.33.4
7.33.4
@pnpm/macos-x64
>= 8.0.0, < 8.6.8
8.6.8
@pnpm/win-x64
< 7.33.4
7.33.4
@pnpm/win-x64
>= 8.0.0, < 8.6.8
8.6.8
pnpm
< 7.33.4
7.33.4
pnpm
>= 8.0.0, < 8.6.8
8.6.8

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

@pnpm/cafs npm → 7.0.5

npm
npm install @pnpm/cafs@7.0.5
yarn
yarn add @pnpm/cafs@7.0.5
pnpm
pnpm add @pnpm/cafs@7.0.5

@pnpm/exe npm → 7.33.4

npm
npm install @pnpm/exe@7.33.4
yarn
yarn add @pnpm/exe@7.33.4
pnpm
pnpm add @pnpm/exe@7.33.4

@pnpm/linux-arm64 npm → 7.33.4

npm
npm install @pnpm/linux-arm64@7.33.4
yarn
yarn add @pnpm/linux-arm64@7.33.4
pnpm
pnpm add @pnpm/linux-arm64@7.33.4

@pnpm/linux-x64 npm → 7.33.4

npm
npm install @pnpm/linux-x64@7.33.4
yarn
yarn add @pnpm/linux-x64@7.33.4
pnpm
pnpm add @pnpm/linux-x64@7.33.4

@pnpm/linuxstatic-arm64 npm → 7.33.4

npm
npm install @pnpm/linuxstatic-arm64@7.33.4
yarn
yarn add @pnpm/linuxstatic-arm64@7.33.4
pnpm
pnpm add @pnpm/linuxstatic-arm64@7.33.4

@pnpm/macos-arm64 npm → 7.33.4

npm
npm install @pnpm/macos-arm64@7.33.4
yarn
yarn add @pnpm/macos-arm64@7.33.4
pnpm
pnpm add @pnpm/macos-arm64@7.33.4

@pnpm/macos-x64 npm → 7.33.4

npm
npm install @pnpm/macos-x64@7.33.4
yarn
yarn add @pnpm/macos-x64@7.33.4
pnpm
pnpm add @pnpm/macos-x64@7.33.4

@pnpm/win-x64 npm → 7.33.4

npm
npm install @pnpm/win-x64@7.33.4
yarn
yarn add @pnpm/win-x64@7.33.4
pnpm
pnpm add @pnpm/win-x64@7.33.4

pnpm npm → 7.33.4

npm
npm install pnpm@7.33.4
yarn
yarn add pnpm@7.33.4
pnpm
pnpm add pnpm@7.33.4

Severity breakdown

CVSS 3.1 7.5

CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H

Attack vector
Network
Attack complexity
High
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

Weakness

CWE-284: Improper Access Control

The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.

How to prevent it
  • Architecture and Design, Operation: Very carefully manage the setting, management, and handling of privileges. Explicitly manage trust zones in the software.
  • Architecture and Design: Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area. Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-vx52-2968-3vc6
pnpm: Environment secrets exfiltrated via env-placeholder expansion in proxy settings read from an untrusted pnpm-workspace.yaml
High Also affects pnpm
GHSA-2rx9-3g3h-c2jv
pnpm: pacquet trust-lockfile install can create dependency symlinks outside the project
High Also affects pnpm
GHSA-vq4v-j7r6-jq4m
pnpm: A tarball dependency's manifest `name` escapes node_modules → arbitrary file write/overwrite on install
High Also affects pnpm
GHSA-c59q-g84q-2gj5
pnpm: Virtual store linker path traversal via unvalidated depPath name in lockfileToDepGraph
High Also affects pnpm
GHSA-qrv3-253h-g69c
pnpm: Path traversal in configDependencies env lockfile allows symlink creation outside node_modules/.pnpm-config
High Also affects pnpm
GHSA-72r4-9c5j-mj57
pnpm: `patch-remove` could delete project-selected files outside the patches directory
High Also affects pnpm
GHSA-fr4h-3cph-29xv
pnpm: Hoisted install imports lockfile alias outside node_modules
High Also affects pnpm
GHSA-v23m-ccfg-pq9h
pnpm: `stage download` writes outside its destination directory via manifest name/version traversal
High Also affects pnpm
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor