Skip to content

[BUG] NPM allows insecure code execution by configuration file #4101

@rotem-cider

Description

@rotem-cider

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" 

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bugthing that needs fixingNeeds Triageneeds review for next stepsRelease 8.xwork is associated with a specific npm 8 release

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions