沙箱逃逸漏洞复现

目录

什么是沙箱以及VM?

沙箱:

VM:

原理

例题分析

问题1:

问题2:

问题3:


什么是沙箱以及VM?

沙箱:

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

VM:

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

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

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

原理

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

vm沙箱逃逸
我们一般进行沙箱逃逸最后都是进行rce,那么在Node里要进行rce就需要procces了,在获取到process对象后我们就可以用require来导入child_process,再利用child_process执行命令。但process挂载在global上,但是我们上面说了在creatContext后是不能访问到global的,所以我们最终的目标是通过各种办法将global上的process引入到沙箱中。

例题分析

const vm = require('vm');
const script = `m + n`;
const sandbox = { m: 1, n: 2 };
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res)


创建了一个名为 sandbox 的对象,它具有两个属性:m 和 n,分别被初始化为 1 和 2。这个对象代表了一个隔离的环境,我们的代码将在其中执行。

然后,我们使用 vm.createContext() 创建了一个新的上下文对象 context,并将 sandbox 作为参数传递给它。这样,context 就成为了一个与 sandbox 具有相同属性和方法的对象。

最后,我们使用 vm.runInContext() 方法来在指定的上下文中执行代码。我们将 script 和 context 作为参数传递给这个方法,它会返回执行结果。

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)


通过this去获取一个toString方法,再通过toString方法来获取构造函数

怎么获取Function,通过toString函数和constructor 属性来获取Function这个属性

this.toString.constructor  因为constructor本身就是指向它的构造函数

先拿到个process模块,通过this(这个this指向的是sandbox全局).toString.constructor拿到所有函数的构造函数Function,接着拿到process模块,最终拿到子模块child_process调用命令执行

问题1:

为什么我们不直接使用{}.toString.constructor('return process')(),却要使用this呢?
空对象调用toString.constructor也能拿到Function,但是空对象是沙盒内部的,沙盒逃逸就是要把外部的对象元素引入进来才行,不把外部元素引入进来的话,逃逸不出来。

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

问题2:

m和n也是沙盒外的对象,为什么也不能用m.toString.constructor('return process')()呢?
m和n也是在外部定义的,也有toString方法,不能的原因是原始类型是值引用而不是类型引用,

如果是值引用,外部的m和n和沙盒内部的mn是两个数据,如果是类型引用,那就是外部的m把内存地址传给内部,内部直接调用外部的内存地址,那这俩就是一样的

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

问题3:

想一想能不能把mn的值引用变成类型引用呢?
可以把mn改成空对象或者空数组甚至正则表达式,这样在外部就是类型引用了,这样内部就直接调用的是内存地址 那内外的mn就是一样的了

context:{m: [], n: {}, x: /regexp/}

这边就能看到执行了任意命令了,这里的whoami执行就是我的主机名。逃逸出沙箱就一种方法,就是拿到沙箱外部的变量或对象,然后用.toString方法和.constructor 属性来获取Function这个属性,然后拿到process,之后就可以执行任意代码了

你可能感兴趣的:(网络渗透防御,node.js)