npm / npm/cli

[BUG] NPM allows insecure code execution by configuration file

Open
#4,101 29 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug Needs Triage Release 8.x
Dominant language
JavaScript
Stars
10.1k
Forks
4.7k
Avg merge
2d 2h
Merged PRs (30d)
19

Description

Is there an existing issue for this?
  • I have searched the existing issues
This issue exists in the latest npm version
  • I am using the latest npm
Current Behavior
Intro

NPM is a package manager used in our CI/CD environments, We are using it to download and install all our different dependencies inside our building phase for production.

In order to use it safely, we use some precautions, We try to minimize as much as possible its exposure to secrets but still sometimes we have no choice but to add them.

Reading the documentation here and best practices using the cli around the internet it is best to run in ci environments with npm ci --ignore-scripts which should not run any scripts, additionally, we saw that npm sees bypassing this command as a high severity

Researching this subject we found out that a developer or malicious actor with access to the codebase can in fact force npm to run scripts although we configured it explicitly to ignore scripts.
Talking with the bounty team the response was that this is not eligible for bounty and is not considered a risk as this is the intended behavior of npm.

We agree that this is a design issue, but nevertheless, this can be used as an attack vector by actors, we want to raise the issue here and understand what mitigations can we have in the meantime, and if this design will be changed in the near future?

The Issue

When adding a .npmrc file to our repo, npm will pick up the file and read its configuration. This means that any command using npm will automatically pick up the configuration file.

It is possible to inject a configuration that will then execute code in the installation context and attempt to attack the installation environment by two vectors:

  1. Adding git=${PWD}/rce.sh to .npmrc and adding an installation from git to the package.json, such as "dependencies": { "ini":"git://github.com./npm/ini.git#v2.0.0" } will execute the rce.sh script when running npm install version 7,8
  2. Adding onload-script=${PWD}/rce to .npmrc will invoke the rce.js script in the library when running ANY npm version 6 command
Expected Behavior

As we are not expecting to run any scripts when running with "--ignore-scripts" this is can and will affect our devops installations if a malicious actor gains access to parts of our codebase.

I understand that this was not the intended behavior of the flag,
but it became the only security measure in use and implies that there will be no unintended scripts running

Steps To Reproduce

Using npm version 7,8

  1. npm init
  2. npm i --save "git://github.com./npm/ini.git#v2.0.0"
  3. echo "git=${PWD}/test.sh" > .npmrc
  4. echo 'echo "HACKED!!!" > poc.txt && git $@' > test.sh
  5. rm -rf node_modules

Attack phase

  1. npm ci --ignore-scripts
  2. cat poc.txt

Using npm version 6

  1. npm init
  2. echo "onload-script=${PWD}/rce" > .npmrc
  3. echo "console.log('Hacked") > rce.js

Attack phase

  1. npm ci --ignore-scripts
Environment
  • npm: 8.1.4
  • Node: v14.15.5
  • OS: OSX
  • platform: Macbook Pro
  • npm config:
; "user" config from /Users/rotembar/.npmrc
registry = "https://registry.npmjs.org/"

; "project" config from fuzz/npm-lion/.npmrc
git = "npm-lion/test.sh" 

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the .npmrc configuration handling used by npm ci --ignore-scripts and reproduce the two reported paths: git=${PWD}/rce.sh with a git dependency in package.json, and onload-script=${PWD}/rce. Compare behavior across npm 6, 7, and 8 using the listed reproduction steps; done means the reported scripts no longer execute unexpectedly, or the supported mitigation is documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
devops, security, tooling
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.