前言
无。
环境搭建
下载安装NodeJS,新建一个目录并运行命令npm init后下载依赖:
npm install vm2@3.6.10
npm提示说该版本的vm2存在高危安全漏洞,但是坚持安装。
vm2
NodeJS沙盒,vm沙盒的优化版本,简单用法如下:
const {VM, VMScript} = require('vm2');
const script = new VMScript("this;");
console.log((new VM()).run(script));
运行一下,看到此时的this作用域为vm环境下的特殊作用域。
可以在本目录的node_modules文件夹下找到vm2的代码,比如main.js中可以找到VMScript的构造函数:
constructor(code, filename) {
this.code = code;
this.filename = filename || 'vm.js';
}
只是简单地把待运行代码保存起来。
也可以找到VM的构造函数:
const host = {
version: parseInt(process.versions.node.split('.')[0]),
console,
String,
Number,
Buffer,
Boolean,
Array,
Date,
Error,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
RegExp,
Function,
Object,
VMError,
Proxy,
Reflect,
Map,
WeakMap,
Set,
WeakSet,
Promise
};
this._context = vm.createContext();
Reflect.defineProperty(this, '_internal', {
value: vm.runInContext(`(function(require, host) { ${cf} \n})`, this._context, {
filename: `${__dirname}/contextify.js`,
displayErrors: false
}).call(this._context, require, host)
});
根据参考文章,这里将一些沙盒中需要的对象放入host中,创建了沙盒作用域并调用vm的API将contextify.js封装成了一个匿名函数,contextify.js涉及到一些对象代理之类的东西。可以看到global下只挂载了VMError和Buffer两个对象:
global.VMError = VMError;
...
const LocalBuffer = global.Buffer = Contextify.readonly(host.Buffer, {
allocUnsafe: function allocUnsafe(size) {
return this.alloc(size);
},
allocUnsafeSlow: function allocUnsafeSlow(size) {
return this.alloc(size);
}
});
readonly一直找到Contextify.object,可以看到Buffer实际上是一个代理对象Proxy:
const proxy = new host.Proxy(object, host.Object.assign(base, traps, deepTraps));
Contextify.proxies.set(object, proxy);
Contextified.set(proxy, object);
return proxy;
该代理对象拦截了get、set、construct等等操作,所以这个Buffer对象虽然是从外面传入沙盒的,但是也无法访问其constructor等属性,从而保证了沙盒安全性。
CVE-2019-10761
基于调用栈过大爆栈捕获外部对象。
CVE-2021-23449
基于import未经沙箱,
trick
基于对象代理和异常捕获。
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!