Nodejs vm/vm2沙箱逃逸

文章介绍了Nodejs中的VM模块和沙箱环境,包括vm模块的作用域、全局变量、常用API如vm.runInThisContext和vm.createContext。还讨论了如何通过vm沙箱逃逸,特别是利用this.constructor.constructor(returnglobal)()来获取global对象,以及vm2模块如何增强沙箱的安全性,防止逃逸。文章以HFCTF2020和HZNUCTF2023final的CTF题目为例,展示了实际的沙箱逃逸技巧。

什么是沙箱以及VM?

什么是沙箱:

沙箱就是能够像一个集装箱一样,把你的应用“装”起来的技术。这样,应用与应用之间,就因为有了边界而不至于相互干扰而被装进集装箱的应用,也可以被方便地搬来搬去。

什么是VM:

VM就是虚拟环境,虚拟机,VM的特点就是不受环境的影响,也可以说他就是一个 沙箱环境 (沙箱模式给模块提供一个环境运行而不影响其它模块和它们私有的沙箱)类似于docker,docker是属于 Sandbox(沙箱) 的一种。

简而言之,vm提供了一个干净的独立环境,提供测试。

在Nodejs中,我们可以通过引入vm模块来创建一个“沙箱”,但其实这个vm模块的隔离功能并不完善,还有很多缺陷,因此Node后续升级了vm,也就是现在的vm2沙箱,vm2引用了vm模块的功能,并在其基础上做了一些优化。

vm模块

参考:https://xz.aliyun.com/t/11859#toc-1

nodejs作用域

用例子来解释很清晰

#1.js
var height1 = 175
exports.height = height1

Node给我们提供了一个将js文件中元素输出的接口exports

#2.js
const age = 20
const user = require("./1")

console.log(age)
console.log(user.height)

输出

20
175
image-20230412195829829

height的作用域是1.js,通过exports创建被require引入2.js

age的作用域是2.js

还有一个global作用域,就是全局变量。Nodejs下其他的所有属性和包都挂载在这个global对象下。在global下挂载了一些全局变量,我们在访问这些全局变量时不需要用global.xxx的方式来访问,直接用xxx就可以调用这个变量。举个例子,console就是挂载在global下的一个全局变量,我们在用console.log输出时并不需要写成global.console.log,其他常见全局变量还有process(一会逃逸要用到)。

我们可以自定义一个name的全局变量,

# 1.js
var height1 = 175
global.name = "ThnPkm"

exports.height = height1

全局变量则不需要exports创建

# 2.js
const age = 20
const user = require("./1")

console.log(age)
console.log(user.height)
console.log(name)

输出时也不需要user.name来引用,name直接是global变量

vm沙箱

前面提到了作用域这个概念,所以我们现在思考一下,如果想要实现沙箱的隔离作用,我们是不是可以创建一个新的作用域,让代码在这个新的作用域里面去运行,这样就和其他的作用域进行了隔离,这也就是vm模块运行的原理。

(在Node中一般把作用域叫上下文)

先来了解几个常用的vm模块的API:

  • vm.runinThisContext(code):在当前global下创建一个作用域(sandbox),并将接收到的参数当作代码运行。sandbox中可以访问到global中的属性,但无法访问其他包中的属性。

也就是说无法访问本地作用域,但可以访问当前的全局对象global

image-20230412205126896
# xxx.js
const vm = require('vm');
const local_var = "local";
const vm_var = vm.runInThisContext('local_var = "vm";');

console.log(vm_var); //vm
console.log(local_var); //local

无权访问本地作用域,所以 local_var 不变

  • vm.createContext([sandbox]): 在使用前需要先创建一个沙箱对象,再将沙箱对象传给该方法(如果没有则会生成一个空的沙箱对象),v8为这个沙箱对象在当前global外再创建一个作用域,此时这个沙箱对象就是这个作用域的全局对象,沙箱内部无法访问global中的属性。
  • vm.runInContext(code, contextifiedSandbox[, options]):参数为要执行的代码和创建完作用域的沙箱对象,代码会在传入的沙箱对象的上下文中执行,并且参数的值与沙箱内的参数值相同。
image-20230412205216692
const vm = require('vm'
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值